server._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 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: server.pike,v 1.10 2010/01/27 12:05:35 astra Exp $
18  */
19  "\" it has to inherit /kernel/module or at least "+
20 #include <config.h>
21 #include <macros.h>
22 #include <classes.h>
23 #include <database.h>
24 #include <attributes.h>
25 #include <assert.h>
26 #include <access.h>
27 #include <roles.h>
28 #include <events.h>
29 #include <functions.h>
30 #include <configure.h>
31 //! server is the most central part of sTeam, loads and handles factories
32 //! and modules. Global Events are also triggered through the server and
33 //! can be subcribed by modules.
34 class server {
35 public:
36 
37 
38 
39 /*!\mainpage sTeam Function Documentation
40  *
41  * \section Server Developers
42  * - Ludger Merkens
43  * - Thomas Bopp
44  * - Christian Schmidt
45  * - Martin Baehr
46  * - Robert Hinn
47  *
48  * Consult the Server documentation for more information.
49  */
50 
51 
52 private object nmaster;
53 private int iLastReboot;
54 private object oDatabase;
55 private object _Persistence; // the persistence manager
56 private object oBacktraces;
57 private mapping mGlobalBlockEvents;
58 private mapping mGlobalNotifyEvents;
59 private mapping mConfigs;
60 private mapping mConfigsFromFile;
61 private mapping mClasses;
62 private mapping mModules;
63 private mapping mErrors;
64 private string sTest;
65 private array aEmailAddresses;
66 private mapping mPortPrograms; // mapping of port programs
67 private object _stderr, _stdout;
68 
69 private mapping userConfigs = ([ "database": 1, "ip": 1, ]);
70 
71 private string sandbox_path = "/";
72 
73 
74 
75 #define CONFIG_FILE "steam.cfg"
76 
77 #define MODULE_SECURITY mModules["security"]
78 #define MODULE_FILEPATH mModules["filepath:tree"]
79 #define MODULE_GROUPS mModules["groups"]
80 #define MODULE_USERS mModules["users"]
81 #define MODULE_OBJECTS mModules["objects"]
82 
83 string get_identifier() { return "Server Object"; }
84 string describe() { return "Server Object"; }
85 string _sprintf() { return "Server Object"; }
86 
87 string get_config_dir () {
88  string dir = mConfigs["config-dir"];
89  if ( !stringp(dir) ) dir = CONFIG_DIR;
90  return dir;
91 }
92 
93 string get_sandbox_path () {
94  return sandbox_path;
95 }
96 
97 private:
98 private void update_config_file ()
99 {
100  string data;
101  mapping config = ([ ]);
102  array obsolete_files = ({ });
103  // from old pre 1.6 XML config file:
104  catch {
105  data = Stdio.read_file( get_config_dir() + "/config.txt" );
106  if ( stringp(data) ) {
107  config |= Config.get_config( data, "config" );
108  MESSAGE( "Found obsolete config file: " + get_config_dir() + "/config.txt" );
109  obsolete_files += ({ get_config_dir() + "/config.txt" });
110  }
111  };
112  // from 1.6 - 2.0 text config file:
113  catch {
114  data = Stdio.read_file( get_config_dir() + "/steam.cnf" );
115  if ( stringp(data) ) {
116  config |= Config.get_config( data );
117  MESSAGE( "Found obsolete config file: " + get_config_dir() + "/steam.cnf" );
118  obsolete_files += ({ get_config_dir() + "/steam.cnf" });
119  }
120  };
121  catch {
122  if ( Stdio.exist( CONFIG_DIR + "/config.tmp" ) ) {
123  MESSAGE( "Found obsolete config file template: "
124  + get_config_dir() + "/config.tmp" );
125  obsolete_files += ({ get_config_dir() + "/config.tmp" });
126  }
127  };
128  catch {
129  if ( Stdio.exist( get_config_dir() + "/config.template" ) ) {
130  MESSAGE( "Found obsolete config file template: "
131  + get_config_dir() + "/config.template" );
132  obsolete_files += ({ get_config_dir() + "/config.template" });
133  }
134  };
135 
136  // remove any hbs() wrappers:
137  foreach ( indices(config), string key ) {
138  if ( !stringp(config[key]) ) continue;
139  string v;
140  if ( sscanf( config[key], "hbs(%s)", v ) > 0 ) {
141  config[key] = Config.string_to_value(v);
142  }
143  }
144 
145  // write to new config file:
146  if ( sizeof(config) > 0 ) {
147  data = Stdio.read_file( get_config_dir() + "/" + CONFIG_FILE );
148  mixed err = catch {
149  Stdio.write_file( get_config_dir() + "/" + CONFIG_FILE,
150  Config.make_config_text_from_template( data, config ) );
151  };
152  if ( err != 0 ) {
153  werror( "Could not write config file (updated from old configs): "
154  + get_config_dir() + "/" + CONFIG_FILE + "\n" );
155  return;
156  }
157  // rename obsolete files:
158  foreach ( obsolete_files, string filename ) {
159  mixed err2 = catch {
160  if ( mv( filename, filename + ".old" ) )
161  MESSAGE( "Renamed obsolete file " + filename + " to " + filename + ".old" );
162  };
163  if ( err2 != 0 ) {
164  werror( "Could not rename obsolete file " + filename + " to " + filename + ".old\n" );
165  }
166  }
167  }
168 }
169 
170 public:
171 
172 
173 /**
174  * read configurations from server config file
175  */
176 private:
177 private void read_config_from_file ()
178 {
179  string data = Stdio.read_file( get_config_dir() + "/" + CONFIG_FILE );
180  if ( !stringp(data) )
181  error("Missing config file. Check for "+get_config_dir()+"/"+CONFIG_FILE+"\n"+
182  "You can either repeat installation or call ./setup manually !");
183 
184  m_delete(mConfigs, "database");
185 
186  mConfigsFromFile = Config.get_config( data );
187  foreach ( indices(mConfigsFromFile), string key ) {
188  if ( stringp(mConfigsFromFile[key]) ) {
189  string v;
190  if ( sscanf( mConfigsFromFile[key], "hbs(%s)", v ) > 0 )
191  //mConfigsFromFile[key] = Config.string_to_value(v);
192  mConfigsFromFile[key] = v;
193  }
194  mixed v = mConfigsFromFile[key];
195  if ( stringp(v) ) v = Config.string_to_value( v );
196  mConfigs[key] = mConfigsFromFile[key];
197  }
198 }
199 
200 public:
201 
202 
203 /**
204  * load configurations from config folder (attribute 'configs')
205  */
206 private:
207 private void read_config_from_config_folder ()
208 {
209  mapping confs = get_internal_config( this_object() );
210 
211  if ( ! mappingp(confs) ) {
212  object admin = GROUP("admin");
213  if ( objectp(admin) )
214  confs = admin->query_attribute("configs");
215  if ( mappingp(confs) )
216  FATAL("Using server config from admin group!");
217  else {
218  FATAL("Could not get server config from admin group! No config!");
219  confs = ([ ]);
220  }
221  }
222 
223  // configs from config file cannot be overwritten:
224  confs |= mConfigsFromFile;
225 
226  // some default configurations to keep compatibility:
227  if ( !confs->web_port_http )
228  confs->web_port_http = confs->http_port;
229  if ( !confs->web_port_ftp )
230  confs->web_port_ftp = confs->ftp_port;
231  if ( !confs->web_port )
232  confs->web_port = confs->http_port;
233 
234  string name, domain, fullname;
235  string hname = gethostname();
236  if ( sscanf(hname, "%s.%s", name, domain) != 2 )
237  name = hname;
238  if ( !stringp(name) || sizeof(name) < 1 )
239  name = "localhost";
240 
241  // check whether machine, domain or web_server must be autodetected:
242  bool autodetect_machine = false;
243  if ( !confs->machine || sizeof(confs->machine) < 1 ||
244  confs->machine == "<autodetect>" ||
245  confs->machine == "(autodetect)" )
246  autodetect_machine = true;
247  bool autodetect_domain = false;
248  if ( !confs->domain || (sizeof(confs->domain) < 1 && autodetect_machine) ||
249  confs->domain == "<autodetect>" ||
250  confs->domain == "(autodetect)" )
251  autodetect_domain = true;
252  bool autodetect_webserver = false;
253  if ( !confs->web_server || sizeof(confs->web_server) < 1 ||
254  confs->web_server == "<autodetect>" ||
255  confs->web_server == "(autodetect)" ||
256  confs->web_server == "<autodetect-ip>" ||
257  confs->web_server == "(autodetect-ip)" )
258  autodetect_webserver = true;
259 
260  // autodetect domain and machine if necessary:
261  if ( autodetect_domain ) {
262  confs->domain = domain;
263  mConfigs["domain"] = domain;
264  MESSAGE( "Autodetected domain: %O", domain );
265  }
266  if ( autodetect_machine ) {
267  confs->machine = name;
268  mConfigs["machine"] = name;
269  MESSAGE( "Autodetected machine: %O", name );
270  }
271 
272  // determine fully qualified hostname:
273  if ( stringp(confs->domain) && sizeof(confs->domain) > 0 )
274  fullname = confs->machine + "." + confs->domain;
275  else
276  fullname = confs->machine;
277 
278  // autodetect web_server if necessary:
279  if ( autodetect_webserver ) {
280  if ( confs->web_server == "<autodetect-ip>" ||
281  confs->web_server == "(autodetect-ip)" ) {
282  mixed web_server_ip;
283  if ( catch( web_server_ip =
284  Protocols.DNS.client()->gethostbyname(System.gethostname())[1][0] ) )
285  confs->web_server = fullname;
286  else if ( stringp(web_server_ip) && sizeof(web_server_ip)>0 )
287  confs->web_server = web_server_ip;
288  else
289  confs->web_server = fullname;
290  }
291  else
292  confs->web_server = fullname;
293  mConfigs["web_server"] = confs->web_server;
294  MESSAGE( "Autodetected web_server: %s", confs->web_server );
295  }
296 
297  if ( !confs->web_mount )
298  confs->web_mount = "/";
299 
300  mConfigs = confs | mConfigs;
301  write_config_to_admin();
302 }
303 
304 public:
305 
306 
307 private:
308 private void write_config_to_admin()
309 {
310  object groups = mModules["groups"];
311  if ( ! set_internal_config( this_object(), mConfigs ) ) {
312  if ( objectp(groups) ) {
313  object admin = groups->lookup("admin");
314  if ( objectp(admin) )
315  admin->set_attribute("configs", mConfigs);
316  }
317  }
318 }
319 
320 public:
321 
322 /**
323  * Save the modules (additional ones perhaps).
324  *
325  */
326 private:
327 private void save_modules()
328 {
329  object groups = mModules["groups"];
330  if ( objectp(groups) ) {
331  object admin = groups->lookup("admin");
332  if ( objectp(admin) ) {
333  admin->set_attribute("modules", mModules);
334  }
335  }
336 }
337 
338 public:
339 
340 int verify_crypt_md5(string password, string hash)
341 {
342 #if constant(Crypto.verify_crypt_md5)
343  return Crypto.verify_crypt_md5(password, hash);
344 #else
345  return Crypto.crypt_md5(password, hash) == hash;
346 #endif
347 }
348 
349 string sha_hash(string pw)
350 {
351 #if constant(Crypto.SHA1)
352  return Crypto.SHA1->hash(pw);
353 #else
354  return Crypto.sha()->update(pw)->digest();
355 #endif
356 }
357 
358 protected:
359  string prepare_sandbox()
360 {
361  string sandbox = mConfigs->sandbox;
362  if ( !stringp(sandbox) )
363  sandbox = getcwd()+"/tmp";
364  if ( sandbox[-1] == '/' )
365  sandbox = sandbox[0..strlen(sandbox)-2];
366  Stdio.mkdirhier(sandbox);
367  foreach ( get_dir(sandbox), string dir_entry ) {
368  if ( dir_entry == "content" ) continue;
369  Stdio.recursive_rm( sandbox + "/" + dir_entry );
370  }
371  string config_dir = get_config_dir();
372  if ( config_dir[-1] == '/' )
373  config_dir = config_dir[0..strlen(config_dir)-2];
374  MESSAGE("Preparing Sandbox in %s (could take a while)", sandbox);
375  Process.create_process( ({ "bin/jail", getcwd()+"/server", sandbox, config_dir, get_config("system_user")||"nobody" }),
376  ([ "env": getenv(),
377  "cwd": getcwd(),
378  "stdout": Stdio.stdout,
379  "stderr": Stdio.stderr,
380  ]))->wait();
381 
382  mixed pconfig = Config.read_config_file( config_dir+"/persistence.cfg",
383  "persistence" );
384  if ( !mappingp(pconfig) ) pconfig = ([ ]);
385  if ( arrayp(pconfig["layer"]) ) {
386  foreach ( pconfig["layer"], mixed layer ) {
387  if ( !mappingp(layer) || !mappingp(layer["mirror"]) ) continue;
388  mixed layer_name = layer["name"];
389  if ( !stringp(layer_name) || sizeof(layer_name) < 1 ) continue;
390  if ( mappingp(layer["mirror"]["content"]) &&
391  stringp(layer["mirror"]["content"]["path"]) &&
392  sizeof(layer["mirror"]["content"]["path"]) > 0 ) {
393  string mirror_path = layer["mirror"]["content"]["path"];
394  catch( mkdir( sandbox + "/mirror" ) );
395  catch( mkdir( sandbox + "/mirror/" + layer_name ) );
396  mixed err = catch( System.symlink( mirror_path, sandbox + "/mirror/"
397  + layer_name + "/content" ) );
398  if ( err )
399  FATAL("Failed to link content mirror for persistence layer %s :\n%O",
400  layer_name, err[0]);
401  }
402  break;
403  }
404  }
405 
406  return sandbox;
407 }
408 
409 public:
410 
411 protected:
412  int start_server()
413 {
414  string sandbox = 0;
415  float boottime = gauge {
416 
417  mGlobalBlockEvents = ([ ]);
418  mGlobalNotifyEvents = ([ ]);
419  mClasses = ([ ]);
420  mModules = ([ ]);
421 
422  int tt = f_get_time_millis();
423  iLastReboot = time();
424  MESSAGE("Server startup on " + (ctime(time())-"\n") + " (PID="+getpid()+")");
425 
426  update_config_file();
427  read_config_from_file();
428  sandbox = prepare_sandbox();
429  sandbox_path = sandbox;
430 
431  catch( rm( sandbox_path + "/server.restart" ) );
432 
433  nmaster = ((program)"kernel/master.pike")();
434  replace_master(nmaster);
435 
436  // default path
437  nmaster->mount("/usr", "/usr");
438  nmaster->mount("/sw", "/sw");
439  nmaster->mount("/opt", "/opt");
440  nmaster->mount("/var", "/var");
441 
442  nmaster->mount("/", sandbox);
443  nmaster->mount("/classes", sandbox+"/classes");
444  nmaster->mount("/net", sandbox+"/net");
445  nmaster->mount("/modules", sandbox+"/modules");
446  nmaster->mount("/libraries", sandbox+"/libraries");
447  nmaster->mount("/net/base", sandbox+"/net/base");
448 
449 
450  nmaster->add_module_path("/libraries");
451  nmaster->add_module_path(sandbox+"/libraries");
452  nmaster->add_include_path("/include");
453 
454  add_constant("_Server", this_object());
455  add_constant("query_config", query_config);
456  add_constant("vartype", nmaster->get_type);
457  add_constant("new", nmaster->new);
458  add_constant("this_user", nmaster->this_user);
459  add_constant("this_socket", nmaster->this_socket);
460  add_constant("geteuid", nmaster->geteuid);
461  add_constant("seteuid", nmaster->seteuid);
462  add_constant("get_type", nmaster->get_type);
463  add_constant("get_functions", nmaster->get_functions);
464  add_constant("get_dir", nmaster->get_dir);
465  add_constant("rm", nmaster->rm);
466  add_constant("file_stat", nmaster->file_stat);
467  add_constant("get_local_functions", nmaster->get_local_functions);
468  add_constant("_exit", shutdown);
469  add_constant("call", nmaster->f_call_out);
470  add_constant("call_out_info", nmaster->f_call_out_info);
471  add_constant("get_time_millis", f_get_time_millis);
472  add_constant("get_time_micros", f_get_time_micros);
473  add_constant("check_equal", f_check_equal);
474  add_constant("start_thread", nmaster->start_thread);
475  add_constant("call_mod", call_module);
476  add_constant("get_module", get_module);
477  add_constant("get_factory", get_factory);
478  add_constant("steam_error", steam_error);
479  add_constant("steam_user_error", steam_user_error);
480  add_constant("describe_backtrace", nmaster->describe_backtrace);
481  add_constant("set_this_user", nmaster->set_this_user);
482  add_constant("run_process", Process.create_process);
483 
484  // crypto changes in 7.6
485  add_constant("verify_crypt_md5", verify_crypt_md5);
486 #if constant(Crypto.make_crypt_md5)
487  add_constant("make_crypt_md5", Crypto.make_crypt_md5);
488 #else
489  add_constant("make_crypt_md5", Crypto.crypt_md5);
490 #endif
491  add_constant("sha_hash", sha_hash);
492 
493  MESSAGE("Loading Persistence...");
494 
495  _Persistence = ((program)"/Persistence.pike")();
496  add_constant("_Persistence", _Persistence);
497  _Persistence->init();
498 
499 #if __REAL_VERSION__ >= 7.4
500  oDatabase = ((program)"/database.pike")();
501 #else
502  oDatabase = new("/database.pike");
503 #endif
504  add_constant("_Database", oDatabase);
505  nmaster->register_constants(); // needed for database/persistence registration
506  oDatabase->init();
507  MESSAGE("Database is "+ master()->describe_object(oDatabase));
508 
509  add_constant("find_object", _Persistence->find_object);
510  add_constant("serialize", oDatabase->serialize);
511  add_constant("unserialize", oDatabase->unserialize);
512 
513  nmaster->register_server(this_object());
514  nmaster->register_constants();
515 
516  mixed err = catch {
517  oDatabase->enable_modules();
518  };
519  if ( err != 0 ) {
520  werror("%O\n%O\n", err[0], err[1]);
521  error("Boot failed: Unable to access database !\n"+
522  "1) Is the database running ?\n"+
523  "2) Check if database string is set correctly \n ("+
524  mConfigs->database+")\n"+
525  "3) The database might not exist - you need to create it.\n"+
526  "4) The user might not have access for this database.\n"+
527  "5) The Pike version you are using does not support MySQL\n"+
528  " Try pike --features to check this.\n");
529  }
530 
531 
532  MESSAGE("Database module support enabled.");
533  MESSAGE("Database ready in %d ms, now booting kernel ....",
534  f_get_time_millis() - tt);
535 
536  load_modules();
537  load_factories();
538  load_modules_db();
539 
540  if ( err = catch(load_objects()) ) {
541  FATAL(err[0]+"\n"+
542  "Unable to load basic objects of sTeam.\n"+
543  "This could mean something is wrong with the database:\n"+
544  "If this is a new installation, you have to drop the database and restart.\n");
545  FATAL("-----------------------------------------\n"+PRINT_BT(err));
546  exit(1);
547 
548  }
549  load_programs();
550  load_pmods("/libraries/");
551  install_modules();
552  _Persistence->post_init();
553 
554  MESSAGE("Initializing objects... " + (time()-iLastReboot) + " seconds");
555  iLastReboot = time();
556  MESSAGE("Setting defaults... " + (time()-iLastReboot) + " seconds");
557 
558  check_root();
559  check_config_folder();
560  read_config_from_config_folder();
561 
562  open_ports();
563  iLastReboot = time();
564  thread_create(abs);
565  // check if root-room is ok...
566  ASSERTINFO(objectp(_Persistence->lookup("rootroom")),
567  "Root-Room is null!!!");
568  };
569 
570  MESSAGE("Server started on " + (ctime(time())-"\n") + " (startup took "+boottime+" seconds)");
571  start_services();
572 
573  if ( check_updates_all() == 1 ) {
574  MESSAGE( "Updates require a restart, restarting server.\n");
575  oDatabase->wait_for_db_lock();
576  return 0;
577  }
578 
579  string user = get_config( "system_user" );
580  nmaster->run_sandbox( sandbox, user );
581  sandbox_path = "/";
582 
583  if ( stringp(sTest) )
584  return -17;
585 }
586 
587 public:
588 
589 /**
590  * Returns the config data stored for some object (server, modules)
591  * within the server (not the config file). This is usually for settings
592  * that are done via the web-interface.
593  * @param obj object for which to fetch the config (e.g. _Server)
594  * @return a mapping with configs, or UNDEFINED if no configs were stored
595  * for that object or an error occured.
596  */
597 mapping get_internal_config ( object obj ) {
598  object config_folder = get_module("filepath:tree")->path_to_object(
599  "/config" );
600  if ( ! objectp(config_folder) ) return UNDEFINED;
601  object config_obj = config_folder->get_object_byname( obj->get_identifier() );
602  if ( ! objectp(config_obj) ) return UNDEFINED;
603  return config_obj->query_attribute("config");
604 }
605 
606 /**
607  * Sets a config mapping for some object (server, modules) within the server
608  * (not the config file). This is usually for settings that are done via the
609  * web-interface.
610  * @param obj object for which to set the config (e.g. _Server)
611  * @param config a mapping with config data
612  * @return true on success, false if the mapping could not be set
613  */
614 bool set_internal_config ( object obj, mapping config ) {
615  object config_folder = get_module("filepath:tree")->path_to_object(
616  "/config" );
617  if ( ! objectp(config_folder) ) return UNDEFINED;
618  object config_obj = config_folder->get_object_byname( obj->get_identifier() );
619  if ( ! objectp(config_obj) ) {
620  object factory = get_factory( CLASS_OBJECT );
621  if ( ! objectp(factory) ) return false;
622  config_obj = factory->execute( ([ "name":obj->get_identifier() ]) );
623  config_obj->move( config_folder );
624  }
625  if ( ! objectp(config_obj) ) return UNDEFINED;
626 
627  config_obj->set_attribute( "config", config );
628  return true;
629 }
630 
631 /**
632  * Returns an array of all available update objects.
633  * @return an array of all updates
634  */
635 array get_updates ()
636 {
637  object updates_folder = get_module("filepath:tree")->path_to_object(
638  "/config/updates" );
639  if ( ! objectp(updates_folder) ) return UNDEFINED;
640  return updates_folder->get_inventory();
641 }
642 
643 /**
644  * Returns an update object (e.g. log file) by name.
645  * @param name identifier (filename) of update to look for
646  * @return the update object, or UNDEFINED if such an update could not be found
647  */
648 object get_update ( string name )
649 {
650  object updates_folder = get_module("filepath:tree")->path_to_object(
651  "/config/updates" );
652  if ( ! objectp(updates_folder) ) return UNDEFINED;
653  return updates_folder->get_object_byname( name );
654 }
655 
656 /**
657  * Adds an update to the server. Call this after an update has been
658  * performed to remember that the update has already been applied.
659  * @param update an object that represents the update (e.g. a log file
660  * of the update process). Note: the object identifier (filename)
661  * will be used to check whether an update has already been applied
662  * @return true when the update object has been successfully added
663  */
664 bool add_update ( object update )
665 {
666  object updates_folder = get_module("filepath:tree")->path_to_object(
667  "/config/updates" );
668  if ( ! objectp(updates_folder) ) return false;
669  return update->move( updates_folder );
670 }
671 
672 /**
673  * Checks updates in all modules
674  * If a function returns > 0, then server will restart
675  */
676 protected:
677  int check_updates_all ()
678 {
679  MESSAGE("Checking updates ....");
680  int ret = 0;
681  // check server for updates:
682  ret |= check_updates();
683  // check databbase for updates:
684  if ( functionp( oDatabase->check_updates ) )
685  ret |= oDatabase->check_updates();
686 
687  // check persistence for updates:
688  if ( functionp( _Persistence->check_updates ) )
689  ret |= _Persistence->check_updates();
690  // check modules for updates:
691  foreach ( values(get_modules()), object module ) {
692  if ( !objectp(module) )
693  continue;
694  if ( functionp( module->check_updates ) )
695  ret |= module->check_updates();
696  }
697  return ret;
698 }
699 
700 public:
701 
702 /**
703  * Check for updates and perform updates if necessary.
704  * Return 1 if any performed updates require a server restart.
705  * Use get_updates() or get_update(name) to check for updates that
706  * have already been performed. Use add_update(obj) to remember that
707  * an update has been performed (e.g. use a log file of the update).
708  * @return 1 if the updates need a server restart, 0 otherwise
709  */
710 int check_updates ()
711 {
712  return 0;
713 }
714 
715 protected:
716  void check_root() {
717  MESSAGE("Testing root user ...");
718  object root = USER("root");
719  int repair = 0;
720 
721  foreach ( root->get_groups(), object grp) {
722  if ( !objectp(grp) )
723  MESSAGE("root user: NULL group detected !");
724  /*
725  else {
726  MESSAGE("GROUP %s", grp->get_identifier());
727  }
728  */
729  }
730  if ( ! GROUP("admin")->is_member(root) ) {
731  MESSAGE("root user is missing in admin group !");
732  repair = 1;
733  }
734  //else MESSAGE("Root is member of ADMIN !");
735 
736  if ( search(root->get_groups(), GROUP("admin")) == -1 )
737  repair = 1;
738 
739  if ( ! GROUP("steam")->is_member(root) ) {
740  MESSAGE("root user is missing in sTeam group !");
741  repair = 1;
742  }
743  //else MESSAGE("Root is member of sTeam !");
744 
745  if ( !stringp(root->get_user_name()) ) {
746  MESSAGE("root user has NULL username!");
747  repair = 1;
748  }
749  if ( repair )
750  repair_root_user();
751 }
752 
753 public:
754 
755 protected:
756  void repair_root_user() {
757  GROUP("admin")->remove_member(USER("root"));
758  GROUP("admin")->add_member(USER("root"));
759  GROUP("steam")->remove_member(USER("root"));
760  GROUP("steam")->add_member(USER("root"));
761  FATAL("Status for user root is " + USER("root")->status());
762  catch {
763  USER("root")->set_user_name("root");
764  USER("root")->set_user_password("steam");
765  };
766 
767  USER("root")->set_attribute(USER_LANGUAGE, "english");
768  USER("root")->set_attribute(USER_FIRSTNAME, "Root");
769  USER("root")->set_attribute(USER_LASTNAME, "User");
770  USER("root")->set_attribute(OBJ_DESC, "The root user is the first administrator");
771  USER("root")->set_attribute("xsl:content", ([ GROUP("steam") : get_module("filepath:tree")->path_to_object("/stylesheets/user_details.xsl"), ]) );
772  USER("root")->set_attribute(OBJ_ICON, get_module("filepath:tree")->path_to_object("/images/user_unknown.jpg") );
773  object wr = USER("root")->query_attribute(USER_WORKROOM);
774  if (!objectp(wr)) {
775  wr = _Persistence->find_object(USER("root")->get_object_id() + 1);
776  if (objectp(wr) && (wr->get_object_class() & CLASS_ROOM) )
777  {
778  werror("\nrestoring USER_WORKROOM of root\n");
779  USER("root")->unlock_attribute(USER_WORKROOM);
780  USER("root")->set_attribute(USER_WORKROOM, wr);
781  USER("root")->lock_attribute(USER_WORKROOM);
782  }
783  }
784  object tb = USER("root")->query_attribute(USER_TRASHBIN);
785  if (!objectp(tb)) {
786  tb = _Persistence->find_object(USER("root")->get_object_id() + 3);
787  if (objectp(tb) && (tb->get_object_class() & CLASS_CONTAINER) ) {
788  werror("\nrestoring USER_TRASHBIN of root\n");
789  USER("root")->unlock_attribute(USER_TRASHBIN);
790  USER("root")->set_attribute(USER_TRASHBIN, tb);
791  USER("root")->lock_attribute(USER_TRASHBIN);
792  }
793  }
794 }
795 
796 public:
797 
798 protected:
799  bool check_config_folder ()
800 {
801  // folder: /config
802  object config_folder = get_module("filepath:tree")->path_to_object(
803  "/config" );
804  if ( ! objectp(config_folder) ) {
805  object container_factory = get_factory(CLASS_CONTAINER);
806  if ( ! objectp(container_factory) ) {
807  FATAL("Container factory not found, cannot create /config folder!");
808  return false;
809  }
810  config_folder = container_factory->execute( ([ "name":"config" ]) );
811  if ( ! objectp(config_folder) ) {
812  FATAL("Could not create /config folder!");
813  return false;
814  }
815  if ( ! config_folder->move( _ROOTROOM ) ) {
816  FATAL("Could not move config folder to root room!");
817  config_folder->delete();
818  return false;
819  }
820  config_folder->set_attribute( OBJ_TYPE, "container_config" );
821  MESSAGE( "Created /config container." );
822  }
823  int permissions_fixed = false;
824  if ( config_folder->query_sanction( _ADMIN ) != SANCTION_ALL ) {
825  config_folder->sanction_object( _ADMIN, SANCTION_ALL );
826  permissions_fixed = true;
827  }
828  if ( config_folder->query_meta_sanction( _ADMIN ) != SANCTION_ALL ) {
829  config_folder->sanction_object_meta( _ADMIN, SANCTION_ALL );
830  permissions_fixed = true;
831  }
832  if ( permissions_fixed )
833  MESSAGE( "Fixed access rights on /config container." );
834  if ( config_folder->query_attribute( OBJ_TYPE ) != "container_config" ) {
835  config_folder->set_attribute( OBJ_TYPE, "container_config" );
836  MESSAGE( "Fixed OBJ_TYPE of /config folder." );
837  }
838 
839  // folder: /config/updates
840  object updates_folder = get_module("filepath:tree")->path_to_object(
841  "/config/updates" );
842  if ( ! objectp(updates_folder) ) {
843  object container_factory = get_factory(CLASS_CONTAINER);
844  if ( ! objectp(container_factory) ) {
845  FATAL("Container factory not found, cannot create /config/updates folder!");
846  return false;
847  }
848  updates_folder = container_factory->execute( ([ "name":"updates" ]) );
849  if ( ! objectp(updates_folder) ) {
850  FATAL("Could not create /config/updates folder!");
851  return false;
852  }
853  updates_folder->set_attribute( OBJ_DESC, "Internal server configs and updates" );
854  updates_folder->set_attribute( OBJ_TYPE, "container_config_updates" );
855  if ( ! updates_folder->move( config_folder ) ) {
856  FATAL("Could not move updates folder to /config!");
857  updates_folder->delete();
858  return false;
859  }
860  MESSAGE( "Created /config/updates folder." );
861  }
862  if ( updates_folder->query_attribute( OBJ_TYPE ) != "container_config_updates" ) {
863  updates_folder->set_attribute( OBJ_TYPE, "container_config_updates" );
864  MESSAGE( "Fixed OBJ_TYPE of /config/updates folder." );
865  }
866 
867  // folder: /config/packages
868  object packages_folder = get_module("filepath:tree")->path_to_object(
869  "/config/packages" );
870  if ( ! objectp( packages_folder ) ) {
871  object container_factory = get_factory(CLASS_CONTAINER);
872  if ( ! objectp(container_factory) ) {
873  FATAL("Container factory not found, cannot create /config/packages folder!");
874  return false;
875  }
876  packages_folder = container_factory->execute( ([ "name":"packages" ]) );
877  if ( ! objectp(packages_folder) ) {
878  FATAL("Could not create /config/packages folder!");
879  return false;
880  }
881  packages_folder->set_attribute( OBJ_DESC, "Package configs" );
882  packages_folder->set_attribute( OBJ_TYPE, "container_config_packages" );
883  if ( ! packages_folder->move( config_folder ) ) {
884  FATAL("Could not move packages folder to /config!");
885  packages_folder->delete();
886  return false;
887  }
888  MESSAGE( "Created /config/packages folder." );
889  }
890  if ( packages_folder->query_attribute( OBJ_TYPE ) != "container_config_packages" ) {
891  packages_folder->set_attribute( OBJ_TYPE, "container_config_packages" );
892  MESSAGE( "Fixed OBJ_TYPE of /config/packages folder." );
893  }
894 
895  // main server config object:
896  object config_obj = config_folder->get_object_byname( get_identifier() );
897  if ( ! objectp(config_obj) ) {
898  object admin = GROUP("admin");
899  if ( objectp(admin) ) {
900  mapping confs = admin->query_attribute("configs");
901  if ( !mappingp(confs) ) confs = ([ ]);
902  if ( ! set_internal_config( this_object(), confs ) ) {
903  FATAL( "Could not create /config/%s config object!", get_identifier() );
904  return false;
905  }
906  werror( "Created /config/%s config object.\n", get_identifier() );
907  }
908  }
909  config_obj = config_folder->get_object_byname( get_identifier() );
910  if ( objectp(config_obj) && config_obj->query_attribute( OBJ_TYPE ) != "object_config_server" ) {
911  config_obj->set_attribute( OBJ_TYPE, "object_config_server" );
912  MESSAGE( "Fixed OBJ_TYPE of /config/%s config object.", get_identifier() );
913  }
914 
915  // decorate server config object with Config decoration:
916  string config_decoration = "server:/decorations/Config.pike";
917  if ( !config_obj->has_decoration( config_decoration ) ) {
918  config_obj->add_decoration( config_decoration );
919  MESSAGE( "Decorated /config/%s with Config decoration: %s",
920  config_obj->get_identifier(), config_decoration );
921  }
922 
923  return true;
924 }
925 
926 public:
927 
928 
929 protected:
930  void start_services()
931 {
932  object u_service = USER("service");
933 
934  // generate a ticket for the service user that lasts ca. 10 years
935  // (this ticket is generated on every restart, so a server could run
936  // up to 10 years without restart before the services can't reconnect):
937  Stdio.write_file( "service.pass",
938  u_service->get_ticket( time() + 316224000 ), 0600 );
939  return;
940 }
941 
942 public:
943 
944 
945 mixed query_config(mixed config)
946 {
947  if ( config == "database" )
948  return 0;
949  return mConfigs[config];
950 }
951 
952 
953 mapping read_certificate () {
954  string path = query_config("config-dir");
955  return cert.read_certificate( ({
956  ({ path+"steam.crt", path+"steam.key" }),
957  path+"steam.cer",
958  }) );
959 }
960 
961 string plusminus(int num)
962 {
963  return ( num > 0 ? "+"+num: (string)num);
964 }
965 
966 
967 string get_database()
968 {
969  //MESSAGE("CALLERPRG="+master()->describe_program(CALLERPROGRAM));
970  if ( CALLER == oDatabase ||
971  CALLERPROGRAM==(program)"/kernel/steamsocket.pike" )
972  return mConfigs["database"];
973  MESSAGE("NO ACCESS to database for "+
974  master()->describe_program(CALLERPROGRAM)+
975  " !!!!!!!!!!!!!!!!!!!!!!!\n\n");
976  return "no access";
977 }
978 
979 mapping get_configs()
980 {
981  mapping res = copy_value(mConfigs);
982  res["database"] = 0;
983  return res;
984 }
985 
986 mixed get_config(mixed key)
987 {
988  return mConfigs[key];
989 }
990 
991 
992 string get_version()
993 {
994  return STEAM_VERSION;
995 }
996 
997 int get_last_reboot()
998 {
999  return iLastReboot;
1000 }
1001 
1002 private:
1003  private void got_kill(int sig)
1004 {
1005  MESSAGE("Closing ports!");
1006  if (objectp(nmaster)) {
1007  foreach(nmaster->get_ports(), object p)
1008  catch(close_port(p));
1009  }
1010 
1011  MESSAGE( "Shutting down ! (waiting for database to save %d items [%d queued/%d busy])",
1012  oDatabase->get_save_size(), oDatabase->get_save_queue_size()[0],
1013  oDatabase->get_save_queue_size()[1]);
1014  werror( "Shutting down ! (waiting for database to save %d items [%d queued/%d busy])\n",
1015  oDatabase->get_save_size(), oDatabase->get_save_queue_size()[0],
1016  oDatabase->get_save_queue_size()[1]);
1017  oDatabase->log_save_queue( 1000 );
1018  catch(oDatabase->wait_for_db_lock());
1019  MESSAGE("Database finished, exiting.");
1020  werror("Database finished, exiting.\n");
1021  string restart_time = Stdio.read_file( "/server.restart" );
1022  if ( stringp(restart_time) && restart_time != "" ) {
1023  MESSAGE( "Will restart due to restart request from %s", restart_time );
1024  werror( "Will restart due to restart request from %s", restart_time );
1025  _exit(0);
1026  }
1027  _exit(1);
1028 }
1029 
1030 public:
1031 
1032 private:
1033  private void got_hangup(int sig)
1034 {
1035  got_kill(sig);
1036 }
1037 
1038 public:
1039 
1040 private:
1041  private void got_sigquit(int sig)
1042 {
1043  nmaster->describe_threads();
1044  got_kill(sig);
1045 }
1046 
1047 public:
1048 
1049 
1050 
1051 mapping get_errors()
1052 {
1053  return mErrors;
1054 }
1055 
1056 void add_error(int t, mixed err)
1057 {
1058  mErrors[t] = err;
1059 }
1060 
1061 int main(int argc, array argv)
1062 {
1063 
1064  mErrors = ([ ]);
1065  mConfigs = ([ ]);
1066  int i;
1067  string path;
1068  int pid = getpid();
1069  sTest = 0;
1070  path = getcwd();
1071 
1072  MESSAGE( "sTeam " + STEAM_VERSION + " running on " + version() + " ..." );
1073  werror( "sTeam " + STEAM_VERSION + " running on " + version() + "\nStartup on " + ctime(time()) );
1074  if ( BRAND_NAME != "steam" )
1075  MESSAGE( "Brand name is '%s'.\n", BRAND_NAME );
1076 
1077  string pidfile = path + "/steam.pid";
1078 
1079  mConfigs["logdir"] = LOG_DIR;
1080  if ( mConfigs["logdir"][-1]!='/' ) mConfigs["logdir"] += "/";
1081  mConfigs["config-dir"] = CONFIG_DIR;
1082  if ( mConfigs["config-dir"][-1]!='/' ) mConfigs["config-dir"] += "/";
1083 
1084  for ( i = 1; i < sizeof(argv); i++ ) {
1085  string cfg, val;
1086  if ( argv[i] == "--test" )
1087  sTest = "all";
1088  else if ( sscanf(argv[i], "--%s=%s", cfg, val) == 2 ) {
1089  int v;
1090  if ( cfg == "test" ) {
1091  sTest = val;
1092  }
1093  if ( cfg == "email" || cfg == "mail" ) {
1094  aEmailAddresses = Config.array_value( val );
1095  }
1096  else if ( cfg == "pid" ) {
1097  pidfile = val;
1098  }
1099  else if ( sscanf(val, "%d", v) == 1 )
1100  mConfigs[cfg] = v;
1101  else
1102  mConfigs[cfg] = val;
1103  }
1104  else if ( sscanf(argv[i], "-D%s", cfg) == 1 ) {
1105  add_constant(cfg, 1);
1106  }
1107  }
1108 
1109  mixed err = catch(_stderr = Stdio.File(mConfigs["logdir"]+"/errors.log", "r"));
1110  if(err)
1111  MESSAGE("Failed to open %s/errors.log", mConfigs["logdir"]);
1112 
1113  signal(signum("QUIT"), got_kill);
1114  signal(signum("TERM"), got_kill);
1115  signal(signum("SIGHUP"), got_hangup);
1116  signal(signum("SIGINT"), got_hangup);
1117  signal(signum("SIGQUIT"), got_sigquit);
1118  return start_server();
1119 }
1120 
1121 object get_stderr()
1122 {
1123  return _stderr;
1124 }
1125 
1126 object get_stdout()
1127 {
1128  return _stdout;
1129 }
1130 
1131 mixed get_module(string module_id)
1132 {
1133  object module;
1134  module = mModules[module_id];
1135  if ( objectp(module) && module->status() >= 0 )
1136  return 0;
1137 }
1138 
1139 mixed call_module(string module, string func, mixed ... args)
1140 {
1141  object mod = mModules[module];
1142  if ( !objectp(mod) )
1143  THROW("Failed to call module "+ module + " - not found.", E_ERROR);
1144  function f = mod->find_function(func);
1145  if ( !functionp(f) )
1146  THROW("Function " + func + " not found inside Module " + module +" !", E_ERROR);
1147  if ( sizeof(args) == 0 )
1148  return f();
1149  return f(@args);
1150 }
1151 
1152 
1153 mapping get_modules()
1154 {
1155  return copy_value(mModules);
1156 }
1157 
1158 array get_module_objs()
1159 {
1160  return values(mModules);
1161 }
1162 
1163 protected:
1164  object f_open_port(string pname)
1165 {
1166  program prg = (program)("/net/port/"+pname);
1167  object port = nmaster->new("/net/port/"+pname);
1168  mPortPrograms[port->get_port_name()] = prg;
1169  if ( get_config(port->get_port_config()) == "disabled" )
1170  return 0;
1171  if ( !port->open_port() && port->port_required() )
1172  return 0;
1173  nmaster->register_port(port);
1174  return port;
1175 }
1176 
1177 public:
1178 
1179 /**
1180  * Open a single port of the server.
1181  *
1182  * @param string pname - the name of the port to open.
1183  * @return the port object or zero.
1184  */
1185 object open_port(string pname)
1186 {
1187  if ( _ADMIN->is_member(nmaster->this_user()) ) {
1188  return f_open_port(pname);
1189  }
1190  return 0;
1191 }
1192 
1193 /**
1194  * Open all ports of the server.
1195  * See the /net/port/ Directory for all available ports.
1196  *
1197  */
1198 protected:
1199  void open_ports()
1200 {
1201  array ports;
1202 
1203  mPortPrograms = ([ ]);
1204 
1205  ports = nmaster->get_dir("/net/port");
1206  MESSAGE("Opening ports ...");
1207  // check for steam.cer...
1208  if ( !Stdio.exist(query_config("config-dir")+"steam.cer") &&
1209  !Stdio.exist(query_config("config-dir")+"steam.crt") ) {
1210  MESSAGE("Certificate File Missing - creating new one ...\n");
1211  string cert = cert.create_cert( ([
1212  "country": "Germany",
1213  "organization": "University of Paderborn",
1214  "unit": "Open sTeam",
1215  "locality": "Paderborn",
1216  "province": "NRW",
1217  "name": get_server_name(),
1218  ]) );
1219  Stdio.write_file( query_config("config-dir") + "steam.cer", cert);
1220  }
1221  for ( int i = sizeof(ports) - 1; i >= 0; i-- ) {
1222  if ( ports[i][0] == '#' || ports[i][0] == '.' || ports[i][-1] == '~' )
1223  continue;
1224  if ( sscanf(ports[i], "%s.pike", ports[i]) != 1 ) continue;
1225  f_open_port(ports[i]);
1226  }
1227 }
1228 
1229 public:
1230 
1231 /**
1232  * Get all string identifiers of available ports (open and close).
1233  *
1234  * @return Array of port identifier strings.
1235  */
1236 array get_ports()
1237 {
1238  return indices(mPortPrograms);
1239 }
1240 
1241 int close_port(object p)
1242 {
1243  if ( !objectp(p) )
1244  error("Cannot close NULL port !");
1245  if ( _ADMIN->is_member(nmaster->this_user()) ) {
1246  if ( functionp(p->close_port) )
1247  p->close_port();
1248  if ( objectp(p) )
1249  destruct(p);
1250  return 1;
1251  }
1252  return 0;
1253 }
1254 
1255 int restart_port(object p)
1256 {
1257  if ( _ADMIN->is_member(nmaster->this_user()) ) {
1258  program prg = object_program(p);
1259  if ( functionp(p->close_port) )
1260  p->close_port();
1261  if ( objectp(p) )
1262  destruct(p);
1263  p = prg();
1264  if ( p->open_port() )
1265  MESSAGE("Port restarted ....");
1266  else {
1267  MESSAGE("Restarting port failed.");
1268  return 0;
1269  }
1270  nmaster->register_port(p);
1271  return 1;
1272  }
1273  return 0;
1274 }
1275 
1276 mapping debug_memory(void|mapping debug_old)
1277 {
1278  mapping dmap = Debug.memory_usage();
1279  if (!mappingp(debug_old) )
1280  return dmap;
1281  foreach(indices(dmap), string idx)
1282  dmap[idx] = (dmap[idx] - debug_old[idx]);
1283  return dmap;
1284 }
1285 
1286 
1287 /**
1288  * Install all modules of the server.
1289  *
1290  */
1291 void install_modules()
1292 {
1293  foreach ( indices(mModules), string module ) {
1294  if ( !objectp(mModules[module]) ) continue;
1295  mixed err = catch {
1296  mModules[module]->runtime_install();
1297  };
1298  if(err)
1299  FATAL( "Failed to install module %s (%O)\n%s",
1300  module, mModules[module],PRINT_BT(err) );
1301  }
1302  foreach ( indices(mModules), string module ) {
1303  if ( !stringp(module) || module == "" ) {
1304  FATAL( "Removing module with invalid name: %O, object: %O\n",
1305  module, mModules[module] );
1306  m_delete( mModules, module );
1307  continue;
1308  }
1309  if ( !objectp(mModules[module]) ) {
1310  FATAL( "Removing broken (non-object) module: %O\n", module );
1311  m_delete( mModules, module );
1312  continue;
1313  }
1314  if ( mModules[module]->status() == PSTAT_DELETED ||
1315  mModules[module]->status() == PSTAT_FAIL_DELETED )
1316  {
1317  FATAL( "Removing deleted module: %O, object: %O, status: %O\n",
1318  module, mModules[module], mModules[module]->status() );
1319  m_delete( mModules, module );
1320  }
1321  }
1322  save_modules();
1323 }
1324 
1325 /**
1326  * register a module - can only be called by database !
1327  *
1328  * @param object mod - the module to register
1329  */
1330 final void register_module(object mod)
1331 {
1332  if ( CALLER == oDatabase ) {
1333  mModules[mod->get_identifier()] = mod;
1334  save_modules();
1335  }
1336 }
1337 
1338 final bool is_registered_module(object mod)
1339 {
1340  if (mModules[mod->get_identifier()] == mod) {
1341  return true;
1342  }
1343  return false;
1344 }
1345 
1346 final void unregister_module(object mod)
1347 {
1348  if ( !objectp(mod) ) return;
1349  !zero_type(mModules[mod->get_identifier()]) ) {
1350  m_delete( mModules, mod->get_identifier() );
1351  save_modules();
1352  }
1353 }
1354 
1355 private:
1356 private mapping load_module_configuration(string name)
1357 {
1358  mapping config;
1359 
1360  string content = Stdio.read_file(CONFIG_DIR+"/"+name+".xml");
1361  if ( stringp(content) )
1362  config = Module.read_config(content, name);
1363  else
1364  config = ([ ]);
1365 
1366  // parse module code for "#define DEPENDENCIES" line:
1367  array file_dependencies;
1368  if ( catch {
1369  array tokens = Parser.Pike.split( Stdio.read_file(nmaster->apply_mount_points("/modules")+"/"+name+".pike") );
1370  string deps;
1371  foreach ( tokens, string token ) {
1372  if ( sscanf( token, "#define%*[ \t]DEPENDENCIES%*[ \t]%s\n", deps ) > 1 ) {
1373  file_dependencies = deps / " ";
1374  break;
1375  }
1376  }
1377  } != 0 ) {
1378  werror( "Could not parse module : %s\n", name );
1379  }
1380  if ( arrayp(file_dependencies) ) {
1381  if ( !arrayp(config->depends) ) config->depends = file_dependencies;
1382  else foreach ( file_dependencies, string dep )
1383  if ( search( config->depends, dep ) < 0 )
1384  config->depends += ({ dep });
1385  }
1386 
1387  config["score"] = 1000;
1388  return config;
1389 }
1390 
1391 public:
1392 
1393 /**
1394  * Load a module
1395  *
1396  * @param string mpath - the filename of the module
1397  * @return
1398  */
1399 private:
1400 private object load_module(string mpath)
1401 {
1402  if ( sscanf(mpath, "%s.pike", mpath) != 1 )
1403  return 0;
1404  MESSAGE( "LOADING MODULE:" + mpath + " ... " );
1405  /* dont load the module that keeps the list of all modules
1406  * Because it is loaded by database
1407  */
1408  if ( mpath == "modules" )
1409  return 0;
1410 
1411  object module = 0;
1412  int database_id = oDatabase->get_variable("#"+mpath);
1413  if ( database_id != 0 )
1414  module = oDatabase->find_object(database_id);
1415 
1416  // we found an already existing one
1417  if ( objectp(module) && module->status() >= 0 )
1418  {
1419  if ( objectp(module->get_object()) )
1420  mModules[module->get_identifier()] = module;
1421  else
1422  FATAL("Failed to create instance of "+mpath + " (status="+
1423  module->status()+")");
1424  }
1425  else
1426  {
1427  MESSAGE("Creating new instance of "+mpath);
1428  // first try to read config file for that module (if any)
1429 
1430  mixed err = catch {
1431  module = nmaster->new("/modules/"+mpath+".pike");
1432  };
1433  if ( err != 0 ) {
1434  FATAL("Error while creating new instance of " + mpath + "\n" +
1435  PRINT_BT(err));
1436  }
1437  err = catch {
1438  if (objectp(module)) {
1439  if (!functionp(module->this) ) /* check existance of function */
1440  {
1441  FATAL("unable to register module \""+mpath+
1442  "/classes/Object");
1443  module = 0;
1444  }
1445  else
1446  {
1447  oDatabase->set_variable("#"+mpath,
1448  module->get_object_id());
1449  mModules[module->get_identifier()] = module;
1450  module->set_attribute(OBJ_DESC, "");
1451  module->loaded();
1452  module->created();
1453  }
1454  }
1455  };
1456  if ( err != 0 )
1457  FATAL("Error registering module \""+mpath+"\":\n"+PRINT_BT(err));
1458  }
1459 
1460  if (objectp(module))
1461  MESSAGE("alias " + module->get_identifier() +
1462  " OID("+module->get_object_id()+")");
1463 
1464  return module;
1465 }
1466 
1467 public:
1468 
1469 bool is_module(object mod)
1470 {
1471  if ( !functionp(mod->get_identifier) )
1472  return false;
1473  object module = mModules[mod->get_identifier()];
1474  if ( objectp(module) )
1475  return 0;
1476 }
1477 
1478 private:
1479 private int order_modules(string mod1, string mod2, mapping configs)
1480 {
1481  if ( sscanf(mod1, "%s.pike", mod1) == 0)
1482  return 0;
1483  if ( sscanf(mod2, "%s.pike", mod2) == 0 )
1484  return 0;
1485 
1486  return configs[mod1]->score < configs[mod2]->score;
1487 }
1488 
1489 public:
1490 
1491 void update_depend(string depend, mapping conf)
1492 {
1493  MESSAGE("Updating: %s", depend);
1494  if ( conf[depend]->mark == 1 )
1495  steam_error("Loop in module Dependencies detected !");
1496  conf[depend]->mark = 1;
1497 
1498  conf[depend]->score++;
1499  if ( arrayp(conf[depend]->depends) ) {
1500  foreach(conf[depend]->depends, string depdep) {
1501  update_depend(depdep, conf);
1502  }
1503  }
1504  conf[depend]->mark = 0;
1505 }
1506 
1507 protected:
1508  int filter_system(string fname)
1509 {
1510  if ( search(fname, "~") >= 0 ) return 0;
1511  if ( fname[0] == '#' || fname[0] == '.' ) return 0;
1512  return 1;
1513 }
1514 
1515 public:
1516 
1517 /**
1518  * Load all modules.
1519  *
1520  */
1521 protected:
1522  void load_modules()
1523 {
1524  int i, tt;
1525 
1526  object module;
1527  array modules;
1528 
1529  tt = f_get_time_millis();
1530  mModules = ([]);
1531  modules = nmaster->get_dir("/modules");
1532 
1533  array priority_load = ({
1534  "log.pike", "security.pike", "cache.pike", "groups.pike", "users.pike",
1535  "objects.pike", "filepath.pike", "message.pike", "mailbox.pike",
1536  "xml_converter.pike" });
1537 
1538  modules -= priority_load;
1539  modules = priority_load + modules;
1540  modules = filter(modules, filter_system);
1541 
1542  mapping configurations = ([ ]);
1543  for ( i = 0; i < sizeof(modules); i++ ) {
1544  string modname;
1545  if ( sscanf(modules[i], "%s.pike", modname) == 0 )
1546  continue;
1547 
1548  mapping conf = load_module_configuration(modname);
1549  if ( arrayp(conf->depends) ) {
1550  foreach(conf->depends, string depend) {
1551  if ( !mappingp(configurations[depend]) )
1552  configurations[depend] = ([ "score": 1000, "depends": 0, ]);
1553  update_depend(depend, configurations);
1554  }
1555  }
1556 
1557  if ( !mappingp(configurations[modname]) )
1558  configurations[modname] = conf;
1559  else
1560  configurations[modname]->depends = conf->depends;
1561  }
1562  // now we need to sort our modules according to the graph in configurations
1563  // sortierung durch vergleich 2er element ist ok
1564  modules = Array.sort_array(modules, order_modules, configurations);
1565 
1566  // finally load the modules
1567  for ( i = 0; i < sizeof(modules); i++ ) {
1568  if ( search(modules[i], "~") >= 0 ) continue;
1569  if ( modules[i][0] == '#' || modules[i][0] == '.' ) continue;
1570  // only load pike programms !
1571  load_module(modules[i]);
1572  }
1573 
1574  foreach(values(mModules), object module) {
1575  if ( objectp(module) )
1576  module->post_load_module();
1577  }
1578  MESSAGE("Loading modules finished in %d ms...",
1579  f_get_time_millis() - tt);
1580 }
1581 
1582 public:
1583 
1584 void load_programs()
1585 {
1586  //bugfix for pike 7.8. init ldap protocol once here to prevent exeption caused
1587  //by ${PIKE_MODULE_PATH} problem
1588  catch( Protocols.LDAP.client() );
1589 
1590  string cl;
1591  int tt = f_get_time_millis();
1592 
1593  array classfiles = nmaster->get_dir("/classes");
1594  foreach(classfiles, cl) {
1595  if ( cl[0] == '.' || cl[0] == '#' || search(cl, "~") >= 0 ||
1596  search(cl, "CVS") >= 0 ) continue;
1597 
1598  MESSAGE("Preparing class: " + cl);
1599  program prg = (program) ("/classes/"+cl);
1600  }
1601  classfiles = nmaster->get_dir("/kernel");
1602  foreach(classfiles, cl) {
1603  if ( cl[0] == '.' || cl[0] == '#' || search(cl, "~") >= 0 ||
1604  search(cl, "CVS") >= 0 )
1605  continue;
1606 
1607  MESSAGE("Preparing class: " + cl);
1608  program prg = (program) ("/kernel/"+cl);
1609  }
1610  MESSAGE("Loading programs finished in %d ms...", f_get_time_millis() - tt);
1611 }
1612 
1613 protected:
1614  void load_pmods(string path, void|string symbol)
1615 {
1616  string newsymbol;
1617  int tt = f_get_time_millis();
1618 
1619  array pmods = nmaster->get_dir(path);
1620  foreach(pmods, string pmod) {
1621  if ( nmaster->is_dir(path + pmod) ) {
1622  sscanf(pmod, "%s.pmod", newsymbol);
1623  if ( stringp(symbol) )
1624  load_pmods(path + pmod, symbol + "." + newsymbol);
1625  else
1626  load_pmods(path + pmod, newsymbol);
1627 
1628  nmaster->resolv(pmod);
1629  }
1630  else {
1631  if (sscanf(pmod, "%s.pmod", pmod) ||
1632  (stringp(symbol) && sscanf(pmod, "%s.pike", pmod)) )
1633  {
1634  if ( search(pmod, ".") >= 0 || search(pmod, "#") >= 0 ) continue;
1635  MESSAGE("Loading Pike-Module %s",
1636  (stringp(symbol)?symbol+".":"")+pmod);
1637  if ( stringp(symbol) )
1638  nmaster->resolv(symbol + "." + pmod);
1639  else
1640  nmaster->resolv(pmod);
1641  }
1642  }
1643  }
1644 }
1645 
1646 public:
1647 
1648 /**
1649  * Load all modules from the database ( stored in the admin group )
1650  *
1651  */
1652 protected:
1653  void load_modules_db()
1654 {
1655  MESSAGE("Loading registered modules from database...");
1656  mixed err = catch {
1657  object groups = mModules["groups"];
1658  if ( objectp(groups) ) {
1659  object admin = _ADMIN;
1660  if ( !objectp(admin) )
1661  return;
1662  mapping modules = admin->query_attribute("modules");
1663  if ( !mappingp(modules) ) {
1664  MESSAGE("No additional modules registered yet!");
1665  return;
1666  }
1667  // sync modules saved in admin group with already loaded
1668  foreach ( indices(modules), string m ) {
1669  if ( !mModules[m] )
1670  mModules[m] = modules[m];
1671  }
1672  }
1673  MESSAGE("Loading modules from database finished.");
1674  };
1675  if ( err != 0 )
1676  FATAL("Loading Modules from Database failed.\n"+PRINT_BT(err));
1677 }
1678 
1679 public:
1680 
1681 /**
1682  *
1683  *
1684  * @param
1685  * @return
1686  * @see
1687  */
1688 protected:
1689  void load_factories()
1690 {
1691  int i;
1692  string factory_name;
1693  object factory;
1694  object proxy;
1695  mixed err;
1696 
1697  array factories;
1698  array loaded = ({});
1699 
1700  int tt = f_get_time_millis();
1701 
1702  factories = nmaster->get_dir("/factories");
1703  factories -= ({ "DateFactory.pike" });
1704  factories -= ({ "CalendarFactory.pike" });
1705  factories -= ({ "AnnotationFactory.pike" });
1706  factories = ({ "DateFactory.pike", "CalendarFactory.pike" }) + factories;
1707  for ( i = sizeof(factories) - 1; i >= 0; i-- ) {
1708  if ( sscanf(factories[i], "%s.pike", factory_name) == 0 )
1709  continue;
1710 
1711  if ( search(factory_name, "~") >= 0 || search(factory_name, "~")>=0 ||
1712  search(factory_name, ".") == 0 || search(factory_name,"#")>=0 )
1713  continue;
1714  MESSAGE("LOADING FACTORY:%s ...", factory_name);
1715  proxy = _Persistence->lookup(factory_name);
1716  if ( !objectp(proxy) ) {
1717  MESSAGE("Creating new instance...");
1718  err = catch {
1719  factory = nmaster->new("/factories/"+factory_name+".pike",
1720  factory_name);
1721  };
1722  if ( err != 0 ) {
1723  MESSAGE("Error while loading factory " + factory_name + "\n"+
1724  PRINT_BT(err));
1725  continue;
1726  }
1727 
1728  mClasses["_loading"] = proxy;
1729  proxy->created();
1730  m_delete(mClasses, "_loading");
1731  if (proxy->status()>=PSTAT_SAVE_OK)
1732  {
1733  MESSAGE("New Factory registered !");
1734  MODULE_OBJECTS->register(factory_name, proxy);
1735  }
1736  }
1737  else {
1738  int iProxyStatus;
1739  err = catch {
1740  int iProxyStatus = proxy->force_load();
1741  };
1742  if (err!=0) {
1743  MESSAGE("Error while loading factory %s status(%d)\n%s\n",
1744  factory_name, iProxyStatus, master()->describe_backtrace(err));
1745  }
1746  }
1747 
1748  if (proxy->status() >= PSTAT_SAVE_OK)
1749  {
1750  mClasses[proxy->get_class_id()] = proxy;
1751  loaded += ({ proxy });
1752  err = catch {
1753  proxy->unlock_attribute(OBJ_NAME);
1754  proxy->set_attribute(OBJ_NAME, proxy->get_identifier());
1755  proxy->lock_attribute(OBJ_NAME);
1756  };
1757  if ( err != 0 ) {
1758  FATAL("There was an error loading a factory...\n"+
1759  PRINT_BT(err));
1760  }
1761  }
1762  else
1763  MESSAGE("factory is %s with status %d",
1764  factory_name, proxy->status());
1765  }
1766  MESSAGE("Loading factories finished in %d ms ...", f_get_time_millis()-tt);
1767 }
1768 
1769 public:
1770 
1771 /**
1772  *
1773  *
1774  * @param
1775  * @return
1776  * @see
1777  */
1778 void load_objects()
1779 {
1780  object factory, root, room, admin, world, steam, guest, postman;
1781  int i;
1782  mapping vars = ([ ]);
1783 
1784  int tt = f_get_time_millis();
1785 
1786  MESSAGE( "Loading Groups:" );
1787  MESSAGE( "* sTeam" );
1788  steam = _Persistence->lookup_group("sTeam");
1789  if ( !objectp(steam) ) {
1790  factory = get_factory(CLASS_GROUP);
1791  vars["name"] = "sTeam";
1792  steam = factory->execute(vars);
1793  ASSERTINFO(objectp(steam), "Failed to create sTeam group!");
1794  steam->set_attribute(OBJ_DESC, "The group of all sTeam users.");
1795  }
1796  add_constant("_GroupAll", steam);
1797  MESSAGE( "* Everyone" );
1798  world = _Persistence->lookup_group("Everyone");
1799  if ( !objectp(world) ) {
1800  factory = get_factory(CLASS_GROUP);
1801  vars["name"] = "Everyone";
1802  world = factory->execute(vars);
1803  ASSERTINFO(objectp(world), "Failed to create world user group!");
1804  world->set_attribute(
1805  OBJ_DESC, "This is the virtual group of all internet users.");
1806  }
1807  MESSAGE( "* Help" );
1808  object hilfe = _Persistence->lookup_group("help");
1809  if ( !objectp(hilfe) ) {
1810  factory = get_factory(CLASS_GROUP);
1811  vars["name"] = "help";
1812  hilfe = factory->execute(vars);
1813  ASSERTINFO(objectp(hilfe), "Failed to create hilfe group!");
1814  hilfe->set_attribute(
1815  OBJ_DESC, "This is the help group of steam.");
1816  }
1817  mixed err = catch {
1818  hilfe->sanction_object(steam, SANCTION_READ|SANCTION_ANNOTATE);
1819  };
1820  if(err)
1821  MESSAGE("Failed sanction on hilfe group\n"+PRINT_BT(err));
1822 
1823  bool rootnew = false;
1824 
1825  MESSAGE( "Groups loaded (took %d ms)", f_get_time_millis() - tt );
1826  MESSAGE( "Loading users:" );
1827  MESSAGE( "* root" );
1828  root = _Persistence->lookup_user("root");
1829  if ( !objectp(root) ) {
1830  rootnew = true;
1831  factory = get_factory(CLASS_USER);
1832  vars["name"] = "root";
1833  vars["pw"] = "steam";
1834  vars["email"] = "";
1835  vars["firstname"] = "Root";
1836  vars["fullname"] = "User";
1837  root = factory->execute(vars);
1838  root->activate_user(factory->get_activation());
1839  ASSERTINFO(objectp(root), "Failed to create root user !");
1840  root->set_attribute(
1841  OBJ_DESC, "The root user is the first administrator of sTeam.");
1842  }
1843  if ( mConfigs->password ) {
1844  root->set_user_password(mConfigs->password);
1845  m_delete(mConfigs, "password");
1846  write_config_to_admin();
1847  }
1848  MESSAGE( "* guest" );
1849  guest = _Persistence->lookup_user("guest");
1850  if ( !objectp(guest) ) {
1851  factory = get_factory(CLASS_USER);
1852  vars["name"] = "guest";
1853  vars["pw"] = "guest";
1854  vars["email"] = "none";
1855  vars["firstname"] = "Guest";
1856  vars["fullname"] = "User";
1857  guest = factory->execute(vars);
1858 
1859  ASSERTINFO(objectp(guest), "Failed to create guest user !");
1860  guest->activate_user(factory->get_activation());
1861  guest->sanction_object(world, SANCTION_MOVE); // move around guest
1862  object guest_wr = guest->query_attribute(USER_WORKROOM);
1863  guest_wr->sanction_object(guest, SANCTION_READ|SANCTION_INSERT);
1864  guest->set_attribute(
1865  OBJ_DESC, "Guest is the guest user.");
1866  }
1867  get_factory(CLASS_USER)->reset_guest();
1868  ASSERTINFO(guest->get_user_name() == "guest", "False name of guest !");
1869  world->add_member(guest);
1870 
1871  MESSAGE( "* service" );
1872  object service = _Persistence->lookup_user("service");
1873  if ( !objectp(service) ) {
1874  factory = get_factory(CLASS_USER);
1875  vars["name"] = "service";
1876  vars["pw"] = "";
1877  vars["email"] = "none";
1878  vars["fullname"] = "Service";
1879  service = factory->execute(vars);
1880 
1881  ASSERTINFO(objectp(service), "Failed to create service user !");
1882  service->activate_user(factory->get_activation());
1883  service->set_user_password("0", 1);
1884  service->sanction_object(world, SANCTION_MOVE); // move around service
1885  object service_wr = service->query_attribute(USER_WORKROOM);
1886  service_wr->sanction_object(service, SANCTION_READ|SANCTION_INSERT);
1887  service->set_attribute(
1888  OBJ_DESC, "Service is the service user.");
1889  }
1890 
1891  MESSAGE( "* postman" );
1892  postman = _Persistence->lookup_user("postman");
1893  if ( !objectp(postman) )
1894  {
1895  factory = get_factory(CLASS_USER);
1896  vars["name"] = "postman";
1897 #if constant(Crypto.Random)
1898  vars["pw"] = Crypto.Random.random_string(10); //disable passwd
1899 #else
1900  vars["pw"] = Crypto.randomness.pike_random()->read(10); //disable passwd
1901 #endif
1902  vars["email"] = "";
1903  vars["fullname"] = "Postman";
1904  postman = factory->execute(vars);
1905 
1906  ASSERTINFO(objectp(postman), "Failed to create postman user !");
1907  postman->activate_user(factory->get_activation());
1908  postman->sanction_object(world, SANCTION_MOVE); // move postman around
1909  object postman_wr = postman->query_attribute(USER_WORKROOM);
1910  postman_wr->sanction_object(postman, SANCTION_READ|SANCTION_INSERT);
1911  postman->set_attribute(OBJ_DESC,
1912  "The postman delivers emails sent to sTeam from the outside.");
1913  }
1914  ASSERTINFO(postman->get_user_name() == "postman", "False name of postman !");
1915  err = catch {
1916  room = _Persistence->lookup("rootroom");
1917  if ( !objectp(room) ) {
1918  factory = get_factory(CLASS_ROOM);
1919  vars["name"] = "root-room";
1920  room = factory->execute(vars);
1921  ASSERTINFO(objectp(room), "Failed to create root room !");
1922  room->sanction_object(steam, SANCTION_READ);
1923  ASSERTINFO(MODULE_OBJECTS->register("rootroom", room),
1924  "Failed to register room !");
1925  root->move(room);
1926  room->set_attribute(
1927  OBJ_DESC, "The root room contains system documents.");
1928  }
1929  };
1930  if ( err ) {
1931  MESSAGE( "Failed to create root room" );
1932  FATAL( "Failed to create root room:\n%s\n", PRINT_BT(err) );
1933  }
1934 
1935  guest->move(room);
1936  postman->move(room);
1937  root->move(room);
1938  if ( rootnew ) {
1939  // only create the exit in roots workroom if the user has
1940  // been just created
1941  MESSAGE("New roots workroom");
1942  object workroom = root->query_attribute(USER_WORKROOM);
1943  if ( objectp(workroom) ) {
1944  object exittoroot;
1945  factory = get_factory(CLASS_EXIT);
1946  exittoroot = factory->execute((["name":"root-room",
1947  "exit_to":room,]));
1948  exittoroot->move(workroom);
1949  }
1950  MESSAGE(" - created exits and root image\n");
1951  }
1952 
1953  admin = _Persistence->lookup_group("Admin");
1954  if ( !objectp(admin) ) {
1955  factory = get_factory(CLASS_GROUP);
1956  vars["name"] = "Admin";
1957  admin = factory->execute(vars);
1958  ASSERTINFO(objectp(admin), "Failed to create Admin user group!");
1959  admin->set_permission(ROLE_ALL_ROLES);
1960  admin->add_member(root);
1961  admin->sanction_object(root, SANCTION_ALL);
1962  admin->set_attribute(
1963  OBJ_DESC, "The admin group is the group of administrators.");
1964  }
1965  if ( admin->get_permission() != ROLE_ALL_ROLES )
1966  admin->set_permission(ROLE_ALL_ROLES);
1967  admin->add_member(root);
1968  admin->add_member(service);
1969 
1970  ASSERTINFO(admin->get_permission() == ROLE_ALL_ROLES,
1971  "Wrong permissions for admin group !");
1972 
1973  object groups = _Persistence->lookup_group("Groups");
1974  if ( !objectp(groups) ) {
1975  MESSAGE("** Creating Groups");
1976  factory = get_factory(CLASS_GROUP);
1977  vars["name"] = "Groups";
1978  groups = factory->execute(vars);
1979  ASSERTINFO(objectp(groups), "Failed to create Groups user group!");
1980  groups->set_attribute(OBJ_DESC,
1981  "The group to create private groups in.");
1982  groups->sanction_object(steam, SANCTION_INSERT|SANCTION_READ);
1983  // everyone can add users and groups to that group!
1984  }
1985 
1986  object wikigroups = _Persistence->lookup_group("WikiGroups");
1987  if ( !objectp(wikigroups) ) {
1988  factory = get_factory(CLASS_GROUP);
1989  vars["name"] = "WikiGroups";
1990  wikigroups = factory->execute(vars);
1991  ASSERTINFO(objectp(wikigroups),"Failed to create WikiGroups user group!");
1992  wikigroups->set_attribute(OBJ_DESC,
1993  "The group to create wiki groups in.");
1994  wikigroups->sanction_object(steam, SANCTION_INSERT|SANCTION_READ);
1995  // everyone can add users and groups to that group!
1996  }
1997 
1998  // as soon as the coder group has members, the security is enabled!
1999  object coders = _Persistence->lookup_group("coder");
2000  if ( !objectp(coders) ) {
2001  factory = get_factory(CLASS_GROUP);
2002  vars["name"] = "coder";
2003  coders = factory->execute(vars);
2004  ASSERTINFO(objectp(coders), "Failed to create coder user group!");
2005  coders->set_attribute(OBJ_DESC,
2006  "The group of people allowed to write scripts.");
2007  coders->add_member(root);
2008  }
2009  mapping roles = ([ ]);
2010 
2011  roles["admin"] = Roles.Role("Administrator", ROLE_ALL_ROLES, 0);
2012  roles["steam"] = Roles.Role("sTeam User", ROLE_READ_ALL, steam);
2013  admin->add_role(roles->admin);
2014  steam->add_role(roles->steam);
2015 
2016  object cont = null;
2017  err = catch { cont = MODULE_FILEPATH->path_to_object("/factories"); };
2018  if (err)
2019  MESSAGE(PRINT_BT(err));
2020 
2021  if ( !objectp(cont) )
2022  {
2023  factory = get_factory(CLASS_CONTAINER);
2024  vars["name"] = "factories";
2025  cont = factory->execute(vars);
2026  ASSERTINFO(objectp(cont),"Failed to create the factories container!");
2027  cont->set_attribute(OBJ_DESC, "This container is for the factories.");
2028  }
2029  ASSERTINFO(objectp(cont), "/factories/ not found");
2030  cont->move(room);
2031 
2032  // backtrace container
2033 
2034  oBacktraces = _Persistence->lookup("backtraces");
2035  if ( !objectp(oBacktraces) ) {
2036  oBacktraces = get_factory(CLASS_CONTAINER)->execute(
2037  (["name":"backtraces", ]) );
2038  ASSERTINFO(MODULE_OBJECTS->register("backtraces", oBacktraces),
2039  "Failed to register backtraces container !");
2040  }
2041  err = catch(oBacktraces->set_attribute(OBJ_URL, "/backtraces"));
2042  if(err)
2043  MESSAGE("failed to set attribute ob backtraces\n"+PRINT_BT(err));
2044  oBacktraces->move(room);
2045 
2046  object oPackages = _Persistence->lookup("packages");
2047  if ( !objectp(oPackages) ) {
2048  oPackages = room->get_object_byname("packages");
2049  if ( !objectp(oPackages) )
2050  oPackages = get_factory(CLASS_CONTAINER)->execute(
2051  (["name":"packages",]));
2052  ASSERTINFO(MODULE_OBJECTS->register("packages", oPackages),
2053  "Failed to register packages container !");
2054  }
2055  oPackages->move(room);
2056 
2057  factory = get_factory(CLASS_USER);
2058  factory->sanction_object(world, SANCTION_READ|SANCTION_EXECUTE);
2059 
2060  for ( i = 31; i >= 0; i-- ) {
2061  factory = get_factory((1<<i));
2062  if ( objectp(factory) ) {
2063  factory->sanction_object(admin, SANCTION_EXECUTE);
2064  // give execute permissions to all factories for all steam users
2065  factory->sanction_object(steam, SANCTION_EXECUTE);
2066  if ( objectp(cont) )
2067  factory->move(cont);
2068  }
2069  }
2070 
2071  object steamroom = steam->query_attribute(GROUP_WORKROOM);
2072  MESSAGE("Placing home module");
2073  object home = get_module("home");
2074  if ( objectp(home) ) {
2075  home->set_attribute(OBJ_NAME, "home");
2076  home->move(room);
2077  catch(home->set_attribute(OBJ_URL, "/"));
2078  }
2079  MESSAGE("Placing WIKI module");
2080  object wiki = get_module("wiki");
2081  if ( objectp(wiki) ) {
2082  catch(wiki->set_attribute(OBJ_NAME, "wiki"));
2083  catch(wiki->set_attribute(OBJ_URL, "/wiki"));
2084  wiki->move(room);
2085  }
2086  MESSAGE("Placing Calendar module");
2087  object calendar = get_module("calendar");
2088  object cal = get_module("filepath:tree")->path_to_object("/calendar");
2089  if ( objectp(cal) )
2090  cal->move(room);
2091 
2092  if ( objectp(calendar) ) {
2093  calendar->set_attribute(OBJ_NAME, "calendar");
2094  catch(calendar->set_attribute(OBJ_URL, "/calendar"));
2095  calendar->move(room);
2096  }
2097  object spm = get_module("SPM");
2098  if ( objectp(spm) ) {
2099  spm->set_attribute(OBJ_NAME, "SPM");
2100  spm->move(room);
2101  }
2102 
2103  MESSAGE("Loading Objects finished in %d ms ...",f_get_time_millis()-tt);
2104 }
2105 
2106 object insert_backtrace(string btname, string btcontent)
2107 {
2108  nmaster->seteuid(USER("root"));
2109  object bt = get_factory(CLASS_DOCUMENT)->execute((["name": btname,
2110  "mimetype":"text/html"]));
2111  bt->set_content(btcontent);
2112  bt->set_attribute(DOC_MIME_TYPE, "text/html");
2113  bt->move(oBacktraces);
2114  bt->sanction_object(GROUP("Everyone"), SANCTION_READ);
2115  object temp = get_module("temp_objects");
2116  int bt_time = get_config("keep_backtraces");
2117  if ( bt_time <= 0 )
2118  bt_time = 60*60*24*7; // one week!
2119  if ( objectp(temp) )
2120  temp->add_temp_object(bt, time() + bt_time); // a week !
2121  return bt;
2122 }
2123 
2124 /**
2125  *
2126  *
2127  * @param
2128  * @return
2129  * @see
2130  */
2131 protected:
2132  void f_run_global_event(int event, int phase, object obj, mixed args)
2133 {
2134  mapping m;
2135 
2136  object logs = get_module("log");
2137  if ( objectp(logs) ) {
2138  mixed err;
2139  if ( phase == PHASE_NOTIFY ) {
2140  err = catch {
2141  logs->log("events", (event&EVENTS_MONITORED?LOG_LEVEL_DEBUG:LOG_LEVEL_INFO),
2142  Events.event_to_description(event, ({ obj })+args));
2143  };
2144  if ( err ) {
2145  FATAL("While logging event: %O\n\n%s\n%O", args,err[0],err[1]);
2146  }
2147  }
2148  else {
2149  err = catch(logs->log("events", LOG_LEVEL_DEBUG, "TRY " +
2150  Events.event_to_description(
2151  event, ({ obj }) + args)));
2152  if ( err ) {
2153  FATAL("While logging event: %s\n%O", err[0], err[1]);
2154  }
2155  }
2156  }
2157 
2158  if ( phase == PHASE_NOTIFY )
2159  m = mGlobalNotifyEvents;
2160  else
2161  m = mGlobalBlockEvents;
2162 
2163  if ( !arrayp(m[event]) )
2164  return;
2165  foreach(m[event], array cb_data) {
2166  if ( !arrayp(cb_data) ) continue;
2167  string fname = cb_data[0];
2168  object o = cb_data[1];
2169  if (!objectp(o)) continue;
2170 
2171  function f = cb_data[2];
2172  if ( !functionp(f) ) {
2173  if (o["find_function"])
2174  f = o->find_function(fname);
2175  else
2176  f = o[fname];
2177  }
2178 
2179  if ( functionp(f) && objectp(function_object(f)) ) {
2180  mixed err = catch( f(event, obj, @args) );
2181  if ( err ) {
2182  if ( phase == PHASE_NOTIFY )
2183  FATAL( "exception in global event:\n%O\n%O\n",
2184  err[0], err[1] );
2185  else
2186  throw(err);
2187  }
2188  }
2189  }
2190 }
2191 
2192 public:
2193 
2194 /**
2195  * run a global event
2196  *
2197  * @param int event - the event id
2198  * @param int phase - PHASE_BLOCK or PHASE_NOTIFY
2199  * @param object obj - the event object
2200  * @param mixed args - array of parameters
2201  *
2202  */
2203 void run_global_event(int event, int phase, object obj, mixed args)
2204 {
2205  return;
2206  f_run_global_event(event, phase, obj, args);
2207 }
2208 
2209 
2210 /**
2211  * subscribe to a global event
2212  *
2213  * @param int event - the event id
2214  * @param function callback - the function to call when the event occurs
2215  * @param int phase - PHASE_NOTIFY or PHASE_BLOCK
2216  *
2217  */
2218 void
2219 add_global_event(int event, function callback, int phase)
2220 {
2221  // FIXME! This should maybe be secured
2222  object obj;
2223  string fname;
2224 
2225  fname = function_name(callback);
2226  obj = function_object(callback);
2227  if ( !objectp(obj) )
2228  THROW("Fatal Error on add_global_event(), no object !", E_ERROR);
2229  if ( !functionp(obj->this) )
2230  THROW("Fatal Error on add_global_event(), invalid object !", E_ACCESS);
2231  if ( !objectp(obj) )
2232  THROW("Fatal Error on add_global_event(), no proxy !", E_ERROR);
2233 
2234  if ( phase == PHASE_NOTIFY ) {
2235  if ( !arrayp(mGlobalNotifyEvents[event]) )
2236  mGlobalNotifyEvents[event] = ({ });
2237  mGlobalNotifyEvents[event] += ({ ({ fname, obj, callback }) });
2238  }
2239  else {
2240  if ( !arrayp(mGlobalBlockEvents[event]) )
2241  mGlobalBlockEvents[event] = ({ });
2242  mGlobalBlockEvents[event] += ({ ({ fname, obj, callback }) });
2243  }
2244 }
2245 
2246 void
2247 remove_global_events()
2248 {
2249  array events;
2250  int event;
2251  array(function) notifiers;
2252 
2253  events = indices(mGlobalNotifyEvents);
2254  foreach ( events, event ) {
2255  notifiers = ({ });
2256  foreach ( mGlobalNotifyEvents[event], array cb_data ) {
2257  notifiers += ({ cb_data });
2258  }
2259  mGlobalNotifyEvents[event] = notifiers;
2260  }
2261  events = indices(mGlobalBlockEvents);
2262  foreach ( events, event ) {
2263  notifiers = ({ });
2264  foreach ( mGlobalBlockEvents[event], array cb_data ) {
2265  if ( cb_data[1] != CALLER )
2266  notifiers += ({ cb_data });
2267  }
2268  mGlobalBlockEvents[event] = notifiers;
2269  }
2270 }
2271 
2272 /**
2273  *
2274  *
2275  * @param
2276  * @return
2277  * @see
2278  */
2279 void
2280 shutdown(void|int reboot)
2281 {
2282  write_config_to_admin();
2283  object user = nmaster->this_user();
2284  if ( !_ADMIN->is_member(user) )
2285  THROW("Illegal try to shutdown server by "+
2286  (objectp(user)?user->get_identifier():"none")+"!",E_ACCESS);
2287  MESSAGE("Shutting down !\n");
2288  oDatabase->wait_for_db_lock();
2289  catch( rm( sandbox_path + "/server.restart" ) );
2290  if ( !reboot )
2291  _exit(1);
2292  _exit(0);
2293 }
2294 
2295 /**
2296  * Check whether a configuration value is permanently changeable from within
2297  * the server.
2298  * Configs that are set through the config file cannot be changed permanently
2299  * from within the server, only through the file (and, thus, a server restart).
2300  *
2301  * @param type the config to check
2302  * @return 1 if the value can be permanently changed, 0 if it can only be
2303  * changed until the next restart
2304  * @see set_config, delete_config
2305  */
2306 int is_config_changeable ( mixed type )
2307 {
2308  if ( zero_type(mConfigsFromFile[type]) )
2309  return 1;
2310  else
2311  return 0;
2312 }
2313 
2314 /**
2315  * Set a configuration value. Configs that are set through the config
2316  * file cannot be changed. (They can be temporarily set by using the
2317  * "force" param.)
2318  *
2319  * @param type the config to be changed
2320  * @param val the new value
2321  * @param force force setting the value (it will be overwritten by the
2322  * config file on the next server restart)
2323  * @return 1 if the value was permanently changed,
2324  * 0 if it could not be changed or could only be changed temporarily
2325  * (until the next restart)
2326  * @see query_config, get_config, delete_config
2327  */
2328 int set_config(mixed type, mixed val, void|bool force)
2329 {
2330  if ( !is_config_changeable( type ) && !force ) return 0;
2331  object user = nmaster->this_user();
2332  if ( objectp(user) && !_ADMIN->is_member(user) ) return 0;
2333 
2334  mConfigs[type] = val;
2335  write_config_to_admin();
2336  return is_config_changeable( type );
2337 }
2338 
2339 
2340 /**
2341  * Remove a configuration value. Configs that are set through the config
2342  * file cannot be changed. (They can be temporarily set by using the
2343  * "force" param.)
2344  *
2345  * @param type the config to be removed
2346  * @param force force removing the value (it will be overwritten by the
2347  * config file on the next server restart)
2348  * @return 1 if the value was removed, 0 if it could not be changed
2349  * or only be changed temporarily
2350  * @see query_config, get_config, set_config
2351  */
2352 int delete_config(mixed type, void|bool force)
2353 {
2354  if ( !is_config_changeable( type ) && !force ) return 0;
2355  object user = nmaster->this_user();
2356  if ( objectp(user) && !_ADMIN->is_member(user) ) return 0;
2357 
2358  m_delete(mConfigs, type);
2359  write_config_to_admin();
2360  return is_config_changeable( type );
2361 }
2362 
2363 /**
2364  *
2365  *
2366  * @param
2367  * @return
2368  * @see
2369  */
2370 void register_class(int class_id, object factory)
2371 {
2372  ASSERTINFO(MODULE_SECURITY->check_access(factory, CALLER, 0,
2373  ROLE_REGISTER_CLASSES, false),
2374  "CALLER must be able to register classes !");
2375  mClasses[class_id] = factory;
2376 }
2377 
2378 /**
2379  *
2380  *
2381  * @param
2382  * @return
2383  * @see
2384  */
2385 final object get_factory(int|object|string class_id)
2386 {
2387  int i, bits;
2388 
2389  if ( stringp(class_id) ) {
2390  foreach(values(mClasses), object factory) {
2391  if ( factory->get_class_name() == class_id )
2392  return factory;
2393  }
2394  return 0;
2395  }
2396  if ( objectp(class_id) ) {
2397  string class_name =
2398  master()->describe_program(object_program(class_id));
2399  // MESSAGE("getting factory for "+ class_name);
2400  if ( sscanf(class_name, "/DB:#%d.%*s", class_id) >= 1 )
2401  return oDatabase->find_object(class_id)->get_object();
2402  class_id = class_id->get_object_class();
2403  }
2404 
2405  for ( i = 31; i >= 0; i-- ) {
2406  bits = (1<<i);
2407  if ( bits <= class_id && bits & class_id ) {
2408  if ( objectp(mClasses[bits]) ) {
2409  return mClasses[bits]->get_object();
2410  }
2411  }
2412  }
2413  return null;
2414 }
2415 
2416 /**
2417  * Check if a given object is the factory of the object class of CALLER.
2418  *
2419  * @param obj - the object to check
2420  * @return true or false
2421  * @see get_factory
2422  * @see is_a_factory
2423  */
2424 bool is_factory(object obj)
2425 {
2426  object factory;
2427 
2428  factory = get_factory(CALLER->get_object_class());
2429  if ( objectp(factory) && factory == obj )
2430  return true;
2431  return false;
2432 }
2433 
2434 /**
2435  * Check if a given object is a factory. Factories are trusted objects.
2436  *
2437  * @param obj - the object that might be a factory
2438  * @return true or false
2439  * @see is_factory
2440  * @see get_factory
2441  */
2442 bool is_a_factory(object obj)
2443 {
2444  if ( !functionp(obj->this) )
2445  return false;
2446 
2447 }
2448 
2449 /**
2450  * get all classes and their factories.
2451  *
2452  * @return the mapping of all classes
2453  * @see is_factory
2454  * @see get_factory
2455  */
2456 final mapping get_classes()
2457 {
2458  return copy_value(mClasses);
2459 }
2460 
2461 array get_factories()
2462 {
2463  return copy_value(values(mClasses));
2464 }
2465 
2466 object get_caller(object obj, mixed bt)
2467 {
2468  int sz = sizeof(bt);
2469  object caller;
2470 
2471  sz -= 3;
2472  for ( ; sz >= 0; sz-- ) {
2473  if ( functionp(bt[sz][2]) ) {
2474  function f = bt[sz][2];
2475  caller = function_object(f);
2476  if ( caller != obj ) {
2477  return caller;
2478  }
2479  }
2480  }
2481  return 0;
2482 
2483 }
2484 
2485 
2486 void mail_password(object user)
2487 {
2488  string adminreply = "admin@" + query_config("machine") + (stringp(query_config("domain"))?"." +query_config("domain"):"");
2489  string pw = user->get_ticket(time() + 3600); // one hour
2490  int https = query_config(CFG_WEBPORT_HTTP);
2491  get_module("smtp")->send_mail(
2492  user->query_attribute(USER_EMAIL),
2493  "You Account Data for sTeam",
2494  "Use the following link to login to "+
2495  "the server\r\n and change your password "+
2496  "within an hour:\r\n"+
2497  "https://"+user->get_user_name()+":"+
2498  pw+"@"+
2499  query_config(CFG_WEBSERVER)+
2500  (https!=443?":"+query_config(CFG_WEBPORT_HTTP):"")+
2501  query_config(CFG_WEBMOUNT)+
2502  "register/forgot_change.html", adminreply, adminreply);
2503 }
2504 
2505 mixed steam_error(string msg, mixed ... args)
2506 {
2507  if ( sizeof(args) > 0 )
2508  msg = sprintf(msg, @args);
2509 
2510  throw(errors.SteamError(msg, backtrace()[1..]));
2511 }
2512 
2513 int f_get_time_millis () {
2514  array tod = System.gettimeofday();
2515  return tod[0]*1000 + tod[1]/1000;
2516 }
2517 
2518 int f_get_time_micros () {
2519  array tod = System.gettimeofday();
2520  return tod[0]*1000000 + tod[1];
2521 }
2522 
2523 int f_check_equal ( mixed a, mixed b )
2524 {
2525  if ( zero_type(a) && !zero_type(b) )
2526  return 0;
2527  else if ( !zero_type(a) && zero_type(b) )
2528  return 0;
2529  else if ( objectp(a) ) {
2530  if ( !objectp(b) )
2531  return 0;
2532  if ( functionp(a->get_object_id) && functionp(b->get_object_id) )
2533  return a->get_object_id() == b->get_object_id();
2534 
2535  if ( functionp(a->equal) && a->equal != a->__null ) return a->equal(b);
2536  return a == b;
2537  }
2538  if ( arrayp(a) && arrayp(b) ) {
2539  if ( sizeof(a) != sizeof(b) ) return 0;
2540  for ( int i=0; i<sizeof(a); i++ ) {
2541  if ( ! f_check_equal( a[i], b[i] ) )
2542  return 0;
2543  }
2544  return 1;
2545  }
2546  if ( mappingp(a) && mappingp(b) ) {
2547  if ( sizeof(a) != sizeof(b) ) return 0;
2548  foreach ( indices(a), mixed key )
2549  if ( !f_check_equal( a[key], b[key] ) ) return 0;
2550  return 1;
2551  }
2552  if ( multisetp(a) && multisetp(b) ) {
2553  if ( sizeof(a) != sizeof(b) ) return 0;
2554  foreach ( a, mixed val )
2555  if ( zero_type( b[val] ) ) return 0;
2556  return 1;
2557  }
2558  return a == b;
2559 }
2560 
2561 
2562 
2563 mixed steam_user_error(string msg, mixed ... args)
2564 {
2565  if ( sizeof(args) > 0 )
2566  msg = sprintf(msg, @args);
2567  throw(errors.SteamUserError(msg, backtrace()[1..]));
2568 }
2569 
2570 string get_server_name()
2571 {
2572  string domain=query_config("domain");
2573  if(stringp(domain) && sizeof(domain))
2574  return query_config("machine") + "." + domain;
2575  else
2576  return query_config("machine");
2577 
2578 }
2579 
2580 string ssl_redirect(string url)
2581 {
2582  string sname = get_server_name();
2583  int https = query_config("https_port");
2584  if ( https == 443 )
2585  return "https://" + sname + url;
2586  return "https://" + sname + ":" + https + url;
2587 }
2588 
2589 string get_server_ip()
2590 {
2591  if ( query_config("ip") )
2592  return query_config("ip");
2593  array result = System.gethostbyname(get_server_name());
2594  if ( arrayp(result) ) {
2595  if ( sizeof(result) >= 2 ) {
2596  if ( arrayp(result[1]) && sizeof(result[1]) > 0 )
2597  return result[1][0];
2598  }
2599  }
2600  return "127.0.0.1";
2601 }
2602 
2603 string get_server_url_presentation()
2604 {
2605  int port = query_config(CFG_WEBPORT_PRESENTATION);
2606 
2607  return "http://"+get_server_name()+(port==80?"":":"+port)+"/";
2608 }
2609 
2610 string get_server_url_administration()
2611 {
2612  int port = query_config(CFG_WEBPORT_ADMINISTRATION);
2613 
2614  return "https://"+get_server_name()+(port==443?"":":"+port)+"/";
2615 }
2616 
2617  object test_save;
2618 
2619 int check_shutdown_condition()
2620 {
2621  // check if shutdown is possible right now - check for active connections.
2622 
2623  return 1;
2624 }
2625 
2626 protected:
2627  void abs()
2628 {
2629  while ( 1 ) {
2630  mixed err;
2631  if ( err=catch(oDatabase->check_save_demon()) ) {
2632  FATAL("FATAL Error, rebooting !\n"+PRINT_BT(err));
2633  MESSAGE("ABS: Shutting down on fatal error !");
2634  oDatabase->wait_for_db_lock();
2635  _exit(2);
2636  }
2637 
2638  int reboot_hour = (int)get_config("reboot_hour");
2639  int reboot_day = (int)get_config("reboot_day");
2640  int reboot_memory = (int)get_config("max_memory");
2641  int reboot_connections = (int)get_config("max_connections");
2642 
2643  // find out the hour and only reboot hourly
2644  if ( time() - iLastReboot > 60*60 ) {
2645  mapping t = localtime(time());
2646  if ( !reboot_day || reboot_day == t->mday ) {
2647  if ( reboot_hour && reboot_hour == t->hour ) {
2648  MESSAGE("ABS: Shutting down on reboot time !");
2649  oDatabase->wait_for_db_lock();
2650  _exit(0);
2651  }
2652  }
2653  }
2654  int num_connections = sizeof(nmaster->get_users());
2655  mapping memory = debug_memory();
2656  int mem = 0;
2657  foreach(indices(memory), string idx)
2658  if ( search(idx, "_bytes") > 0 )
2659  mem += memory[idx];
2660 
2661 #if 0
2662  GROUP("admin")->mail("Server " + get_server_name() + " Status: <br>"+
2663  "Memory: "+ mem/(1024*1024)+ "M<br>"+
2664  "Connections: " + num_connections, "Status of " +
2665  get_server_name());
2666 #endif
2667  if ( reboot_connections > 0 && num_connections > reboot_connections &&
2668  check_shutdown_condition() )
2669  {
2670  MESSAGE("ABS: Shutting down due to number of connections !");
2671  oDatabase->wait_for_db_lock();
2672  _exit(0);
2673  }
2674  if ( reboot_memory > 0 && reboot_memory < mem && check_shutdown_condition())
2675  {
2676  MESSAGE("ABS: Shutting down due to memory usage !");
2677  oDatabase->wait_for_db_lock();
2678  _exit(0);
2679  }
2680  sleep(300); // 10 minutes
2681  }
2682 }
2683 
2684 public:
2685 
2686 array get_cmdline_email_addresses () {
2687  return aEmailAddresses;
2688 }
2689 
2690 {
2691  MESSAGE("\n*** Testing sTeam server (%O) ***\n", sTest);
2692 
2693  array testsuites = ({ });
2694  if ( sTest == "all" ) {
2695  foreach(values(mClasses), object factory)
2696  testsuites += ({ factory });
2697  foreach ( values(mModules), object module )
2698  testsuites += ({ module });
2699  }
2700  else {
2701  object obj = get_module( sTest );
2702  if ( !objectp(obj) )
2703  obj = get_factory( sTest );
2704  if ( !objectp(obj) && sTest == "database" )
2705  obj = oDatabase;
2706  if ( !objectp(obj) && sTest == "persistence" )
2707  obj = _Persistence;
2708  if ( !objectp(obj) && sTest == "webdav" )
2709  obj = nmaster->new("/net/webdav.pike", get_module("filepath:tree"), false);
2710  if ( !objectp(obj) && sTest == "Scripts" )
2711  obj = get_factory(CLASS_DOCUMENT)->execute((["name":"test.pike",]));
2712  if ( objectp(obj) )
2713  testsuites += ({ obj });
2714  }
2715  testsuites -= ({ 0 });
2716 
2717  MESSAGE("Testsuites are: %s", (testsuites->get_identifier())*", ");
2718  foreach ( testsuites, object suite )
2719  if ( !Test.start_test( suite ) )
2720  MESSAGE("Starting test in %O failed !", suite);
2721 
2722  //MESSAGE("\n*** All tests finished ***\n");
2723  nmaster->f_call_out( wait_for_tests, 10 );
2724  // exit(1); do not exit after test ...
2725 }
2726 
2727 void wait_for_tests () {
2728  if ( Test.all_tests_finished() ) {
2729  MESSAGE( Test.get_report() );
2730  exit( 1 );
2731  }
2732  mapping tests = Test.get_testsuites();
2733  int finished = 0;
2734  int pending = 0;
2735  foreach ( indices(tests), object suite ) {
2736  if ( Test.is_test_finished( suite ) ) finished++;
2737  else pending++;
2738  }
2739  MESSAGE( "*** %d tests finished, %d tests running: ***", finished, pending );
2740  mapping pending_tests = Test.get_pending_tests();
2741  foreach ( indices(pending_tests), object pending_suite ) {
2742  MESSAGE( "* %O : %s", pending_suite->get_identifier(),
2743  pending_tests[pending_suite] * "; " );
2744  }
2745  nmaster->f_call_out( wait_for_tests, 10 );
2746 }
2747 
2748 int get_object_class() { return 0; }
2749 int get_object_id() { return 0; }
2750 
2751 int status() { return PSTAT_SAVE_OK; }
2752 object get_creator() { return USER("root"); }
2753 
2754 function find_function(string fname) { return this_object()[fname]; }
2755 
2756 
2757 };