orb_url._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2007 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: orb_url.pike,v 1.4 2010/08/20 20:42:25 astra Exp $
18  */
19 inherit "/kernel/secure_mapping";
20 inherit "/kernel/orb";
21 #include <macros.h>
22 #include <attributes.h>
23 #include <exception.h>
24 #include <types.h>
25 #include <classes.h>
26 #include <events.h>
27 #include <database.h>
28 class orb_url : public secure_mapping,orb{
29 public:
30 
31 
32 
33 
34 
35 #ifdef URL_DEBUG
36 #define DEBUG_URL(s, args...) werror("[orb:url] "+s+"\n", args)
37 #else
38 #define DEBUG_URL(s, args...)
39 #endif
40 
41  mapping mPathCache = ([ ]);
42  mapping newPathCache = ([ ]);
43 
44 /**
45  * overwrites secure_mapping.list to provide additional information
46  */
47 mixed list() {
48  mixed result= ::list();
49  object filepath = _Server->get_module("filepath:tree");
50 
51  if (arrayp(result)) {
52  string newresult = "";
53  object obj;
54  foreach(result, string url) {
55  if (stringp(url)) {
56  obj = path_to_object(url);
57  if ( objectp(obj) ) {
58  url = httplib.replace_uml(url);
59  newresult += "<site><url><![CDATA["+url+"]]><\/url>";
60  newresult += "<id>"+obj->get_object_id()+"<\/id>";
61  if (objectp(filepath)) {
62  string path = filepath->object_to_filename(obj);
63  path = httplib.replace_uml(path);
64  newresult += "<path><![CDATA["+path+"]]><\/path>";
65  }
66  newresult += "<\/site>";
67  }
68  }
69  }
70  result = newresult;
71  }
72  return result;
73 }
74 
75 mixed list_raw()
76 {
77  mapping result = ([]);
78  foreach (::list();; mixed key)
79  {
80  result[key] = get_value(key);
81  }
82  return result;
83 }
84 
85 void clean_list()
86 {
87  foreach (::list();; mixed key)
88  {
89  if (!get_value(key) && (string)(int)key != key)
90  delete(key);
91  }
92 }
93 
94 /**
95  * Conversion function.
96  *
97  * @param object obj - the object to convert.
98  */
99 void convert_url(object obj)
100 {
101  return;
102  mixed old_value = obj->query_attribute(OBJ_URL);
103  if ( stringp(old_value) )
104  obj->set_attribute(OBJ_URL, old_value);
105 }
106 
107 /**
108  * Callback function when the module is installed. Registers
109  * the 'url' attribute in the object factory.
110  *
111  */
112 void create_module()
113 {
114  set_value("/", get_object_id());
115 }
116 
117 private:
118 private void low_set_url(object obj, string val)
119 {
120  int id;
121 
122  if ( stringp(val) )
123  id = get_value(val);
124  if ( id > 0 )
125  set_value(id, 0);
126 
127  string url = get_value(obj->get_object_id());
128  if ( stringp(url) )
129  {
130  set_value(url, 0);
131  if (!val)
132  {
133  werror("obj_url.pike: deleting %O -> %O\n", url, obj);
134  delete(url);
135  }
136  }
137 
138  if ( stringp(val) )
139  set_value(val, obj->get_object_id());
140  set_value(obj->get_object_id(), val);
141  if (stringp(url) && strlen(url)>0) {
142  m_delete(mPathCache, url);
143  if (url[-1] != '/')
144  m_delete(mPathCache, url+"/");
145  else
146  m_delete(mPathCache, dirname(url));
147  }
148  if (stringp(val) && strlen(val)>0) {
149  m_delete(mPathCache, val);
150  if (val[-1] != '/')
151  m_delete(mPathCache, val+"/");
152  else
153  m_delete(mPathCache, dirname(val));
154  }
155 }
156 
157 public:
158 
159 void update_url(object obj)
160 {
161  string url = obj->query_attribute(OBJ_URL);
162  low_set_url(obj, url);
163 }
164 
165 /**
166  * The 'url' attribute is acquired from this module and all url change
167  * calls on objects will end up here.
168  *
169  * @param string|int key - attribute to change, should be 'url'.
170  * @param mixed val - new value of 'url' attribute.
171  * @return true or new value.
172  * @see query_attribute
173  */
174 mixed set_attribute(string|int key, mixed val)
175 {
176  int id;
177 
178  if ( key == "url" )
179  FATAL("Old URL syntax detected - skipping !");
180  if ( stringp(val) ) {
181  int id = get_value(val);
182  if ( id>0 ) {
183  object urlObject = find_object(id);
184  if ( objectp(urlObject) &&
185  urlObject != obj &&
186  urlObject->status() >= 0 &&
187  urlObject->status() != PSTAT_DELETED)
188  {
189  steam_error("Trying to set URL to conflicting value(%s) for %O,"+
190  "taken by %O (%s)",
191  val,
192  obj->get_object(),
193  urlObject->get_object(),
194  _FILEPATH->object_to_filename(urlObject));
195  }
196  }
197  }
198  if ( val == "none" || val == "" )
199  val = 0;
200  low_set_url(obj, val);
201  return true;
202  }
203  else {
204  return ::set_attribute(key, val);
205  }
206 }
207 
208 /**
209  * Query an attribute, but should be 'url' in general for
210  * other objects whose acquiring end up here.
211  *
212  * @param string|int key - attribute to query.
213  * @return value for url in database or this modules attribute value.
214  * @see set_attribute
215  */
216 mixed query_attribute(string|int key)
217 {
218  if ( key == OBJ_URL ) {
219  return get_value(obj->get_object_id());
220  }
221  return ::query_attribute(key);
222 }
223 
224 /**
225  * Get the acquired attribute. No URL to get no loops.
226  *
227  * @param string|int key - the key of the attribute.
228  * @return the acquire object or function.
229  */
230 mixed get_acquire_attribute(string|int key)
231 {
232  if ( key == OBJ_URL )
233  return 0;
234  return ::get_acquire_attribute(key);
235 }
236 
237 void set_acquire_attribute(string|int key, mixed val)
238 {
239  if ( key == OBJ_URL )
240  return;
241  ::set_acquire_attribute(key, val);
242 }
243 
244 /**
245  * Resolve a path with the url module by getting the saved value for
246  * 'path' and returning the appropriate object.
247  *
248  * @param object uid - the uid for compatibility with orb:filepath
249  * @param string path - the path to resolve.
250  * @return the saved object value for 'path'.
251  */
252 object
253 resolve_path(object|string uid, string path)
254 {
255  if ( !objectp(uid) && !stringp(uid))
256  return path_to_object(path);
257  string prefix;
258  if (objectp(uid)) {
259  prefix = object_to_filename(uid);
260  }
261  else {
262  prefix = uid;
263  }
264  array tokens = path / "/";
265  object obj = uid;
266 
267  foreach(tokens, string t) {
268  obj = obj->get_object_byname(t);
269  if ( !objectp(obj) )
270  return 0;
271  }
272  return obj;
273 }
274 
275 object add_vhost(string host, string user_group)
276 {
277  object usergroup = GROUP(user_group) || USER(user_group);
278  if (host == "" || !objectp(usergroup))
279  return UNDEFINED;
280 
281  mapping hosts = GROUP("Admin")->query_attribute("virtual_hosts");
282  if (!mappingp(hosts)) hosts = ([]);
283  hosts[host] = usergroup;
284  GROUP("Admin")->set_attribute("virtual_hosts", hosts);
285  return usergroup;
286 }
287 
288 void remove_vhost(string host)
289 {
290  mapping hosts = GROUP("Admin")->query_attribute("virtual_hosts");
291  m_delete(hosts, host);
292  GROUP("Admin")->set_attribute("virtual_hosts", hosts);
293 }
294 
295 object resolve_vhost(string host)
296 {
297  object destination;
298  object usergroup = GROUP("Admin")->query_attribute("virtual_hosts")[host];
299  if (objectp(usergroup))
300  destination = usergroup->query_attribute("GROUP_PUBLICROOM") ||
301  usergroup->query_attribute("USER_PUBLICROOM") ||
302  usergroup->query_attribute("GROUP_WORKROOM") ||
303  usergroup->query_attribute("USER_WORKROOM");
304  return destination;
305 }
306 
307 object lookup_vhost(string host)
308 {
309  object usergroup = (GROUP("Admin")->query_attribute("virtual_hosts")||([]))[host];
310  if (objectp(usergroup))
311  return usergroup;
312 }
313 
314 mapping list_vhosts()
315 {
316  mapping hosts = GROUP("Admin")->query_attribute("virtual_hosts");
317  foreach (hosts; string host; mixed usergroup)
318  {
319  if (objectp(usergroup))
320  hosts[host] = usergroup->describe();
321  }
322  return hosts;
323 }
324 
325 #define NEWURLMODE
326 
327 object path_to_object(string path)
328 {
329 #ifdef NEWURLMODE
330  return url_to_object(path);
331 #else
332  return old_path_to_object(path);
333 #endif
334 }
335 
336 /**
337  * Returns an object for the given path if some object is registered
338  * with value 'path'.
339  *
340  * @param string path - the path to process.
341  * @return looks up an object in the database.
342  * @see resolve_path
343  */
344 object old_path_to_object(string path)
345 {
346  object obj;
347 
348  if ( !stringp(path) )
349  return 0;
350 
351  if (objectp(obj=mPathCache[path])) {
352  if (!stringp(obj->query_attribute(OBJ_URL)) ||
353  obj->query_attribute(OBJ_URL)==path)
354  return obj;
355 
356  m_delete(mPathCache, path);
357  }
358 
359  int l = strlen(path);
360  int oid;
361  string p;
362 
363 
364  if ( l > 1 ) {
365  if ( path[0] != '/' ) {
366  path = "/"+path;
367  l++;
368  }
369  }
370  else if ( l == 0 ) {
371  path = "/";
372  l = 1;
373  }
374  LOG("url:path_to_object("+path+")");
375 
376  p = path;
377  oid = get_value(p);
378  // if the path is the path to a directory, try to find the index files
379  if ( oid == 0 && l >= 1 && path[l-1] == '/' ) {
380  p = path + "index.xml";
381  oid = get_value(p);
382  if ( oid == 0 ) {
383  p = path + "index.html";
384  oid = get_value(p);
385  }
386  if ( oid == 0 ) {
387  p = path+"index.htm";
388  oid = get_value(p);
389  }
390  }
391 
392  // if we find no registered object we should try to get any registered
393  // prefix container of this and use normal filepath handling from there.
394  if ( oid == 0 ) {
395  array prefixes = path / "/";
396  if ( sizeof(prefixes) >= 2 ) {
397  for ( int i = sizeof(prefixes) - 1; i >= 0; i-- ) {
398  p = prefixes[..i]*"/";
399  oid = get_value(p);
400  if ( oid == 0)
401  oid = get_value(p+"/"); // try also containers with / at the end
402 
403  if ( oid != 0 ) {
404  obj = find_object(oid);
405  object module = _Server->get_module("filepath:tree");
406  if ( objectp(module) ) {
407  obj = module->resolve_path(obj, (prefixes[i+1..]*"/"));
408  mPathCache[path] = obj;
409  return obj;
410  }
411  }
412  }
413  }
414  }
415 
416  LOG("Found object: " + oid);
417  obj = find_object(oid);
418  mPathCache[path] = obj;
419 
420  if ( obj == 0 )
421  set_value(p, 0);
422  return find_object(oid);
423 }
424 
425 
426 object url_to_object(string path, void|string _host)
427 {
428  string host = _host;
429  if (!host)
430  host = _Server->get_server_name();
431 
432  //if (!host && objectp(CALLER) && CALLER->get_vhost)
433  // host = CALLER->get_vhost();
434 
435  object usergroup = lookup_vhost(host);
436  object hostdest;
437 
438  // fail if we can't find a requested host,
439  // but don't fail if it's the default host
440  if (!objectp(usergroup) && _host)
441  return 0;
442  else if (!objectp(usergroup))
443  return new_path_to_object(path);
444  else
445  hostdest = resolve_vhost(host);
446 
447  if (path[0] != '/')
448  path = "/"+path;
449  if (sizeof(path) > 1 && path[-1] == '/')
450  path = path[..sizeof(path)-2];
451 
452  DEBUG_URL("path: %O:%O", host, path);
453 
454  if (path == "/")
455  return hostdest;
456 
457  array path_tokens = path / "/";
458 
459  object filepath = _Server->get_module("filepath:tree");
460  object pathdest = filepath->path_to_object("/"+path_tokens[1]);
461  if (!objectp(pathdest))
462  pathdest = hostdest->get_object_byname(path_tokens[1]);
463 
464  if (!objectp(pathdest))
465  {
466  object group = GROUP(usergroup->get_identifier()+"."+path_tokens[1]);
467  if (!objectp(group))
468  group = GROUP(path_tokens[1]);
469  if (objectp(group) && (host == _Server->get_server_name() ||
470  usergroup->is_member(group)))
471  pathdest = group->query_attribute("GROUP_PUBLICROOM") ||
472  group->query_attribute("GROUP_WORKROOM");
473  else if (objectp(USER(path_tokens[1])) &&
474  (host == _Server->get_server_name() ||
475  usergroup->is_virtual_member(USER(path_tokens[1]))))
476  pathdest = USER(path_tokens[1])->query_attribute("USER_PUBLICROOM") ||
477  USER(path_tokens[1])->query_attribute("USER_WORKROOM");
478  }
479 
480  if (objectp(pathdest))
481  if (sizeof(path_tokens) > 2)
482  return resolve_path(pathdest, path_tokens[2..]*"/");
483  else
484  return pathdest;
485 
486  return UNDEFINED;
487 }
488 
489 object new_path_to_object(string path)
490 {
491  object obj;
492 
493  if ( !stringp(path) )
494  return 0;
495 
496  int oid;
497 
498  if (!sizeof(path))
499  path = "/";
500 
501  if (path[0] != '/')
502  path = "/"+path;
503  LOG("url:new_path_to_object("+path+")");
504 
505  if (objectp(obj=newPathCache[path]))
506  {
507  if (!stringp(obj->query_attribute(OBJ_URL)) ||
508  obj->query_attribute(OBJ_URL)==path)
509  return obj;
510 
511  m_delete(newPathCache, path);
512  }
513 
514  array path_tokens = path / "/";
515  object filepath = _Server->get_module("filepath:tree");
516  if (sizeof(path_tokens) > 1)
517  {
518  obj = filepath->path_to_object("/"+path_tokens[1]);
519  if (!obj)
520  if (GROUP(path_tokens[1]))
521  obj = GROUP(path_tokens[1])->query_attribute("GROUP_PUBLICROOM") ||
522  GROUP(path_tokens[1])->query_attribute("GROUP_WORKROOM");
523  else if (USER(path_tokens[1]))
524  obj = USER(path_tokens[1])->query_attribute("USER_PUBLICROOM") ||
525  USER(path_tokens[1])->query_attribute("USER_WORKROOM");
526  }
527 
528  if (obj && sizeof(path_tokens) > 2)
529  obj = filepath->resolve_path(obj, path_tokens[2..]*"/");
530 
531  LOG("Found object: " + obj);
532  newPathCache[path] = obj;
533 
534  if ( obj == 0 )
535  delete(path);
536  return obj;
537 }
538 
539 /**
540  * Gets the path for an object by looking up the objects id in the
541  * database and returns a path or 0.
542  *
543  * @param object obj - the object to get a path for.
544  * @return the path for object 'obj'.
545  */
546 string old_object_to_filename(object obj)
547 {
548  string path = get_value(obj->get_object_id());
549  if ( !stringp(path) ) {
550 
551  foreach ( indices(mVirtualPath), object module ) {
552  string vpath = module->contains_virtual(obj);
553  if ( stringp(vpath) )
554  return mVirtualPath[module] + vpath;
555  }
556 
557  path = "";
558  object env = obj->get_environment();
559  // check if environment is registered
560 
561 
562  if ( objectp(env) ) {
563  path = object_to_filename(env);
564  }
565  if ( !stringp(path) )
566  steam_error("Unable to resolve path for "+obj->describe());
567  if ( strlen(path) == 0 )
568  return "";
569 
570  if ( path[-1] != '/' )
571  path += "/";
572 
573  path += obj->get_identifier();
574  }
575  return path;
576 }
577 
578 string object_to_filename(object obj, void|string server_name)
579 {
580  DEBUG_URL("new_object_to_filename(%O, %O)", obj, server_name);
581  string path;
582 // path = get_value(obj->get_object_id());
583  if ( !stringp(path) )
584  {
585  foreach ( indices(mVirtualPath), object module )
586  {
587  string vpath = module->contains_virtual(obj);
588  if ( stringp(vpath) )
589  {
590  DEBUG_URL("new_object_to_filename(%O): %O-%O-%O", obj, module, vpath, mVirtualPath[module]);
591  return mVirtualPath[module] + vpath;
592  }
593  }
594  }
595 
596  object owner = obj->query_attribute("OBJ_OWNER") || obj->get_root_environment()->query_attribute("OBJ_OWNER");
597 // if (objectp(CALLER) && CALLER->get_vhost)
598 // DEBUG_URL("new_object_to_filename(%O) caller w vhost:%O", obj, CALLER->get_vhost());
599 // else
600 // DEBUG_URL("new_object_to_filename(%O) caller:%O", obj, CALLER);
601 // object socket = Caller.get_socket(this, backtrace());
602 // if (socket && socket->get_vhost)
603 // DEBUG_URL("new_object_to_filename(%O) socket w vhost: %O", obj, socket->get_vhost());
604 // else
605 // DEBUG_URL("new_object_to_filename(%O) socket: %O", obj, socket);
606 
607  if (owner && obj->get_object_id() == (owner->query_attribute("GROUP_PUBLICROOM") ||
608  owner->query_attribute("USER_PUBLICROOM") ||
609  owner->query_attribute("GROUP_WORKROOM") ||
610  owner->query_attribute("USER_WORKROOM"))->get_object_id())
611  {
612 
613  object socket = Caller.get_socket(this, backtrace());
614  if (!server_name && objectp(socket) && socket->get_vhost)
615  server_name = socket->get_vhost();
616  object vhost_owner = lookup_vhost(server_name);
617 
618  DEBUG_URL("new_object_to_filename(%O) %O - %O - socket: %O", obj, server_name, vhost_owner, socket);
619 
620  if (vhost_owner)
621  {
622  // if the current host is a virtual host for the owner, path is /
623  if (owner == vhost_owner)
624  path = "/";
625 
626  // if owner is a subgroup or user check if it is a member of the vhost group,
627  // path is the subgroup or user name.
628  else if (server_name == _Server->get_server_name() ||
629  vhost_owner->is_virtual_member(owner))
630  if (has_prefix(owner->get_identifier(), vhost_owner->get_identifier()+"."))
631  path = "/"+owner->get_identifier()[sizeof(vhost_owner->get_identifier())+1..];
632  else
633  path = "/"+owner->get_identifier();
634  }
635  else
636  // if this is a publicroom then path is groupname.
637  path = "/"+owner->get_identifier();
638  }
639  else
640  DEBUG_URL("new_object_to_filename(%O) env: %O owner: %O root: %O owner: %O socket: %O room: %O", obj, obj->get_environment(), obj->query_attribute("OBJ_OWNER"), obj->get_root_environment(), owner, Caller.get_socket(this, backtrace()),
641  (owner && (owner->query_attribute("GROUP_PUBLICROOM") ||
642  owner->query_attribute("USER_PUBLICROOM") ||
643  owner->query_attribute("GROUP_WORKROOM") ||
644  owner->query_attribute("USER_WORKROOM"))));
645 
646  if (!owner && obj->query_attribute("OBJ_NAME") == "root-room")
647  path = "/";
648 
649  if (!path)
650  {
651  object env = obj->get_environment();
652 
653  if ( objectp(env) )
654  {
655  string parentpath = object_to_filename(env, server_name);
656  if (parentpath[-1] != '/')
657  parentpath+="/";
658  path = parentpath+obj->get_identifier();
659  }
660  }
661 
662  if ( !path )
663  steam_error("Unable to resolve path for "+obj->describe());
664  DEBUG_URL("new_object_to_filename(%O) result: %O", obj, path);
665  return path;
666 }
667 
668 mapping object_to_url(object obj)
669 {
670  // find owner
671  // check if owner has a virtual host
672  // get list of groups owner belongs to
673  // get virtual hosts for each group
674  mapping vhosts = GROUP("Admin")->query_attribute("virtual_hosts");
675  mapping host_groups = ([]);
676  foreach (vhosts; string host; object group)
677  {
678  if (!host_groups[group])
679  host_groups[group] = ({ host });
680  else
681  host_groups[group] += ({ host });
682  }
683  object owner = obj->query_attribute("OBJ_OWNER") || obj->get_root_environment()->query_attribute("OBJ_OWNER");
684  array groups = ({ owner });
685  array subgroups = owner->get_groups();
686  while(sizeof(subgroups))
687  {
688  groups += subgroups;
689  subgroups = Array.flatten(subgroups->get_groups());
690  }
691  mapping matches = host_groups & groups;
692  array hosts = Array.flatten(values(matches));
693  if (!sizeof(hosts))
694  hosts += ({ _Server->get_server_name() });
695 
696  mapping urls = ([]);
697  foreach (hosts;; string host)
698  {
699  string path = object_to_filename(obj, host);
700  if (path && sizeof(path))
701  urls[host] = sprintf("http://%s%s", host, path);
702  }
703  return urls;
704 }
705 
706 string object_to_path(object obj)
707 {
708  string fullpath = object_to_filename(obj);
709  return dirname(fullpath);
710 }
711 
712 /**
713  * Get environment from a given path. This checks if the directory
714  * prefix of 'url' is also registered in the database.
715  *
716  * @param string url - path to get the environment object for.
717  * @return the environment object or 0.
718  */
719 object path_to_environment(string url)
720 {
721  sscanf(url, "/%s/%*s", url);
722  return path_to_object(url);
723 }
724 
725 string execute(mapping vars)
726 {
727  return "<html><body><h2>Welcome to sTeam</h2><br/>"+
728  "Congratulations, you successfully installed a sTeam server!"+
729  "<br/>To be able to get anything working on this "+
730  "Web Port, <br/>you need to install the web Package.</body></html>";
731 }
732 
733 string get_identifier() { return "filepath:url"; }
734 string get_table_name() { return "orb_url"; }
735 int get_object_class() { return ::get_object_class() | CLASS_SCRIPT; }
736 
737 
738 };