835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer.
af9bac2001-08-21Per Hedbor // Copyright © 1996 - 2001, Roxen IS.
835c6c2001-06-17Martin Nilsson // // The Roxen WebServer main program.
24c6c12000-02-20Martin Nilsson //
02ee412000-12-17Henrik Grubbström (Grubba) // Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others.
edc9af1998-07-11David Hedbor // ABS and suicide systems contributed freely by Francesco Chemolli
835c6c2001-06-17Martin Nilsson 
4eeda62003-09-15Martin Stjernholm constant cvs_version="$Id: roxen.pike,v 1.837 2003/09/15 15:14:26 mast Exp $";
5d3e442002-12-09Henrik Grubbström (Grubba)  //! @appears roxen //! //! The Roxen WebServer main program.
8fb5172000-05-27Per Hedbor  // The argument cache. Used by the image cache.
855c391999-11-29Per Hedbor ArgCache argcache;
1720541998-10-04Henrik Grubbström (Grubba)  // Some headerfiles
4dd97c1996-12-04Per Hedbor #define IN_ROXEN
8540e11997-06-14Francesco Chemolli #include <roxen.h> #include <config.h>
b1fca01996-11-12Per Hedbor #include <module.h> #include <variables.h>
753a831999-08-30Per Hedbor #include <stat.h>
9c19002001-02-27Per Hedbor #include <timers.h>
14179b1997-01-29Per Hedbor 
1720541998-10-04Henrik Grubbström (Grubba) // Inherits
753a831999-08-30Per Hedbor inherit "global_variables";
6897c92001-06-27Honza Petrous #ifdef SNMP_AGENT inherit "snmpagent"; #endif
b1fca01996-11-12Per Hedbor inherit "hosts"; inherit "disk_cache";
970e242000-09-12Per Hedbor // inherit "language";
2ff8461999-09-02Per Hedbor inherit "supports";
08eba62000-07-09Per Hedbor inherit "module_support";
970e242000-09-12Per Hedbor inherit "config_userdb";
7c92b91998-11-19Per Hedbor 
79c3a62001-04-25Jonas Wallden #ifdef THREADS // Used when running threaded to find out which thread is the backend thread. Thread.Thread backend_thread; #endif /* THREADS */
23414a2000-07-21Andreas Lange // --- Locale defines --- //<locale-token project="roxen_start"> LOC_S </locale-token> //<locale-token project="roxen_message"> LOC_M </locale-token> #define LOC_S(X,Y) _STR_LOCALE("roxen_start",X,Y) #define LOC_M(X,Y) _STR_LOCALE("roxen_message",X,Y) #define CALL_M(X,Y) _LOCALE_FUN("roxen_message",X,Y)
be788e2000-03-27Martin Nilsson // --- Debug defines ---
81f8af1999-12-20Martin Nilsson  #ifdef SSL3_DEBUG
3e6f6b2001-03-11Martin Nilsson # define SSL3_WERR(X) report_debug("SSL3: "+X+"\n")
81f8af1999-12-20Martin Nilsson #else
bfb4d41999-12-28Martin Nilsson # define SSL3_WERR(X)
81f8af1999-12-20Martin Nilsson #endif #ifdef THREAD_DEBUG
3e6f6b2001-03-11Martin Nilsson # define THREAD_WERR(X) report_debug("Thread: "+X+"\n")
81f8af1999-12-20Martin Nilsson #else
bfb4d41999-12-28Martin Nilsson # define THREAD_WERR(X)
81f8af1999-12-20Martin Nilsson #endif
60a9121999-10-10Henrik Grubbström (Grubba) 
8cc5fc2003-03-05Martin Stjernholm // Needed to get core dumps of seteuid()'ed processes on Linux. #if constant(System.dumpable) #define enable_coredumps(X) System.dumpable(X) #elif constant(system.dumpable) // Pike 7.2. #define enable_coredumps(X) system.dumpable(X) #else #define enable_coredumps(X) #endif
8514832000-11-27Per Hedbor #define DDUMP(X) sol( combine_path( __FILE__, "../../" + X ), dump ) static function sol = master()->set_on_load;
aa78722001-08-24Martin Stjernholm #ifdef TEST_EUID_CHANGE int test_euid_change; #endif
50b6972001-08-31Per Hedbor string md5( string what ) { return Gmp.mpz(Crypto.md5()->update( what )->digest(),256) ->digits(32); }
970e242000-09-12Per Hedbor string query_configuration_dir() { return configuration_dir; }
855c391999-11-29Per Hedbor 
5964251999-11-19Per Hedbor string filename( program|object o )
95b69e1999-09-05Per Hedbor {
5964251999-11-19Per Hedbor  if( objectp( o ) ) o = object_program( o );
2537c32000-02-14Per Hedbor  string fname = master()->program_name( o );
5964251999-11-19Per Hedbor  if( !fname ) fname = "Unknown Program"; return fname-(getcwd()+"/");
95b69e1999-09-05Per Hedbor }
8cbc5f2002-09-03Martin Stjernholm // Note that 2.5 is a nonexisting version. It's only used for the // cache static optimization for tags such as <if> and <emit> inside // <cache> since that optimization can give tricky incompatibilities // with 2.4.
07a09a2003-06-11Martin Stjernholm array(string) compat_levels = ({"2.1", "2.2", "2.4", "2.5", "3.3", "3.4", "3.5"});
b0a9ea2001-04-18Martin Stjernholm 
0f8b2f1999-03-27Henrik Grubbström (Grubba) #ifdef THREADS
6664122001-02-23Per Hedbor mapping(string:string) thread_names = ([]); string thread_name( object thread ) { string tn; if( thread_names[ tn=sprintf("%O",thread) ] ) return thread_names[tn]; return tn; } void name_thread( object thread, string name ) { catch(thread->set_name( name )); thread_names[ sprintf( "%O", thread ) ] = name; }
0f8b2f1999-03-27Henrik Grubbström (Grubba) // This mutex is used by Privs
855c391999-11-29Per Hedbor Thread.Mutex euid_egid_lock = Thread.Mutex();
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* THREADS */
c5e0961999-10-04Per Hedbor /*
8fb5172000-05-27Per Hedbor  * The privilege changer. Works like a mutex lock, but changes the UID/GID * while held. Blocks all threads. *
c5e0961999-10-04Per Hedbor  * Based on privs.pike,v 1.36. */
0f8b2f1999-03-27Henrik Grubbström (Grubba) int privs_level;
81f8af1999-12-20Martin Nilsson static class Privs
c5e0961999-10-04Per Hedbor {
0f8b2f1999-03-27Henrik Grubbström (Grubba) #if efun(seteuid) int saved_uid; int saved_gid; int new_uid; int new_gid;
7b798d2000-07-04Per Hedbor #define LOGP (variables && variables->audit && variables->audit->query())
0f8b2f1999-03-27Henrik Grubbström (Grubba)  #if constant(geteuid) && constant(getegid) && constant(seteuid) && constant(setegid) #define HAVE_EFFECTIVE_USER #endif static private string _getcwd() { if (catch{return(getcwd());}) { return("Unknown directory (no x-bit on current directory?)"); } } static private string dbt(array t) { if(!arrayp(t) || (sizeof(t)<2)) return ""; return (((t[0]||"Unknown program")-(_getcwd()+"/"))-"base_server/")+":"+t[1]+"\n"; } #ifdef THREADS
b84a161999-06-07Martin Stjernholm  static mixed mutex_key; // Only one thread may modify the euid/egid at a time.
d3a71c1999-04-20Martin Stjernholm  static object threads_disabled;
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* THREADS */ int p_level; void create(string reason, int|string|void uid, int|string|void gid) {
f198282001-09-27Henrik Grubbström (Grubba)  // No need for Privs if the uid has been changed permanently. if(getuid()) return;
0f8b2f1999-03-27Henrik Grubbström (Grubba) #ifdef PRIVS_DEBUG
3e6f6b2001-03-11Martin Nilsson  report_debug(sprintf("Privs(%O, %O, %O)\n" "privs_level: %O\n", reason, uid, gid, privs_level));
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* PRIVS_DEBUG */ #ifdef HAVE_EFFECTIVE_USER array u; #ifdef THREADS if (euid_egid_lock) { catch { mutex_key = euid_egid_lock->lock(); }; }
d3a71c1999-04-20Martin Stjernholm  threads_disabled = _disable_threads();
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* THREADS */ p_level = privs_level++; /* Needs to be here since root-priviliges may be needed to * use getpw{uid,nam}. */ saved_uid = geteuid(); saved_gid = getegid(); seteuid(0); /* A string of digits? */
c5e0961999-10-04Per Hedbor  if(stringp(uid) && (replace(uid,"0123456789"/"",({""})*10)==""))
0f8b2f1999-03-27Henrik Grubbström (Grubba)  uid = (int)uid;
c5e0961999-10-04Per Hedbor 
3a4b9a1999-12-27Martin Nilsson  if(stringp(gid) && (replace(gid, "0123456789"/"", ({"" })*10) == ""))
0f8b2f1999-03-27Henrik Grubbström (Grubba)  gid = (int)gid;
c5e0961999-10-04Per Hedbor  if(!stringp(uid))
0f8b2f1999-03-27Henrik Grubbström (Grubba)  u = getpwuid(uid);
81f8af1999-12-20Martin Nilsson  else
c5e0961999-10-04Per Hedbor  {
0f8b2f1999-03-27Henrik Grubbström (Grubba)  u = getpwnam(uid);
81f8af1999-12-20Martin Nilsson  if(u)
0f8b2f1999-03-27Henrik Grubbström (Grubba)  uid = u[2]; }
eae43a2001-08-13Per Hedbor 
81f8af1999-12-20Martin Nilsson  if(u && !gid)
c5e0961999-10-04Per Hedbor  gid = u[3];
81f8af1999-12-20Martin Nilsson  if(!u)
c5e0961999-10-04Per Hedbor  {
81f8af1999-12-20Martin Nilsson  if (uid && (uid != "root"))
c5e0961999-10-04Per Hedbor  {
81f8af1999-12-20Martin Nilsson  if (intp(uid) && (uid >= 60000))
c5e0961999-10-04Per Hedbor  {
b84a161999-06-07Martin Stjernholm  report_warning(sprintf("Privs: User %d is not in the password database.\n" "Assuming nobody.\n", uid));
0f8b2f1999-03-27Henrik Grubbström (Grubba)  // Nobody. gid = gid || uid; // Fake a gid also. u = ({ "fake-nobody", "x", uid, gid, "A real nobody", "/", "/sbin/sh" }); } else { error("Unknown user: "+uid+"\n"); } } else { u = ({ "root", "x", 0, gid, "The super-user", "/", "/sbin/sh" }); } } if(LOGP)
49cd282000-08-11Andreas Lange  report_notice(LOC_M(1, "Change to %s(%d):%d privs wanted (%s), from %s"),
23414a2000-07-21Andreas Lange  (string)u[0], (int)uid, (int)gid, (string)reason, (string)dbt(backtrace()[-2]));
0f8b2f1999-03-27Henrik Grubbström (Grubba) 
9963842001-09-26Martin Stjernholm  if (u[2]) {
0f8b2f1999-03-27Henrik Grubbström (Grubba) #if efun(cleargroups)
9963842001-09-26Martin Stjernholm  catch { cleargroups(); };
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* cleargroups */ #if efun(initgroups)
9963842001-09-26Martin Stjernholm  catch { initgroups(u[0], u[3]); };
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif
9963842001-09-26Martin Stjernholm  }
0f8b2f1999-03-27Henrik Grubbström (Grubba)  gid = gid || getgid(); int err = (int)setegid(new_gid = gid); if (err < 0) {
49cd282000-08-11Andreas Lange  report_warning(LOC_M(2, "Privs: WARNING: Failed to set the "
23414a2000-07-21Andreas Lange  "effective group id to %d!\n" "Check that your password database is correct " "for user %s(%d),\n and that your group " "database is correct.\n"), gid, (string)u[0], (int)uid);
0f8b2f1999-03-27Henrik Grubbström (Grubba)  int gid2 = gid; #ifdef HPUX_KLUDGE if (gid >= 60000) { /* HPUX has doesn't like groups higher than 60000, * but has assigned nobody to group 60001 (which isn't even * in /etc/group!). * * HPUX's libc also insists on filling numeric fields it doesn't like * with the value 60001! */
81f8af1999-12-20Martin Nilsson  report_debug("Privs: WARNING: Assuming nobody-group.\n"
0f8b2f1999-03-27Henrik Grubbström (Grubba)  "Trying some alternatives...\n"); // Assume we want the nobody group, and try a couple of alternatives foreach(({ 60001, 65534, -2 }), gid2) {
81f8af1999-12-20Martin Nilsson  report_debug("%d... ", gid2);
0f8b2f1999-03-27Henrik Grubbström (Grubba)  if (initgroups(u[0], gid2) >= 0) { if ((err = setegid(new_gid = gid2)) >= 0) {
81f8af1999-12-20Martin Nilsson  report_debug("Success!\n");
0f8b2f1999-03-27Henrik Grubbström (Grubba)  break; } } } } #endif /* HPUX_KLUDGE */ if (err < 0) {
81f8af1999-12-20Martin Nilsson  report_debug("Privs: Failed\n");
3b76dc2002-01-29Martin Stjernholm  error ("Failed to set EGID to %d\n", gid);
0f8b2f1999-03-27Henrik Grubbström (Grubba)  }
81f8af1999-12-20Martin Nilsson  report_debug("Privs: WARNING: Set egid to %d instead of %d.\n",
0f8b2f1999-03-27Henrik Grubbström (Grubba)  gid2, gid); gid = gid2; } if(getgid()!=gid) setgid(gid||getgid()); seteuid(new_uid = uid);
0217b82003-03-03Henrik Grubbström (Grubba)  enable_coredumps(1);
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* HAVE_EFFECTIVE_USER */ } void destroy() {
f198282001-09-27Henrik Grubbström (Grubba)  // No need for Privs if the uid has been changed permanently. if(getuid()) return;
0f8b2f1999-03-27Henrik Grubbström (Grubba) #ifdef PRIVS_DEBUG
3e6f6b2001-03-11Martin Nilsson  report_debug(sprintf("Privs->destroy()\n" "privs_level: %O\n", privs_level));
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* PRIVS_DEBUG */ #ifdef HAVE_EFFECTIVE_USER /* Check that we don't increase the privs level */ if (p_level >= privs_level) { report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" "in wrong order! Saved level:%d Current level:%d\n" "Occurs in:\n%s\n", saved_uid, saved_gid, new_uid, new_gid, p_level, privs_level, describe_backtrace(backtrace()))); return(0); } if (p_level != privs_level-1) { report_error(sprintf("Change back to uid#%d gid#%d from uid#%d gid#%d\n" "Skips privs level. Saved level:%d Current level:%d\n" "Occurs in:\n%s\n", saved_uid, saved_gid, new_uid, new_gid, p_level, privs_level, describe_backtrace(backtrace()))); } privs_level = p_level; if(LOGP) { catch { array bt = backtrace(); if (sizeof(bt) >= 2) {
c8ee712000-09-09Andreas Lange  report_notice(LOC_M(3,"Change back to uid#%d gid#%d, from %s")+"\n",
23414a2000-07-21Andreas Lange  saved_uid, saved_gid, dbt(bt[-2]));
0f8b2f1999-03-27Henrik Grubbström (Grubba)  } else {
49cd282000-08-11Andreas Lange  report_notice(LOC_M(4,"Change back to uid#%d gid#%d, "
c8ee712000-09-09Andreas Lange  "from backend")+"\n", saved_uid, saved_gid);
0f8b2f1999-03-27Henrik Grubbström (Grubba)  } }; }
81f8af1999-12-20Martin Nilsson #ifdef PRIVS_DEBUG
0f8b2f1999-03-27Henrik Grubbström (Grubba)  int uid = geteuid(); if (uid != new_uid) {
3e6f6b2001-03-11Martin Nilsson  report_debug("Privs: UID #%d differs from expected #%d\n" "%s\n", uid, new_uid, describe_backtrace(backtrace()));
0f8b2f1999-03-27Henrik Grubbström (Grubba)  } int gid = getegid(); if (gid != new_gid) {
3e6f6b2001-03-11Martin Nilsson  report_debug("Privs: GID #%d differs from expected #%d\n" "%s\n", gid, new_gid, describe_backtrace(backtrace()));
0f8b2f1999-03-27Henrik Grubbström (Grubba)  }
81f8af1999-12-20Martin Nilsson #endif /* PRIVS_DEBUG */
0f8b2f1999-03-27Henrik Grubbström (Grubba)  seteuid(0); array u = getpwuid(saved_uid); #if efun(cleargroups) catch { cleargroups(); }; #endif /* cleargroups */ if(u && (sizeof(u) > 3)) { catch { initgroups(u[0], u[3]); }; } setegid(saved_gid); seteuid(saved_uid);
0217b82003-03-03Henrik Grubbström (Grubba)  enable_coredumps(1);
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* HAVE_EFFECTIVE_USER */ }
dbfe9d2000-04-13Per Hedbor #else /* efun(seteuid) */ void create(string reason, int|string|void uid, int|string|void gid){}
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif /* efun(seteuid) */
c5e0961999-10-04Per Hedbor }
0f8b2f1999-03-27Henrik Grubbström (Grubba)  /* Used by read_config.pike, since there seems to be problems with * overloading otherwise. */
3e3bab2001-01-19Per Hedbor static Privs PRIVS(string r, int|string|void u, int|string|void g)
0f8b2f1999-03-27Henrik Grubbström (Grubba) { return Privs(r, u, g); }
8fb5172000-05-27Per Hedbor // font cache and loading. // // This will be changed to a list of server global modules, to make it // easier to implement new types of fonts (such as PPM color fonts, as // an example)
3e3bab2001-01-19Per Hedbor class Fonts { class Font { Image.Image write( string ... what ); array(int) text_extents( string ... what ); }; array available_font_versions(string name, int size); string describe_font_type(string n); Font get_font(string f, int size, int bold, int italic, string justification, float|int xspace, float|int yspace); Font resolve_font(string f, string|void justification);
8a12902001-11-14Henrik Grubbström (Grubba)  array(string) available_fonts(int(0..1)|void force_reload);
3e3bab2001-01-19Per Hedbor } Fonts fonts;
8fb5172000-05-27Per Hedbor 
d093992000-09-25Per Hedbor // Will replace Configuration after create() is run.
9a8a152000-09-25Per Hedbor program _configuration; /*set in create*/
b1fca01996-11-12Per Hedbor 
d093992000-09-25Per Hedbor array(Configuration) configurations = ({});
b1fca01996-11-12Per Hedbor 
9567c12001-02-23Martin Stjernholm private void stop_all_configurations() { configurations->unregister_urls(); #ifdef THREADS // Spend some time waiting out the handler threads before starting // to shut down the modules. hold_handler_threads(); release_handler_threads(3); #endif configurations->stop(1); }
1720541998-10-04Henrik Grubbström (Grubba) // Function that actually shuts down Roxen. (see low_shutdown).
1e5cc42001-03-17Martin Stjernholm private void really_low_shutdown(int exit_code)
a9d8111998-09-01Henrik Grubbström (Grubba) {
8fb5172000-05-27Per Hedbor  // Die nicely. Catch for paranoia reasons
3107101998-09-01Marcus Comstedt #ifdef THREADS
532b902001-08-13Martin Stjernholm  catch (stop_handler_threads());
3107101998-09-01Marcus Comstedt #endif /* THREADS */
532b902001-08-13Martin Stjernholm  destruct (cache); catch {
5e610a2003-05-26Martin Stjernholm #if 0
1e5cc42001-03-17Martin Stjernholm  if (exit_code) report_notice("Restarting Roxen.\n"); else report_notice("Shutting down Roxen.\n");
5e610a2003-05-26Martin Stjernholm #else report_notice("Restarting or shutting down Roxen.\n"); #endif
1e5cc42001-03-17Martin Stjernholm  };
4eeda62003-09-15Martin Stjernholm  roxenloader.real_exit( exit_code ); // Now we die...
a9d8111998-09-01Henrik Grubbström (Grubba) }
1e5cc42001-03-17Martin Stjernholm private int _recurse;
e83c432001-03-11Martin Nilsson 
a9d8111998-09-01Henrik Grubbström (Grubba) // Shutdown Roxen // exit_code = 0 True shutdown // exit_code = -1 Restart
1e5cc42001-03-17Martin Stjernholm private void low_shutdown(int exit_code)
a9d8111998-09-01Henrik Grubbström (Grubba) {
532b902001-08-13Martin Stjernholm  if(_recurse >= 4)
c5e0961999-10-04Per Hedbor  {
532b902001-08-13Martin Stjernholm  catch (report_notice("Exiting roxen (spurious signals received).\n")); catch (stop_all_configurations()); destruct(cache);
e83c432001-03-11Martin Nilsson #ifdef THREADS
532b902001-08-13Martin Stjernholm  catch (stop_handler_threads());
e83c432001-03-11Martin Nilsson #endif /* THREADS */
4eeda62003-09-15Martin Stjernholm  roxenloader.real_exit(exit_code);
e83c432001-03-11Martin Nilsson  }
ee8bc32001-08-21Martin Stjernholm  if (_recurse++) return;
e83c432001-03-11Martin Nilsson 
79b7c32001-09-13Honza Petrous  catch(stop_all_configurations());
6897c92001-06-27Honza Petrous #ifdef SNMP_AGENT
79b7c32001-09-13Honza Petrous  if(objectp(snmpagent)) { snmpagent->stop_trap();
6897c92001-06-27Honza Petrous  snmpagent->disable();
79b7c32001-09-13Honza Petrous  }
6897c92001-06-27Honza Petrous #endif
8fb5172000-05-27Per Hedbor  call_out(really_low_shutdown, 0.1, exit_code);
b1fca01996-11-12Per Hedbor }
a9d8111998-09-01Henrik Grubbström (Grubba) // Perhaps somewhat misnamed, really... This function will close all
c974c91999-06-27Per Hedbor // listen ports and then quit. The 'start' script should then start a // new copy of roxen automatically.
7de3f32001-02-02Fredrik Noring void restart(float|void i, void|int exit_code)
a6e4a12000-07-09Per Hedbor //! Restart roxen, if the start script is running
81f8af1999-12-20Martin Nilsson {
7de3f32001-02-02Fredrik Noring  call_out(low_shutdown, i, exit_code || -1);
81f8af1999-12-20Martin Nilsson }
8fb5172000-05-27Per Hedbor 
81f8af1999-12-20Martin Nilsson void shutdown(float|void i)
a6e4a12000-07-09Per Hedbor //! Shut down roxen
81f8af1999-12-20Martin Nilsson { call_out(low_shutdown, i, 0);
f4e1b71999-10-08Per Hedbor }
14179b1997-01-29Per Hedbor 
e83c432001-03-11Martin Nilsson void exit_when_done() { report_notice("Interrupt request received.\n"); low_shutdown(-1); }
4820e01999-07-27Henrik Grubbström (Grubba) /* * handle() stuff */
34fbbc1998-02-05Henrik Grubbström (Grubba) #ifdef THREADS
50b58b2001-01-31Per Hedbor // function handle = threaded_handle;
34fbbc1998-02-05Henrik Grubbström (Grubba) 
3e3bab2001-01-19Per Hedbor Thread do_thread_create(string id, function f, mixed ... args)
4f4bc11998-02-04Per Hedbor {
3e3bab2001-01-19Per Hedbor  Thread.Thread t = thread_create(f, @args);
6664122001-02-23Per Hedbor  name_thread( t, id );
4f4bc11998-02-04Per Hedbor  return t; }
676f582000-03-24Per Hedbor // Shamelessly uses facts about pikes preemting algorithm. // Might have to be fixed in the future. class Queue
a6e4a12000-07-09Per Hedbor //! Thread.Queue lookalike, which uses some archaic and less //! known features of the preempting algorithm in pike to optimize the //! read function.
03aa462002-10-01Martin Stjernholm // // If those archaic and less known features are to depend on the // interpreter lock in the while loop that waits on the condition // variable then it doesn't work since Pike always might yield before // a function call (specifically, the wait() call in the condition // variable). Thus a handler thread might wait even though there is a // request to process. However, the only effect is that that specific // request isn't serviced timely; when the next request comes in the // thread will be woken up and both requests will be handled. // Furthermore it's extremely rare in the first place since there // normally are several handler threads.
676f582000-03-24Per Hedbor { inherit Thread.Condition : r_cond; array buffer=allocate(8); int r_ptr, w_ptr; int size() { return w_ptr - r_ptr; } mixed read() {
8140af2002-10-27Martin Stjernholm  while(!(w_ptr - r_ptr)) {
291cf02002-10-23Martin Stjernholm  // Make a MutexKey for wait() to please 7.3. This will of course // not fix the race, but we ignore that. See the discussion
8140af2002-10-27Martin Stjernholm  // above. (Must have an extra ref to the mutex since the // MutexKey doesn't keep one.) Thread.Mutex m = Thread.Mutex(); r_cond::wait (m->lock()); }
676f582000-03-24Per Hedbor  mixed tmp = buffer[r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. return tmp; }
9567c12001-02-23Martin Stjernholm  mixed tryread() { if (!(w_ptr - r_ptr)) return ([])[0]; mixed tmp = buffer[r_ptr]; buffer[r_ptr++] = 0; // Throw away any references. return tmp; }
164a582003-04-14Martin Stjernholm  // Warning: This function isn't thread safe.
676f582000-03-24Per Hedbor  void write(mixed v) { if(w_ptr >= sizeof(buffer)) { buffer=buffer[r_ptr..]+allocate(8); w_ptr-=r_ptr; r_ptr=0; } buffer[w_ptr++]=v; r_cond::signal(); } }
4c3c532001-08-09Per Hedbor // // This is easier than when there are no threads. // // See the discussion below. :-) // // // But there is extra functionality below we really want, though, // // so let's use that one instead... // function async_sig_start( function f, int really ) // { // return lambda( mixed ... args ) { // thread_create( f, @args ); // }; // }
8fb5172000-05-27Per Hedbor local static Queue handle_queue = Queue();
a6e4a12000-07-09Per Hedbor //! Queue of things to handle. //! An entry consists of an array(function fp, array args)
1720541998-10-04Henrik Grubbström (Grubba) 
8fb5172000-05-27Per Hedbor local static int thread_reap_cnt;
9567c12001-02-23Martin Stjernholm //! Number of handler threads in the process of being stopped. static int threads_on_hold; //! Number of handler threads on hold.
14179b1997-01-29Per Hedbor 
8fb5172000-05-27Per Hedbor local static void handler_thread(int id)
a6e4a12000-07-09Per Hedbor //! The actual handling function. This functions read function and //! parameters from the queue, calls it, then reads another one. There //! is a lot of error handling to ensure that nothing serious happens if //! the handler function throws an error.
14179b1997-01-29Per Hedbor {
9567c12001-02-23Martin Stjernholm  THREAD_WERR("Handle thread ["+id+"] started"); mixed h, q;
aa78722001-08-24Martin Stjernholm  set_u_and_gid (1); #ifdef TEST_EUID_CHANGE if (test_euid_change) { Stdio.File f = Stdio.File(); if (f->open ("rootonly", "r") && f->read()) werror ("Handler thread %d can read rootonly\n", id); else werror ("Handler thread %d can't read rootonly\n", id); } #endif
9567c12001-02-23Martin Stjernholm  while(1)
4f4bc11998-02-04Per Hedbor  { if(q=catch {
45cae31998-03-06Henrik Grubbström (Grubba)  do {
4eb3192001-09-02Martin Stjernholm // if (!busy_threads) werror ("GC: %d\n", gc());
bfb4d41999-12-28Martin Nilsson  THREAD_WERR("Handle thread ["+id+"] waiting for next event");
9567c12001-02-23Martin Stjernholm  if(arrayp(h=handle_queue->read()) && h[0]) { THREAD_WERR(sprintf("Handle thread [%O] calling %O(%{%O, %})", id, h[0], h[1] / 1));
67f60e2000-07-09Martin Nilsson  set_locale();
76ae182001-02-23Martin Stjernholm  busy_threads++;
45cae31998-03-06Henrik Grubbström (Grubba)  h[0](@h[1]); h=0;
76ae182001-02-23Martin Stjernholm  busy_threads--;
3107101998-09-01Marcus Comstedt  } else if(!h) { // Roxen is shutting down.
3bc3a72001-02-22Martin Stjernholm  report_debug("Handle thread ["+id+"] stopped.\n");
3107101998-09-01Marcus Comstedt  thread_reap_cnt--;
23007b2000-05-28Martin Nilsson #ifdef NSERIOUS
10089c2000-05-17Martin Nilsson  if(!thread_reap_cnt) report_debug("+++ATH\n");
23007b2000-05-28Martin Nilsson #endif
3107101998-09-01Marcus Comstedt  return;
45cae31998-03-06Henrik Grubbström (Grubba)  }
9567c12001-02-23Martin Stjernholm #ifdef DEBUG else if (h != 1) error ("Unknown message in handle_queue: %O\n", h); #endif else { num_hold_messages--; THREAD_WERR("Handle thread [" + id + "] put on hold"); threads_on_hold++;
8140af2002-10-27Martin Stjernholm  if (Thread.Condition cond = hold_wakeup_cond) {
291cf02002-10-23Martin Stjernholm  // Make a MutexKey for wait() to please 7.3. This will of // course not fix the race, but we ignore that. See the
8140af2002-10-27Martin Stjernholm  // comment at the declaration of hold_wakeup_cond. (Must // have an extra ref to the mutex since the MutexKey // doesn't keep one.) Thread.Mutex m = Thread.Mutex(); cond->wait (m->lock()); }
9567c12001-02-23Martin Stjernholm  threads_on_hold--; THREAD_WERR("Handle thread [" + id + "] released"); }
45cae31998-03-06Henrik Grubbström (Grubba)  } while(1); }) {
774c9e2000-01-13Henrik Grubbström (Grubba)  if (h = catch {
434bac2000-07-14Andreas Lange  report_error(/*LOCALE("", "Uncaught error in handler thread: %s" "Client will not get any response from Roxen.\n"),*/ describe_backtrace(q));
774c9e2000-01-13Henrik Grubbström (Grubba)  if (q = catch {h = 0;}) {
52faaf2001-03-28Martin Stjernholm  report_error(LOC_M(5, "Uncaught error in handler thread: %sClient "
c8ee712000-09-09Andreas Lange  "will not get any response from Roxen.")+"\n",
67f60e2000-07-09Martin Nilsson  describe_backtrace(q));
8d31322001-06-06Martin Stjernholm  catch (q = 0);
774c9e2000-01-13Henrik Grubbström (Grubba)  } }) { catch { report_error("Error reporting error:\n"); report_error(sprintf("Raw error: %O\n", h[0])); report_error(sprintf("Original raw error: %O\n", q[0])); };
8d31322001-06-06Martin Stjernholm  catch (q = 0); catch (h = 0);
45cae31998-03-06Henrik Grubbström (Grubba)  } }
4f4bc11998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor }
68d2562001-01-31Per Hedbor  void handle(function f, mixed ... args)
3aaaa71997-06-12Wilhelm Köhler { handle_queue->write(({f, args })); }
14179b1997-01-29Per Hedbor int number_of_threads;
a6e4a12000-07-09Per Hedbor //! The number of handler threads to run.
76ae182001-02-23Martin Stjernholm  int busy_threads; //! The number of currently busy threads.
f526692000-05-16Henrik Grubbström (Grubba) static array(object) handler_threads = ({});
a6e4a12000-07-09Per Hedbor //! The handler threads, the list is kept for debug reasons.
8fb5172000-05-27Per Hedbor 
14179b1997-01-29Per Hedbor void start_handler_threads() {
8552d92001-01-13Martin Nilsson  if (query("numthreads") <= 1) {
7b798d2000-07-04Per Hedbor  set( "numthreads", 1 );
76ae182001-02-23Martin Stjernholm  report_warning (LOC_S(1, "Starting one thread to handle requests.")+"\n");
23414a2000-07-21Andreas Lange  } else {
c8ee712000-09-09Andreas Lange  report_notice (LOC_S(2, "Starting %d threads to handle requests.")+"\n",
8552d92001-01-13Martin Nilsson  query("numthreads") );
a60c4c1997-07-03Henrik Grubbström (Grubba)  }
f526692000-05-16Henrik Grubbström (Grubba)  array(object) new_threads = ({});
8552d92001-01-13Martin Nilsson  for(; number_of_threads < query("numthreads"); number_of_threads++)
f526692000-05-16Henrik Grubbström (Grubba)  new_threads += ({ do_thread_create( "Handle thread [" + number_of_threads + "]", handler_thread, number_of_threads ) }); handler_threads += new_threads;
14179b1997-01-29Per Hedbor }
06583f1997-09-03Per Hedbor 
9567c12001-02-23Martin Stjernholm static int num_hold_messages; static Thread.Condition hold_wakeup_cond = Thread.Condition();
03aa462002-10-01Martin Stjernholm // Note: There are races in the use of this condition variable, but // the only effect of that is that some handler thread might be // considered hung when it's actually waiting on hold_wakeup_cond, and // the hold/release handler threads function deal with hung threads // anyway. The outcome would only be that release_handler_threads // starts some extra handler thread unnecessarily.
9567c12001-02-23Martin Stjernholm  void hold_handler_threads() //! Tries to put all handler threads on hold, but gives up if it takes //! too long. { if (!hold_wakeup_cond) { THREAD_WERR("Ignoring request to hold handler threads during stop"); return; } int timeout=10; #if constant(_reset_dmalloc) // DMALLOC slows stuff down a bit... timeout *= 10; #endif /* constant(_reset_dmalloc) */ THREAD_WERR("Putting " + (number_of_threads - threads_on_hold) + " handler threads on hold, " + threads_on_hold + " on hold already"); for (int i = number_of_threads - threads_on_hold - num_hold_messages; i-- > 0;) { handle_queue->write (1); num_hold_messages++; } while (threads_on_hold < number_of_threads && timeout--) sleep (0.1); THREAD_WERR(threads_on_hold + " handler threads on hold, " + (number_of_threads - threads_on_hold) + " not responding"); } void release_handler_threads (int numthreads) //! Releases any handler threads put on hold. If necessary new threads //! are started to ensure that at least @[numthreads] threads are
1d32602001-08-13Martin Stjernholm //! responding. Threads that haven't arrived to the hold state since //! @[hold_handler_threads] are considered nonresponding.
9567c12001-02-23Martin Stjernholm { if (Thread.Condition cond = hold_wakeup_cond) { // Flush out any remaining hold messages from the queue. for (int i = handle_queue->size(); i && num_hold_messages; i--) { mixed task = handle_queue->tryread(); if (task == 1) num_hold_messages--; else handle_queue->write (task); } #ifdef DEBUG if (num_hold_messages) error ("num_hold_messages is bogus (%d).\n", num_hold_messages); #endif num_hold_messages = 0; int blocked_threads = number_of_threads - threads_on_hold; int threads_to_create = numthreads - threads_on_hold;
1d32602001-08-13Martin Stjernholm  THREAD_WERR("Releasing " + threads_on_hold + " threads on hold");
9567c12001-02-23Martin Stjernholm  cond->broadcast(); if (threads_to_create > 0) { array(object) new_threads = ({});
1d32602001-08-13Martin Stjernholm  for (int n = 0; n < threads_to_create; number_of_threads++, n++)
9567c12001-02-23Martin Stjernholm  new_threads += ({ do_thread_create( "Handle thread [" + number_of_threads + "]", handler_thread, number_of_threads ) }); handler_threads += new_threads; report_notice ("Created %d new handler threads to compensate " "for %d blocked ones.\n", threads_to_create, blocked_threads); } } else { THREAD_WERR("Ignoring request to release handler threads during stop"); return; } }
3bc3a72001-02-22Martin Stjernholm static Thread.MutexKey backend_block_lock;
3107101998-09-01Marcus Comstedt void stop_handler_threads()
3bc3a72001-02-22Martin Stjernholm //! Stop all the handler threads and the backend, but give up if it //! takes too long.
3107101998-09-01Marcus Comstedt {
95b69e1999-09-05Per Hedbor  int timeout=10;
f526692000-05-16Henrik Grubbström (Grubba) #if constant(_reset_dmalloc) // DMALLOC slows stuff down a bit... timeout *= 10; #endif /* constant(_reset_dmalloc) */
81f8af1999-12-20Martin Nilsson  report_debug("Stopping all request handler threads.\n");
9567c12001-02-23Martin Stjernholm  // Wake up any handler threads on hold, and ensure none gets on hold // after this. if (Thread.Condition cond = hold_wakeup_cond) { hold_wakeup_cond = 0; cond->broadcast(); }
3107101998-09-01Marcus Comstedt  while(number_of_threads>0) { number_of_threads--; handle_queue->write(0); thread_reap_cnt++; }
f526692000-05-16Henrik Grubbström (Grubba)  handler_threads = ({});
3bc3a72001-02-22Martin Stjernholm 
9567c12001-02-23Martin Stjernholm  if (this_thread() != backend_thread && !backend_block_lock) {
3bc3a72001-02-22Martin Stjernholm  thread_reap_cnt++; Thread.Mutex mutex = Thread.Mutex(); backend_block_lock = mutex->lock(); call_out (lambda () { thread_reap_cnt--; report_debug("Backend thread stopped.\n"); mutex->lock(); error("Backend stop failed.\n"); }, 0); }
3107101998-09-01Marcus Comstedt  while(thread_reap_cnt) {
f526692000-05-16Henrik Grubbström (Grubba)  sleep(0.1);
3107101998-09-01Marcus Comstedt  if(--timeout<=0) {
9567c12001-02-23Martin Stjernholm  report_debug("Giving up waiting on threads; " "%d threads blocked.\n", thread_reap_cnt); #ifdef DEBUG describe_all_threads(); #endif
3107101998-09-01Marcus Comstedt  return; } } }
50b58b2001-01-31Per Hedbor  #else // handle function used when THREADS is not enabled.
68d2562001-01-31Per Hedbor  void handle(function f, mixed ... args)
50b58b2001-01-31Per Hedbor { f(@args); } // function handle = unthreaded_handle;
4c3c532001-08-09Per Hedbor #endif /* THREADS */
50b58b2001-01-31Per Hedbor function async_sig_start( function f, int really ) { class SignalAsyncVerifier( function f ) { static int async_called; void really_call( array args ) { async_called = 0; f( @args ); } void call( mixed ... args ) {
6146222001-06-24Per Hedbor  if( async_called && async_called-time() )
50b58b2001-01-31Per Hedbor  {
40acb32002-04-15Martin Stjernholm  report_debug("Received signal %s\n", (string) signame( args[0] ) );
50b58b2001-01-31Per Hedbor  report_debug("\n\n" "Async calling failed for %O, calling synchronous\n", f); report_debug("Backtrace at time of hangup:\n%s\n", describe_backtrace( backtrace() )); f( @args ); return; }
20bd1e2001-06-24Per Hedbor  if( !async_called ) // Do not queue more than one call at a time. {
40acb32002-04-15Martin Stjernholm  report_debug("Received signal %s\n", (string) signame( args[0] ) );
20bd1e2001-06-24Per Hedbor  async_called=time(); call_out( really_call, 0, args ); }
50b58b2001-01-31Per Hedbor  } }; // call_out is not really useful here, since we probably want to run // the signal handler immediately, not whenever the backend thread // is available. /per // // Calling directly like this may however lead to recursive mutex // lock errors. The problem cannot be solved using lock(2) since the // internal structures may be in an inconsistent state from the // previous call, and waiting for the lock probably leads to a // deadlock. /noring // // But on the other hand, you are not very likely to have any mutex // locks in an unthreaded pike, since it's quite impossible. /per // // But still, the problems with inconsistent internal states are // there. The API:s for many (thread safe) objects are designed to // only allow one (1) caller at any given time. It's a bug if this // restriction can be circumvented using signals. I suggest that // Thread.Mutex takes care of this problem in non-threaded mode. // / noring // // Apparantly it already did that. :-) // // I also fixed SIGHUP to be somewhat more asynchronous. // // I also added a rather small amount of magic so that it is called // asynchronously the first time it is received, but repeated // signals are not called asynchronously unless the first signal // handler was actually called. // // Except for the SIGQUIT signal, which dumps a backtrace. It would // be an excercise in futility to call that one asynchronously. // // I hope that this will solve all your problems. /per if( really > 0 ) return lambda( mixed ... args ){ call_out( f, 0, @args ); }; if( really < 0 ) return f; return SignalAsyncVerifier( f )->call; }
81f8af1999-12-20Martin Nilsson 
76ae182001-02-23Martin Stjernholm #ifdef THREADS
164a582003-04-14Martin Stjernholm static Thread.Queue bg_queue = Thread.Queue();
76ae182001-02-23Martin Stjernholm static int bg_process_running;
8f63822001-02-27Martin Stjernholm // Use a time buffer to strike a balance if the server is busy and // always have at least one busy thread: The maximum waiting time in // that case is somewhere between bg_time_buffer_min and // bg_time_buffer_max. If there are only short periods of time between // the queue runs, the max waiting time will shrink towards the // minimum. static constant bg_time_buffer_max = 30; static constant bg_time_buffer_min = 0.5; static int bg_last_busy = 0;
76ae182001-02-23Martin Stjernholm static void bg_process_queue() { if (bg_process_running) return; // Relying on the interpreter lock here. bg_process_running = 1;
8f63822001-02-27Martin Stjernholm  int maxbeats = min (time() - bg_last_busy, bg_time_buffer_max) * (int) (1 / 0.04);
76ae182001-02-23Martin Stjernholm  if (mixed err = catch {
164a582003-04-14Martin Stjernholm  while (bg_queue->size()) { // Not a race here since only one thread is reading the queue. array task = bg_queue->read();
8f63822001-02-27Martin Stjernholm  // Wait a while if another thread is busy already. if (busy_threads > 1) { for (maxbeats = max (maxbeats, (int) (bg_time_buffer_min / 0.04)); busy_threads > 1 && maxbeats > 0; maxbeats--) // Pike implementation note: If 0.02 or smaller, we'll busy wait here. sleep (0.04); bg_last_busy = time(); }
54671c2001-11-12Martin Stjernholm #ifdef DEBUG_BACKGROUND_RUN
164a582003-04-14Martin Stjernholm  report_debug ("background_run run %s (%s)\n",
54671c2001-11-12Martin Stjernholm  functionp (task[0]) ?
aa94b42001-11-21Martin Stjernholm  sprintf ("%s: %s", Function.defined (task[0]), master()->describe_function (task[0])) : programp (task[0]) ? sprintf ("%s: %s", Program.defined (task[0]), master()->describe_program (task[0])) : sprintf ("%O", task[0]),
54671c2001-11-12Martin Stjernholm  map (task[1], lambda (mixed arg) {return sprintf ("%O", arg);}) * ", ");
76ae182001-02-23Martin Stjernholm #endif
8f63822001-02-27Martin Stjernholm  if (task[0]) // Ignore things that have become destructed. task[0] (@task[1]); if (busy_threads > 1) bg_last_busy = time();
76ae182001-02-23Martin Stjernholm  } }) { bg_process_running = 0; handle (bg_process_queue); throw (err); } bg_process_running = 0; } #endif void background_run (int|float delay, function func, mixed... args) //! Enqueue a task to run in the background in a way that makes as //! little impact as possible on the incoming requests. No matter how //! many tasks are queued to run in the background, only one is run at
8f63822001-02-27Martin Stjernholm //! a time. The tasks won't be starved, though.
76ae182001-02-23Martin Stjernholm //! //! The function @[func] will be enqueued after approximately @[delay] //! seconds, to be called with the rest of the arguments as its //! arguments. //! //! The function might be run in the backend thread, so it should //! never run for a considerable time. Instead do another call to //! @[background_run] to queue it up again after some work has been //! done, or use @[BackgroundProcess]. {
164a582003-04-14Martin Stjernholm #ifdef DEBUG_BACKGROUND_RUN report_debug ("background_run enqueue %s (%s)\n", functionp (func) ? sprintf ("%s: %s", Function.defined (func), master()->describe_function (func)) : programp (func) ? sprintf ("%s: %s", Program.defined (func), master()->describe_program (func)) : sprintf ("%O", func), map (args, lambda (mixed arg) {return sprintf ("%O", arg);}) * ", "); #endif
76ae182001-02-23Martin Stjernholm #ifdef THREADS if (!hold_wakeup_cond) // stop_handler_threads is running; ignore more work. return;
6e2e5d2003-01-20Martin Stjernholm  function enqueue = lambda()
76ae182001-02-23Martin Stjernholm  { bg_queue->write (({func, args}));
164a582003-04-14Martin Stjernholm  if (!bg_process_running)
76ae182001-02-23Martin Stjernholm  handle (bg_process_queue); }; if (delay) call_out (enqueue, delay); else enqueue();
6e2e5d2003-01-20Martin Stjernholm  enqueue = 0; // To avoid garbage.
76ae182001-02-23Martin Stjernholm #else // Can't do much better when we haven't got threads.. call_out (func, delay, @args); #endif } class BackgroundProcess //! A class to do a task repeatedly in the background, in a way that //! makes as little impact as possible on the incoming requests (using //! @[background_run]). { int|float period; int stopping = 0; static void repeat (function func, mixed args) { if (stopping) return; func (@args); background_run (period, repeat, func, args); }
d35d5f2001-06-13Jonas Wallden  //! @decl void set_period (int|float period); //! //! Changes the period to @[period] seconds between calls. void set_period (int|float period_) { period = period_; }
76ae182001-02-23Martin Stjernholm  //! @decl static void create (int|float period, function func, mixed... args); //! //! The function @[func] will be called with the following arguments //! after approximately @[period] seconds, and then kept being //! called with approximately that amount of time between each call. //! //! The repetition will stop if @[stop] is called, or if @[func] //! throws an error. static void create (int|float period_, function func, mixed... args) { period = period_; background_run (period, repeat, func, args); } void stop() //! Sets a flag to stop the succession of calls. { stopping = 1; }
54671c2001-11-12Martin Stjernholm  string _sprintf() {return "BackgroundProcess()";}
76ae182001-02-23Martin Stjernholm }
df36c61999-10-08Henrik Grubbström (Grubba) 
934b3f2000-02-04Per Hedbor mapping get_port_options( string key )
a6e4a12000-07-09Per Hedbor //! Get the options for the key 'key'. //! The intepretation of the options is protocol specific.
934b3f2000-02-04Per Hedbor { return (query( "port_options" )[ key ] || ([])); } void set_port_options( string key, mapping value )
a6e4a12000-07-09Per Hedbor //! Set the options for the key 'key'. //! The intepretation of the options is protocol specific.
934b3f2000-02-04Per Hedbor { mapping q = query("port_options"); q[ key ] = value; set( "port_options" , q ); save( ); }
9ec1002002-10-01Anders Johansson Configuration find_configuration_for_url(object url, void|string url_base) //! Tries to to determine if a request for the given url would end up //! in this server, and if so returns the corresponding configuration.
6dcf352002-09-20Anders Johansson { Configuration c;
9ec1002002-10-01Anders Johansson  string url_with_port = sprintf("%s://%s:%d%s", url->scheme, url->host, url->port, (sizeof(url->path)?url->path:"/"));
6dcf352002-09-20Anders Johansson  foreach( indices(urls), string u ) { mixed q = urls[u];
9ec1002002-10-01Anders Johansson  if( glob( u+"*", url_with_port ) ) {
3454852003-08-29Marcus Wellhardh  if( (c = q->port->find_configuration_for_url((string)url_with_port, 0, 1 )) )
6dcf352002-09-20Anders Johansson  {
9ec1002002-10-01Anders Johansson  if (search(u, "*") != -1 || search(u, "?") != -1) { // Something like "http://*:80/" // Base url if (url_base) { Standards.URI base = Standards.URI(url_base); if (url->host == base->host && url->port == base->port && url->scheme == base->scheme) break; } // Confguration location Standards.URI config = Standards.URI(c->get_url()); if (url->host == config->host && url->port == config->port && url->scheme == config->scheme) break; // Do not match c = 0; } else break;
6dcf352002-09-20Anders Johansson  }
9ec1002002-10-01Anders Johansson  }
6dcf352002-09-20Anders Johansson  } return c; }
4c04e52000-07-25Marcus Comstedt class InternalRequestID //! ID for internal requests that are not linked to any real request. { inherit RequestID;
7ead5a2001-08-15Per Hedbor  this_program set_path( string f ) { raw_url = Roxen.http_encode_string( f );
8cbc5f2002-09-03Martin Stjernholm 
7ead5a2001-08-15Per Hedbor  if( strlen( f ) > 5 ) { string a; switch( f[1] ) { case '<': if (sscanf(f, "/<%s>/%s", a, f)==2) { config = (multiset)(a/","); f = "/"+f; } // intentional fall-through case '(': if(strlen(f) && sscanf(f, "/(%s)/%s", a, f)==2) { prestate = (multiset)( a/","-({""}) ); f = "/"+f; } } } not_query = Roxen.simplify_path( scan_for_query( f ) ); return this_object(); } this_program set_url( string url ) {
8cbc5f2002-09-03Martin Stjernholm  sscanf( url, "%s://%s/%s", prot, misc->host, string path ); prot = upper_case( prot ); method = "GET"; raw = "GET /" + url + " HTTP/1.1\r\n\r\n"; raw_url = "/" + path;
7ead5a2001-08-15Per Hedbor  Configuration c; foreach( indices(urls), string u ) { mixed q = urls[u]; if( glob( u+"*", url ) ) if( (c = q->port->find_configuration_for_url(url, this_object(), 1 )) ) { conf = c; port_obj = q->port; break; } } if(!c) { // pass 2: Find a configuration with the 'default' flag set. foreach( configurations, c ) if( c->query( "default_server" ) ) { conf = c; break; } else c = 0; } if(!c) { // pass 3: No such luck. Let's allow default fallbacks. foreach( indices(urls), string u ) { mixed q = urls[u]; if( (c = q->port->find_configuration_for_url( url,this_object(), 1 )) ) { conf = c; port_obj = q->port; break; } } }
8cbc5f2002-09-03Martin Stjernholm  return set_path( raw_url );
7ead5a2001-08-15Per Hedbor  } static string _sprintf() { return sprintf("RequestID(conf=%O; not_query=%O)", conf, not_query ); }
b00cae2000-09-30Per Hedbor  static void create()
4c04e52000-07-25Marcus Comstedt  { client = ({ "Roxen" }); prot = "INTERNAL"; method = "GET";
99cb5e2001-02-05Per Hedbor  real_variables = ([]); variables = FakedVariables( real_variables );
4dd3e02001-08-17Martin Nilsson  root_id = this_object();
99cb5e2001-02-05Per Hedbor 
bb7b502003-01-24Anders Johansson  misc = ([ "pref_languages": PrefLanguages(), "cacheable": INITIAL_CACHEABLE, ]);
4c04e52000-07-25Marcus Comstedt  cookies = ([]); throttle = ([]);
db4ca92000-09-18Henrik Grubbström (Grubba)  client_var = ([]);
4c04e52000-07-25Marcus Comstedt  request_headers = ([]); prestate = (<>); config = (<>); supports = (<>); pragma = (<>); rest_query = ""; extra_extension = "";
2622752000-11-13Marcus Comstedt  remoteaddr = "127.0.0.1";
4c04e52000-07-25Marcus Comstedt  } }
49284b2000-02-17Per Hedbor 
c5e0961999-10-04Per Hedbor class Protocol
c091442000-08-15Johan Sundström //! The basic protocol. //! Implements reference handling, finding Configuration objects
a6e4a12000-07-09Per Hedbor //! for URLs, and the bind/accept handling.
c5e0961999-10-04Per Hedbor {
722c9e2001-11-07Henrik Grubbström (Grubba)  static Stdio.Port port_obj;
934b3f2000-02-04Per Hedbor  inherit "basic_defvar";
abc59a2000-08-23Per Hedbor  int bound;
c5e0961999-10-04Per Hedbor 
5773f62000-08-27Martin Stjernholm  string path;
c5e0961999-10-04Per Hedbor  constant name = "unknown"; constant supports_ipless = 0;
a6e4a12000-07-09Per Hedbor  //! If true, the protocol handles ip-less virtual hosting
ea65f72001-07-21Martin Stjernholm 
c5e0961999-10-04Per Hedbor  constant requesthandlerfile = "";
a6e4a12000-07-09Per Hedbor  //! Filename of a by-connection handling class. It is also possible //! to set the 'requesthandler' class member in a overloaded create //! function.
c091442000-08-15Johan Sundström 
c5e0961999-10-04Per Hedbor  constant default_port = 4711;
a6e4a12000-07-09Per Hedbor  //! If no port is specified in the URL, use this one
934b3f2000-02-04Per Hedbor 
c5e0961999-10-04Per Hedbor  int port;
a6e4a12000-07-09Per Hedbor  //! The currently bound portnumber
ea65f72001-07-21Martin Stjernholm 
c5e0961999-10-04Per Hedbor  string ip;
a6e4a12000-07-09Per Hedbor  //! The IP-number (0 for ANY) this port is bound to
ea65f72001-07-21Martin Stjernholm 
8fb5172000-05-27Per Hedbor  int refs;
a6e4a12000-07-09Per Hedbor  //! The number of references to this port
ea65f72001-07-21Martin Stjernholm 
c5e0961999-10-04Per Hedbor  program requesthandler;
a6e4a12000-07-09Per Hedbor  //! The per-connection request handling class
ea65f72001-07-21Martin Stjernholm 
ddefa61999-10-04Marcus Comstedt  array(string) sorted_urls = ({});
a6e4a12000-07-09Per Hedbor  //! Sorted by length, longest first
ea65f72001-07-21Martin Stjernholm 
ddefa61999-10-04Marcus Comstedt  mapping(string:mapping) urls = ([]);
a6e4a12000-07-09Per Hedbor  //! .. url -> ([ "conf":.., ... ])
c5e0961999-10-04Per Hedbor 
ea65f72001-07-21Martin Stjernholm  mapping(Configuration:mapping) conf_data = ([]); //! Maps the configuration objects to the data mappings in @[urls].
ddefa61999-10-04Marcus Comstedt  void ref(string name, mapping data)
a6e4a12000-07-09Per Hedbor  //! Add a ref for the URL 'name' with the data 'data'
c5e0961999-10-04Per Hedbor  {
ddefa61999-10-04Marcus Comstedt  if(urls[name])
8fb5172000-05-27Per Hedbor  {
ea65f72001-07-21Martin Stjernholm  conf_data[urls[name]->conf] = urls[name] = data;
8fb5172000-05-27Per Hedbor  return; // only ref once per URL }
5773f62000-08-27Martin Stjernholm  if (!refs) path = data->path; else if (path != (data->path || "")) path = 0;
c5e0961999-10-04Per Hedbor  refs++;
ea65f72001-07-21Martin Stjernholm  conf_data[data->conf] = urls[name] = data; sorted_urls = Array.sort_array(indices(urls),
24625e2000-08-28Per Hedbor  lambda(string a, string b) { return sizeof(a)<sizeof(b); });
c5e0961999-10-04Per Hedbor  }
b43d382002-04-19Anders Johansson  void unref(string _name) //! Remove a ref for the URL '_name'
c5e0961999-10-04Per Hedbor  {
bc5c2a2000-08-23Per Hedbor // if(!urls[name]) // only unref once // return;
5773f62000-08-27Martin Stjernholm 
b43d382002-04-19Anders Johansson  m_delete(conf_data, urls[_name]->conf); m_delete(urls, _name);
5773f62000-08-27Martin Stjernholm  if (!path && sizeof (Array.uniq (values (urls)->path)) == 1) path = values (urls)[0]->path;
b43d382002-04-19Anders Johansson  sorted_urls -= ({_name});
f74e252002-04-24Henrik Grubbström (Grubba) #ifdef PORT_DEBUG report_debug("Protocol(%s://%s:%d/)->unref(%O): refs:%d\n", name, ip, port, _name, refs); #endif /* PORT_DEBUG */
722c9e2001-11-07Henrik Grubbström (Grubba)  if( !--refs ) {
61d1c32001-11-27Henrik Grubbström (Grubba)  if (port_obj) { destruct(port_obj); }
722c9e2001-11-07Henrik Grubbström (Grubba)  port_obj = 0;
b43d382002-04-19Anders Johansson  m_delete(open_ports[name][ip], port); if(!sizeof(open_ports[name][ip])) m_delete(open_ports[name], ip); if(!sizeof(open_ports[name])) m_delete(open_ports, name);
722c9e2001-11-07Henrik Grubbström (Grubba)  //destruct( ); // Close the port. } } Stdio.File accept() { return port_obj->accept(); } string query_address() { return port_obj && port_obj->query_address();
c5e0961999-10-04Per Hedbor  }
1550aa2000-08-12Per Hedbor  mapping mu;
8514832000-11-27Per Hedbor  string rrhf;
8fb5172000-05-27Per Hedbor  static void got_connection()
c5e0961999-10-04Per Hedbor  {
3e3bab2001-01-19Per Hedbor  Stdio.File q = accept( );
8fb5172000-05-27Per Hedbor  if( q )
1550aa2000-08-12Per Hedbor  {
8514832000-11-27Per Hedbor  if( !requesthandler ) { requesthandler = (program)(rrhf); }
3e3bab2001-01-19Per Hedbor  Configuration c;
24625e2000-08-28Per Hedbor  if( refs < 2 )
1550aa2000-08-12Per Hedbor  { if(!mu) { mu = urls[sorted_urls[0]];
1f4a6c2000-08-28Per Hedbor  if(!(c=mu->conf)->inited ) c->enable_all_modules(); } else c = mu->conf;
1550aa2000-08-12Per Hedbor  } requesthandler( q, this_object(), c ); }
ddefa61999-10-04Marcus Comstedt  }
8fb5172000-05-27Per Hedbor  local function sp_fcfu;
4c87772000-08-11Per Hedbor 
7bc7382002-08-19Henrik Grubbström (Grubba) #define INIT(X) do{ \ mapping _=(X); \ string __=_->path; \ c=_->conf; \ if(__&&id->adjust_for_config_path) \ id->adjust_for_config_path(__); \ if(!c->inited) \ c->enable_all_modules(); \ } while(0)
347c722000-08-31Per Hedbor 
ea65f72001-07-21Martin Stjernholm #ifdef DEBUG_URL2CONF #define URL2CONF_MSG(X...) report_debug (X) #else #define URL2CONF_MSG(X...) #endif
9a8a152000-09-25Per Hedbor  Configuration find_configuration_for_url( string url, RequestID id, int|void no_default )
a6e4a12000-07-09Per Hedbor  //! Given a url and requestid, try to locate a suitable configuration //! (virtual site) for the request. //! This interface is not at all set in stone, and might change at //! any time.
ddefa61999-10-04Marcus Comstedt  {
9a8a152000-09-25Per Hedbor  Configuration c;
09d0382003-04-14Henrik Grubbström (Grubba)  if( sizeof( urls ) == 1 && !no_default)
4c87772000-08-11Per Hedbor  {
347c722000-08-31Per Hedbor  if(!mu) mu=urls[sorted_urls[0]]; INIT( mu );
ea65f72001-07-21Martin Stjernholm  URL2CONF_MSG ("%O %O cached: %O\n", this_object(), url, c);
4c87772000-08-11Per Hedbor  return c;
3d18ba2001-12-19Henrik Grubbström (Grubba)  } else if (!sizeof(sorted_urls)) { URL2CONF_MSG("%O %O No active URLS!\n", this_object(), url); return 0;
4c87772000-08-11Per Hedbor  } url = lower_case( url );
8fb5172000-05-27Per Hedbor  // The URLs are sorted from longest to shortest, so that short // urls (such as http://*/) will not match before more complete // ones (such as http://*.roxen.com/)
ddefa61999-10-04Marcus Comstedt  foreach( sorted_urls, string in ) { if( glob( in+"*", url ) ) {
347c722000-08-31Per Hedbor  INIT( urls[in] );
ea65f72001-07-21Martin Stjernholm  URL2CONF_MSG ("%O %O sorted_urls: %O\n", this_object(), url, c);
1d7d6d2000-02-16Per Hedbor  return c;
ddefa61999-10-04Marcus Comstedt  } }
8fb5172000-05-27Per Hedbor 
ea65f72001-07-21Martin Stjernholm  if( no_default ) { URL2CONF_MSG ("%O %O no default\n", this_object(), url);
8fb5172000-05-27Per Hedbor  return 0;
ea65f72001-07-21Martin Stjernholm  }
8fb5172000-05-27Per Hedbor  // No host matched, or no host header was included in the request.
347c722000-08-31Per Hedbor  // Is the URL in the '*' ports?
2ad7d02000-03-27Per Hedbor  mixed i;
347c722000-08-31Per Hedbor  if( !functionp(sp_fcfu) && ( i=open_ports[ name ][ 0 ][ port ] ) )
8fb5172000-05-27Per Hedbor  sp_fcfu = i->find_configuration_for_url;
347c722000-08-31Per Hedbor  if( sp_fcfu && (sp_fcfu != find_configuration_for_url)
ea65f72001-07-21Martin Stjernholm  && (i = sp_fcfu( url, id, 1 ))) { URL2CONF_MSG ("%O %O sp_fcfu: %O\n", this_object(), url, i);
7120b82000-03-27Per Hedbor  return i;
ea65f72001-07-21Martin Stjernholm  }
8fb5172000-05-27Per Hedbor  // No. We have to default to one of the other ports. // It might be that one of the servers is tagged as a default server. multiset choices = (< >);
d093992000-09-25Per Hedbor  foreach( configurations, Configuration c )
8fb5172000-05-27Per Hedbor  if( c->query( "default_server" ) ) choices |= (< c >); if( sizeof( choices ) )
7120b82000-03-27Per Hedbor  {
8fb5172000-05-27Per Hedbor  // First pick default servers bound to this port
347c722000-08-31Per Hedbor  foreach( values(urls), mapping cc ) if( choices[ cc->conf ] )
8fb5172000-05-27Per Hedbor  {
347c722000-08-31Per Hedbor  INIT( cc );
ea65f72001-07-21Martin Stjernholm  URL2CONF_MSG ("%O %O conf in choices: %O\n", this_object(), url, c);
347c722000-08-31Per Hedbor  return c;
8fb5172000-05-27Per Hedbor  }
347c722000-08-31Per Hedbor 
8fb5172000-05-27Per Hedbor  // if there is no such servers, pick the first default server
347c722000-08-31Per Hedbor  // available. FIXME: This makes it impossible to handle the // server path correctly.
1a231e2000-06-04Martin Nilsson  c = ((array)choices)[0]; if(!c->inited) c->enable_all_modules();
ea65f72001-07-21Martin Stjernholm  URL2CONF_MSG ("%O %O any in choices: %O\n", this_object(), url, c);
1a231e2000-06-04Martin Nilsson  return c;
7120b82000-03-27Per Hedbor  }
8fb5172000-05-27Per Hedbor  // if we end up here, there is no default port at all available // so grab the first configuration that is available at all.
347c722000-08-31Per Hedbor  INIT( urls[sorted_urls[0]] );
8fb5172000-05-27Per Hedbor  id->misc->defaulted=1;
ea65f72001-07-21Martin Stjernholm  URL2CONF_MSG ("%O %O first in sorted_urls: %O\n", this_object(), url, c);
8fb5172000-05-27Per Hedbor  return c;
c5e0961999-10-04Per Hedbor  }
934b3f2000-02-04Per Hedbor  mixed query_option( string x )
a6e4a12000-07-09Per Hedbor  //! Query the port-option 'x' for this port.
10bdc51999-10-08Henrik Grubbström (Grubba)  {
934b3f2000-02-04Per Hedbor  return query( x );
10bdc51999-10-08Henrik Grubbström (Grubba)  }
934b3f2000-02-04Per Hedbor  string get_key()
a6e4a12000-07-09Per Hedbor  //! Return he key used for this port (protocol:ip:portno)
df36c61999-10-08Henrik Grubbström (Grubba)  {
934b3f2000-02-04Per Hedbor  return name+":"+ip+":"+port; }
df36c61999-10-08Henrik Grubbström (Grubba) 
934b3f2000-02-04Per Hedbor  void save()
a6e4a12000-07-09Per Hedbor  //! Save all port options
934b3f2000-02-04Per Hedbor  { set_port_options( get_key(), mkmapping( indices(variables), map(indices(variables),query))); }
df36c61999-10-08Henrik Grubbström (Grubba) 
934b3f2000-02-04Per Hedbor  void restore()
a6e4a12000-07-09Per Hedbor  //! Restore all port options from saved values
934b3f2000-02-04Per Hedbor  { foreach( (array)get_port_options( get_key() ), array kv ) set( kv[0], kv[1] );
df36c61999-10-08Henrik Grubbström (Grubba)  }
8fb5172000-05-27Per Hedbor  static void create( int pn, string i )
a6e4a12000-07-09Per Hedbor  //! Constructor. Bind to the port 'pn' ip 'i'
c5e0961999-10-04Per Hedbor  {
934b3f2000-02-04Per Hedbor  port = pn; ip = i; restore();
8514832000-11-27Per Hedbor  if( file_stat( "../local/"+requesthandlerfile ) ) rrhf = "../local/"+requesthandlerfile; else rrhf = requesthandlerfile; DDUMP( rrhf ); #ifdef DEBUG
c5e0961999-10-04Per Hedbor  if( !requesthandler )
8514832000-11-27Per Hedbor  requesthandler = (program)(rrhf); #endif
722c9e2001-11-07Henrik Grubbström (Grubba)  port_obj = Stdio.Port(); if(!port_obj->bind( port, got_connection, ip ))
b6fb051999-11-02Per Hedbor  {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(6, "Failed to bind %s://%s:%d/ (%s)")+"\n",
23414a2000-07-21Andreas Lange  (string)name, (ip||"*"), (int)port, strerror( errno() ));
abc59a2000-08-23Per Hedbor  bound = 0; } else bound = 1;
c5e0961999-10-04Per Hedbor  }
81f8af1999-12-20Martin Nilsson 
8fb5172000-05-27Per Hedbor  static string _sprintf( )
b6fb051999-11-02Per Hedbor  {
c8ee712000-09-09Andreas Lange  return "Protocol("+name+"://"+ip+":"+port+")";
b6fb051999-11-02Per Hedbor  }
c5e0961999-10-04Per Hedbor }
4820e01999-07-27Henrik Grubbström (Grubba) 
b8fd5c2000-09-28Per Hedbor #if constant(SSL.sslfile)
df36c61999-10-08Henrik Grubbström (Grubba) class SSLProtocol
a6e4a12000-07-09Per Hedbor //! Base protocol for SSL ports. Exactly like Port, but uses SSL.
df36c61999-10-08Henrik Grubbström (Grubba) { inherit Protocol; // SSL context
3e3bab2001-01-19Per Hedbor  SSL.context ctx;
df36c61999-10-08Henrik Grubbström (Grubba)  class destruct_protected_sslfile {
3e3bab2001-01-19Per Hedbor  SSL.sslfile sslfile;
df36c61999-10-08Henrik Grubbström (Grubba)  mixed `[](string s) { return sslfile[s]; }
60a9121999-10-10Henrik Grubbström (Grubba)  mixed `[]=(string s, mixed val) { return sslfile[s] = val; }
df36c61999-10-08Henrik Grubbström (Grubba)  mixed `->(string s) { return sslfile[s]; }
60a9121999-10-10Henrik Grubbström (Grubba)  mixed `->=(string s, mixed val) { return sslfile[s] = val; }
df36c61999-10-08Henrik Grubbström (Grubba)  void destroy() {
2dd46b2000-03-24Per Hedbor  if (sslfile)
60a9121999-10-10Henrik Grubbström (Grubba)  sslfile->close();
df36c61999-10-08Henrik Grubbström (Grubba)  }
3e3bab2001-01-19Per Hedbor  void create(object q)
df36c61999-10-08Henrik Grubbström (Grubba)  { sslfile = SSL.sslfile(q, ctx); } }
3e3bab2001-01-19Per Hedbor  Stdio.File accept()
df36c61999-10-08Henrik Grubbström (Grubba)  {
3e3bab2001-01-19Per Hedbor  Stdio.File q = ::accept(); if (q) return [object(Stdio.File)](object)destruct_protected_sslfile(q);
df36c61999-10-08Henrik Grubbström (Grubba)  return 0; } void create(int pn, string i) { ctx = SSL.context();
6f72d42000-02-08Per Hedbor  set_up_ssl_variables( this_object() );
2dd46b2000-03-24Per Hedbor  port = pn; ip = i;
934b3f2000-02-04Per Hedbor 
2dd46b2000-03-24Per Hedbor  restore();
d0a4f82001-04-18Per Hedbor  object privs = Privs("Reading cert file"); int key_matches;
2dd46b2000-03-24Per Hedbor  string f, f2;
d0a4f82001-04-18Per Hedbor  ctx->certificates = ({});
df36c61999-10-08Henrik Grubbström (Grubba) 
4ed0992003-01-15Henrik Grubbström (Grubba)  foreach( map(query_option("ssl_cert_file"), String.trim_whites),
7a4e602002-11-25Anders Johansson  string cert_file )
df36c61999-10-08Henrik Grubbström (Grubba)  {
d0a4f82001-04-18Per Hedbor  if( catch{ f = lopen(cert_file, "r")->read(); } ) {
7a4e602002-11-25Anders Johansson  report_error(LOC_M(8,"SSL3: Reading cert-file '%s' failed!")+"\n", cert_file);
d0a4f82001-04-18Per Hedbor  return; }
df36c61999-10-08Henrik Grubbström (Grubba) 
d0a4f82001-04-18Per Hedbor  if( strlen(query_option("ssl_key_file")) && catch{ f2 = lopen(query_option("ssl_key_file"),"r")->read(); } )
2dd46b2000-03-24Per Hedbor  {
7a4e602002-11-25Anders Johansson  report_error(LOC_M(9, "SSL3: Reading key-file '%s' failed!")+"\n", query_option("ssl_key_file"));
df36c61999-10-08Henrik Grubbström (Grubba)  return; }
81f8af1999-12-20Martin Nilsson 
d0a4f82001-04-18Per Hedbor  object msg = Tools.PEM.pem_msg()->init( f ); object part = msg->parts["CERTIFICATE"] || msg->parts["X509 CERTIFICATE"]; string cert; if (!part || !(cert = part->decoded_body()))
2dd46b2000-03-24Per Hedbor  {
d0a4f82001-04-18Per Hedbor  report_error(LOC_M(10, "SSL3: No certificate found.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  return; }
d0a4f82001-04-18Per Hedbor  if( f2 ) msg = Tools.PEM.pem_msg()->init( f2 ); function r = Crypto.randomness.reasonably_random()->read;
81f8af1999-12-20Martin Nilsson 
d0a4f82001-04-18Per Hedbor  SSL3_WERR(sprintf("key file contains: %O", indices(msg->parts)));
81f8af1999-12-20Martin Nilsson 
d0a4f82001-04-18Per Hedbor  if (part = msg->parts["RSA PRIVATE KEY"])
df36c61999-10-08Henrik Grubbström (Grubba)  {
d0a4f82001-04-18Per Hedbor  string key;
81f8af1999-12-20Martin Nilsson 
d0a4f82001-04-18Per Hedbor  if (!(key = part->decoded_body())) { report_error(LOC_M(11,"SSL3: Private rsa key not valid")+" (PEM).\n"); return; }
df36c61999-10-08Henrik Grubbström (Grubba) 
d0a4f82001-04-18Per Hedbor  object rsa = Standards.PKCS.RSA.parse_private_key(key); if (!rsa) { report_error(LOC_M(11, "SSL3: Private rsa key not valid")+" (DER).\n"); return; } ctx->rsa = rsa; SSL3_WERR(sprintf("RSA key size: %d bits", rsa->rsa_size())); if (rsa->rsa_size() > 512) { /* Too large for export */ ctx->short_rsa = Crypto.rsa()->generate_key(512, r); // ctx->long_rsa = Crypto.rsa()->generate_key(rsa->rsa_size(), r); } ctx->rsa_mode();
3d18ba2001-12-19Henrik Grubbström (Grubba)  // FIXME: Support PKCS7
d0a4f82001-04-18Per Hedbor  object tbs = Tools.X509.decode_certificate (cert); if (!tbs) { report_error(LOC_M(13,"SSL3: Certificate not valid (DER).")+"\n"); return; }
1e7b6f2002-01-31Jens Larsson  if (tbs->public_key->rsa->public_key_equal (rsa)) {
d0a4f82001-04-18Per Hedbor  key_matches++;
1e7b6f2002-01-31Jens Larsson  // DWIM: Make sure the main cert comes first in the cert list. ctx->certificates = ({ cert }) + ctx->certificates; } else {
392a642002-03-21Henrik Grubbström (Grubba)  ctx->certificates += ({ cert });
d0a4f82001-04-18Per Hedbor  continue;
1e7b6f2002-01-31Jens Larsson  }
df36c61999-10-08Henrik Grubbström (Grubba)  }
d0a4f82001-04-18Per Hedbor  else if (part = msg->parts["DSA PRIVATE KEY"])
2dd46b2000-03-24Per Hedbor  {
d0a4f82001-04-18Per Hedbor  string key;
df36c61999-10-08Henrik Grubbström (Grubba) 
d0a4f82001-04-18Per Hedbor  if (!(key = part->decoded_body())) { report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (PEM).\n"); return; } object dsa = Standards.PKCS.DSA.parse_private_key(key); if (!dsa) { report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (DER).\n"); return; }
81f8af1999-12-20Martin Nilsson 
d0a4f82001-04-18Per Hedbor  SSL3_WERR(sprintf("Using DSA key.")); dsa->use_random(r); ctx->dsa = dsa; /* Use default DH parameters */
0217b82003-03-03Henrik Grubbström (Grubba) #if constant(SSL.Cipher) ctx->dh_params = SSL.Cipher.DHParameters(); #else
d0a4f82001-04-18Per Hedbor  ctx->dh_params = SSL.cipher.dh_parameters();
0217b82003-03-03Henrik Grubbström (Grubba) #endif
d0a4f82001-04-18Per Hedbor  ctx->dhe_dss_mode(); // FIXME: Add cert <-> private key check.
1e7b6f2002-01-31Jens Larsson  ctx->certificates = ({ cert }) + ctx->certificates;
d0a4f82001-04-18Per Hedbor  } else
2dd46b2000-03-24Per Hedbor  {
d0a4f82001-04-18Per Hedbor  report_error(LOC_M(17,"SSL3: No private key found.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  return; }
d0a4f82001-04-18Per Hedbor  ctx->random = r;
df36c61999-10-08Henrik Grubbström (Grubba)  }
d0a4f82001-04-18Per Hedbor  if( !key_matches )
2dd46b2000-03-24Per Hedbor  {
d0a4f82001-04-18Per Hedbor  report_error(LOC_M(14, "SSL3: Certificate and private key do not " "match.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  return; } #if EXPORT ctx->export_mode(); #endif ::create(pn, i); }
b8fd5c2000-09-28Per Hedbor 
b6fb051999-11-02Per Hedbor  string _sprintf( ) { return "SSLProtocol("+name+"://"+ip+":"+port+")"; }
dd7e661999-10-09Henrik Grubbström (Grubba) }
b8fd5c2000-09-28Per Hedbor #endif
df36c61999-10-08Henrik Grubbström (Grubba) 
0d0e952000-11-13Per Hedbor mapping(string:Protocol) build_protocols_mapping()
761baa1999-12-08Per Hedbor {
0d0e952000-11-13Per Hedbor  mapping protocols = ([]); int st = gethrtime();
56509c2001-06-30Martin Stjernholm  report_debug("Protocol handlers ... \b");
0d0e952000-11-13Per Hedbor #ifndef DEBUG class lazy_load( string prog, string name )
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor  program real; static void realize()
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor  if( catch {
8514832000-11-27Per Hedbor  DDUMP( prog );
0d0e952000-11-13Per Hedbor  real = (program)prog; protocols[name] = real; } ) report_error("Failed to compile protocol handler for "+name+"\n");
761baa1999-12-08Per Hedbor  }
0d0e952000-11-13Per Hedbor  Protocol `()(mixed ... x)
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor  if(!real) realize(); return real(@x); }; mixed `->( string x )
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor  if(!real) realize(); return predef::`->(real, x);
761baa1999-12-08Per Hedbor  }
0d0e952000-11-13Per Hedbor  }; #endif foreach( glob( "prot_*.pike", get_dir("protocols") ), string s )
6796aa1999-12-11Per Hedbor  {
0d0e952000-11-13Per Hedbor  sscanf( s, "prot_%s.pike", s ); #if !constant(SSL.sslfile) switch( s )
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor  case "https": case "ftps": continue;
761baa1999-12-08Per Hedbor  }
0d0e952000-11-13Per Hedbor #endif
56509c2001-06-30Martin Stjernholm  report_debug( "\b%s \b", s );
6f72d42000-02-08Per Hedbor 
0d0e952000-11-13Per Hedbor  catch
761baa1999-12-08Per Hedbor  {
0d0e952000-11-13Per Hedbor #ifdef DEBUG protocols[ s ] = (program)("protocols/prot_"+s+".pike"); #else protocols[ s ] = lazy_load( ("protocols/prot_"+s+".pike"),s );
761baa1999-12-08Per Hedbor #endif
0d0e952000-11-13Per Hedbor  };
934b3f2000-02-04Per Hedbor  }
0d0e952000-11-13Per Hedbor  foreach( glob("prot_*.pike",get_dir("../local/protocols")||({})), string s )
1d7d6d2000-02-16Per Hedbor  {
0d0e952000-11-13Per Hedbor  sscanf( s, "prot_%s.pike", s ); #if !constant(SSL.sslfile) switch( s )
1d7d6d2000-02-16Per Hedbor  {
0d0e952000-11-13Per Hedbor  case "https": case "ftps": continue;
df36c61999-10-08Henrik Grubbström (Grubba)  }
b8fd5c2000-09-28Per Hedbor #endif
56509c2001-06-30Martin Stjernholm  report_debug( "\b%s \b", s );
0d0e952000-11-13Per Hedbor  catch { #ifdef DEBUG protocols[ s ] = (program)("../local/protocols/prot_"+s+".pike");
761baa1999-12-08Per Hedbor #else
0d0e952000-11-13Per Hedbor  protocols[ s ] = lazy_load( ("../local/protocols/prot_"+s+".pike"),s );
761baa1999-12-08Per Hedbor #endif
0d0e952000-11-13Per Hedbor  }; }
56509c2001-06-30Martin Stjernholm  report_debug("\bDone [%.1fms]\n", (gethrtime()-st)/1000.0 );
0d0e952000-11-13Per Hedbor  return protocols; }
b00cae2000-09-30Per Hedbor 
479d8a1999-11-25Henrik Grubbström (Grubba) 
0d0e952000-11-13Per Hedbor mapping protocols;
c5e0961999-10-04Per Hedbor  mapping(string:mapping) open_ports = ([ ]);
5857892002-06-27Martin Stjernholm mapping(string:mapping(string:Configuration)) urls = ([]);
c5e0961999-10-04Per Hedbor array sorted_urls = ({});
dd7e661999-10-09Henrik Grubbström (Grubba) array(string) find_ips_for( string what )
b1fca01996-11-12Per Hedbor {
c5e0961999-10-04Per Hedbor  if( what == "*" || lower_case(what) == "any" )
b1fca01996-11-12Per Hedbor  return 0;
c5e0961999-10-04Per Hedbor 
15635b1999-10-10Per Hedbor  if( is_ip( what ) )
dd7e661999-10-09Henrik Grubbström (Grubba)  return ({ what });
c5e0961999-10-04Per Hedbor  array res = gethostbyname( what ); if( !res || !sizeof( res[1] ) )
c183182000-09-09Andreas Lange  report_error(LOC_M(46, "Cannot possibly bind to %O, that host is "
c8ee712000-09-09Andreas Lange  "unknown. Substituting with ANY")+"\n", what);
c5e0961999-10-04Per Hedbor  else
92898c1999-10-10Marcus Comstedt  return Array.uniq(res[1]);
c5e0961999-10-04Per Hedbor }
38c4892002-01-11Henrik Grubbström (Grubba) string normalize_url(string url)
c5e0961999-10-04Per Hedbor {
38c4892002-01-11Henrik Grubbström (Grubba)  if (!sizeof (url - " " - "\t")) return "";
8fb5172000-05-27Per Hedbor  url = lower_case( url );
21182a2001-10-05Per Hedbor  Standards.URI ui = Standards.URI(url); ui->fragment = 0; url = (string)ui;
bc5c2a2000-08-23Per Hedbor  url = replace( url, "/ANY", "/*" ); url = replace( url, "/any", "/*" );
21182a2001-10-05Per Hedbor 
38c4892002-01-11Henrik Grubbström (Grubba)  string host, path, protocol;
bc5c2a2000-08-23Per Hedbor  sscanf( url, "%[^:]://%[^/]%s", protocol, host, path );
38c4892002-01-11Henrik Grubbström (Grubba)  if (!host || !stringp(host)) return ""; if (!protocols[ protocol ]) return ""; int port;
bc5c2a2000-08-23Per Hedbor  sscanf(host, "%[^:]:%d", host, port); if( !port ) { port = protocols[ protocol ]->default_port; url = protocol+"://"+host+":"+port+path; }
38c4892002-01-11Henrik Grubbström (Grubba)  return url; } void unregister_url(string url, Configuration conf) { string ourl = url; if (!sizeof(url = normalize_url(url))) return;
bc5c2a2000-08-23Per Hedbor 
e7e6031999-11-05Per Hedbor  report_debug("Unregister "+url+"\n");
0aee222000-08-15Martin Stjernholm 
9c3c6c2001-11-09Henrik Grubbström (Grubba)  if (urls[url] && (!conf || !urls[url]->conf || (urls[url]->conf == conf)) && urls[url]->port)
c5e0961999-10-04Per Hedbor  {
ddefa61999-10-04Marcus Comstedt  urls[ url ]->port->unref(url);
c5e0961999-10-04Per Hedbor  m_delete( urls, url );
03aa492000-08-23Per Hedbor  m_delete( urls, ourl );
c5e0961999-10-04Per Hedbor  sort_urls();
b1fca01996-11-12Per Hedbor  } }
934b3f2000-02-04Per Hedbor array all_ports( ) {
f047522000-09-25Per Hedbor  return Array.uniq( values( urls )->port )-({0});
934b3f2000-02-04Per Hedbor } Protocol find_port( string name ) { foreach( all_ports(), Protocol p ) if( p->get_key() == name ) return p; }
c5e0961999-10-04Per Hedbor void sort_urls()
1668b21998-04-23Henrik Grubbström (Grubba) {
c5e0961999-10-04Per Hedbor  sorted_urls = indices( urls ); sort( map( map( sorted_urls, strlen ), `-), sorted_urls );
1668b21998-04-23Henrik Grubbström (Grubba) }
d093992000-09-25Per Hedbor int register_url( string url, Configuration conf )
c5e0961999-10-04Per Hedbor {
03aa492000-08-23Per Hedbor  string ourl = url;
8fb5172000-05-27Per Hedbor  url = lower_case( url );
1e3cd52000-02-02Martin Stjernholm  if (!sizeof (url - " " - "\t")) return 1;
c5e0961999-10-04Per Hedbor  string protocol; string host; int port; string path;
b1fca01996-11-12Per Hedbor 
21182a2001-10-05Per Hedbor  Standards.URI ui = Standards.URI(url); mapping opts = ([]); string a, b; foreach( (ui->fragment||"")/";", string x ) { sscanf( x, "%s=%s", a, b ); opts[a]=b; } ui->fragment = 0; url = (string)ui; if( (int)opts->nobind ) { report_warning(
55c0522001-11-07Henrik Grubbström (Grubba)  LOC_M(61,"Not binding the port %O, disabled in configuration")+"\n",
21182a2001-10-05Per Hedbor  url ); return 0; }
ac76fc1999-10-07Henrik Grubbström (Grubba)  url = replace( url, "/ANY", "/*" ); url = replace( url, "/any", "/*" );
c5e0961999-10-04Per Hedbor  sscanf( url, "%[^:]://%[^/]%s", protocol, host, path );
228fea2000-02-15Leif Stensson  if (!host || !stringp(host)) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(19,"Bad URL '%s' for server `%s'")+"\n",
23414a2000-07-21Andreas Lange  url, conf->query_name());
abc59a2000-08-23Per Hedbor  return 0;
228fea2000-02-15Leif Stensson  }
b8fd5c2000-09-28Per Hedbor  if( !protocols[ protocol ] ) {
55a8662000-11-20Per Hedbor  report_error(LOC_M(7,"The protocol '%s' is not available")+"\n", protocol);
b8fd5c2000-09-28Per Hedbor  return 0; }
228fea2000-02-15Leif Stensson  sscanf(host, "%[^:]:%d", host, port);
c5e0961999-10-04Per Hedbor 
38c5072000-02-28Per Hedbor  if( !port ) { port = protocols[ protocol ]->default_port; url = protocol+"://"+host+":"+port+path; }
c5e0961999-10-04Per Hedbor  if( strlen( path ) && ( path[-1] == '/' ) ) path = path[..strlen(path)-2]; if( !strlen( path ) ) path = 0;
f034ab2001-04-17Per Hedbor  if( urls[ url ] )
c5e0961999-10-04Per Hedbor  {
f034ab2001-04-17Per Hedbor  if( !urls[ url ]->port ) m_delete( urls, url ); else if( urls[ url ]->conf )
c5e0961999-10-04Per Hedbor  {
f034ab2001-04-17Per Hedbor  if( urls[ url ]->conf != conf ) { report_error(LOC_M(20, "Cannot register URL %s, " "already registered by %s!")+"\n", url, urls[ url ]->conf->name); return 0; } urls[ url ]->port->ref(url, urls[url]); return 1;
c5e0961999-10-04Per Hedbor  }
f034ab2001-04-17Per Hedbor  else urls[ url ]->port->unref( url );
c5e0961999-10-04Per Hedbor  }
dd7e661999-10-09Henrik Grubbström (Grubba)  Protocol prot;
c5e0961999-10-04Per Hedbor  if( !( prot = protocols[ protocol ] ) ) {
49cd282000-08-11Andreas Lange  report_error(LOC_M(21, "Cannot register URL %s, "
c8ee712000-09-09Andreas Lange  "cannot find the protocol %s!")+"\n",
23414a2000-07-21Andreas Lange  url, protocol);
c5e0961999-10-04Per Hedbor  return 0; } if( !port ) port = prot->default_port;
dd7e661999-10-09Henrik Grubbström (Grubba)  array(string) required_hosts;
8fb5172000-05-27Per Hedbor  if (is_ip(host))
f526692000-05-16Henrik Grubbström (Grubba)  required_hosts = ({ host });
21182a2001-10-05Per Hedbor  else if( is_ip(opts->ip) ) required_hosts = ({ opts->ip });
8fb5172000-05-27Per Hedbor  else required_hosts = find_ips_for( host );
b6fb051999-11-02Per Hedbor 
8fb5172000-05-27Per Hedbor  if (!required_hosts) required_hosts = ({ 0 }); // ANY
c5e0961999-10-04Per Hedbor  mapping m; if( !( m = open_ports[ protocol ] ) )
8fb5172000-05-27Per Hedbor  // always add 'ANY' (0) here, as an empty mapping, for speed reasons. // There is now no need to check for both open_ports[prot][0] and // open_ports[prot][0][port], we can go directly to the latter // test. m = open_ports[ protocol ] = ([ 0:([]) ]); if( sizeof( required_hosts - ({ 0 }) ) // not ANY && m[ 0 ][ port ] && prot->supports_ipless ) // The ANY port is already open for this port, and since this // protocol supports IP-less virtual hosting, there is no need to // open yet another port, that would mosts probably only conflict // with the ANY port anyway. (this is true on most OSes, it works // on Solaris, but fails on linux) required_hosts = ({ 0 });
ea65f72001-07-21Martin Stjernholm  urls[ url ] = ([ "conf":conf, "path":path, "hostname": host ]); urls[ ourl ] = urls[url] + ([]);
c5e0961999-10-04Per Hedbor  sorted_urls += ({ url });
dd7e661999-10-09Henrik Grubbström (Grubba)  int failures;
c5e0961999-10-04Per Hedbor 
38c5072000-02-28Per Hedbor  foreach(required_hosts, string required_host) {
dd7e661999-10-09Henrik Grubbström (Grubba)  if( m[ required_host ] && m[ required_host ][ port ] ) {
7c708f2000-02-05Henrik Grubbström (Grubba)  m[required_host][port]->ref(url, urls[url]);
38c5072000-02-28Per Hedbor 
7c708f2000-02-05Henrik Grubbström (Grubba)  urls[url]->port = m[required_host][port];
65d2002000-09-25Per Hedbor  urls[ourl]->port = m[required_host][port];
dd7e661999-10-09Henrik Grubbström (Grubba)  continue; /* No need to open a new port */ } if( !m[ required_host ] ) m[ required_host ] = ([ ]);
7c708f2000-02-05Henrik Grubbström (Grubba)  mixed err; if (err = catch { m[ required_host ][ port ] = prot( port, required_host ); }) { failures++; report_error(sprintf("Initializing the port handler for URL " + url + " failed!\n" "%s\n", describe_backtrace(err))); continue; }
abc59a2000-08-23Per Hedbor 
dd7e661999-10-09Henrik Grubbström (Grubba)  if( !( m[ required_host ][ port ] ) ) { m_delete( m[ required_host ], port ); failures++; if (required_host) {
2839e22001-10-19Henrik Grubbström (Grubba)  report_warning(LOC_M(22, "Binding the port on IP %s failed\n" " for URL %s!\n"), required_host, url);
dd7e661999-10-09Henrik Grubbström (Grubba)  } continue; }
abc59a2000-08-23Per Hedbor 
dd7e661999-10-09Henrik Grubbström (Grubba)  urls[ url ]->port = m[ required_host ][ port ];
03aa492000-08-23Per Hedbor  urls[ ourl ]->port = m[ required_host ][ port ];
dd7e661999-10-09Henrik Grubbström (Grubba)  m[ required_host ][ port ]->ref(url, urls[url]);
03aa492000-08-23Per Hedbor  if( !m[ required_host ][ port ]->bound ) failures++;
dd7e661999-10-09Henrik Grubbström (Grubba)  }
abc59a2000-08-23Per Hedbor  if (failures == sizeof(required_hosts)) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(23, "Cannot register URL %s!")+"\n", url);
c5e0961999-10-04Per Hedbor  return 0; } sort_urls();
8514832000-11-27Per Hedbor  report_notice(" "+LOC_S(3, "Registered %s for %s")+"\n",
434bac2000-07-14Andreas Lange  url, conf->query_name() );
c5e0961999-10-04Per Hedbor  return 1; }
d093992000-09-25Per Hedbor Configuration find_configuration( string name )
45dc022000-08-16Martin Stjernholm //! Searches for a configuration with a name or fullname like the //! given string. See also get_configuration().
b1fca01996-11-12Per Hedbor {
b613482001-01-31Per Hedbor  // Optimization, in case the exact name is given... if( Configuration o = get_configuration( name ) ) return o;
9699bf1999-10-11Per Hedbor  name = replace( lower_case( replace(name,"-"," ") )-" ", "/", "-" );
d093992000-09-25Per Hedbor  foreach( configurations, Configuration o )
9699bf1999-10-11Per Hedbor  {
0666741999-12-07Henrik Grubbström (Grubba)  if( (lower_case( replace( replace(o->name, "-"," ") - " " , "/", "-" ) ) == name) || (lower_case( replace( replace(o->query_name(), "-", " ") - " " , "/", "-" ) ) == name) )
c5e0961999-10-04Per Hedbor  return o;
9699bf1999-10-11Per Hedbor  }
0666741999-12-07Henrik Grubbström (Grubba)  return 0;
b1fca01996-11-12Per Hedbor }
c5e0961999-10-04Per Hedbor mapping(string:array(int)) error_log=([]);
ec2fe11997-06-09Henrik Grubbström (Grubba) 
a6ef1f2000-03-28Johan Sundström // Write a string to the administration interface error log and to stderr.
c60cae2000-02-02Johan Sundström void nwrite(string s, int|void perr, int|void errtype,
c821c31999-12-27Martin Stjernholm  object|void mod, object|void conf)
b1fca01996-11-12Per Hedbor {
add34e2000-08-17Per Hedbor  int log_time = time(1);
0fe69d2000-03-19Martin Nilsson  string reference = (mod ? Roxen.get_modname(mod) : conf && conf->name) || "";
c60cae2000-02-02Johan Sundström  string log_index = sprintf("%d,%s,%s", errtype, reference, s); if(!error_log[log_index]) error_log[log_index] = ({ log_time });
c5e0961999-10-04Per Hedbor  else
c60cae2000-02-02Johan Sundström  error_log[log_index] += ({ log_time });
e99fd41999-11-17Per Hedbor  if( mod ) {
040f882001-08-20Per Hedbor  if( mod->error_log ) mod->error_log[log_index] += ({ log_time });
e99fd41999-11-17Per Hedbor  } if( conf ) {
040f882001-08-20Per Hedbor  if( conf->error_log ) conf->error_log[log_index] += ({ log_time });
e99fd41999-11-17Per Hedbor  }
c60cae2000-02-02Johan Sundström  if(errtype >= 1)
81f8af1999-12-20Martin Nilsson  report_debug( s );
b1fca01996-11-12Per Hedbor } // When was Roxen started?
2ff8461999-09-02Per Hedbor int boot_time =time(); int start_time =time();
b1fca01996-11-12Per Hedbor  string version() {
10089c2000-05-17Martin Nilsson #ifndef NSERIOUS
8552d92001-01-13Martin Nilsson  return query("default_ident")?real_version:query("ident");
daff902000-02-23Martin Nilsson #else multiset choices=(<>);
8552d92001-01-13Martin Nilsson  string version=query("default_ident")?real_version:query("ident");
0893642000-02-25Martin Nilsson  return version+", "+ ({ "Applier of Templates", "Beautifier of Layouts", "Conqueror of Comdex", "Deliverer of Documents", "Enhancer of Abilities", "Freer of Webmasters", "Generator of Logs", "Helper of Users", "Interpreter of Scripts", "Juggler of Java-code", "Keeper of Databases", "Locator of Keywords", "Manipulator of Data", "Negatiator of Protocols", "Operator of Sites", "Provider of Contents", "Quintessence of Quality", "Responder to Connections", "Server of Webs", "Translator of Texts", "Unifier of Interfaces", "Valet of Visitors", "Watcher for Requests", "Xylem of Services", "Yielder of Information", "Zenith of Extensibility" })[random(26)];
daff902000-02-23Martin Nilsson #endif
b1fca01996-11-12Per Hedbor }
81f8af1999-12-20Martin Nilsson public void log(mapping file, RequestID request_id)
b1fca01996-11-12Per Hedbor {
81f8af1999-12-20Martin Nilsson  if(!request_id->conf) return;
14179b1997-01-29Per Hedbor  request_id->conf->log(file, request_id);
b1fca01996-11-12Per Hedbor }
3e3bab2001-01-19Per Hedbor #if ROXEN_COMPAT < 2.2
81f8af1999-12-20Martin Nilsson // Support for unique user id's
3e3bab2001-01-19Per Hedbor private Stdio.File current_user_id_file;
b1fca01996-11-12Per Hedbor private int current_user_id_number, current_user_id_file_last_mod; private void restore_current_user_id_number() { if(!current_user_id_file) current_user_id_file = open(configuration_dir + "LASTUSER~", "rwc"); if(!current_user_id_file) { call_out(restore_current_user_id_number, 2); return;
81f8af1999-12-20Martin Nilsson  }
b1fca01996-11-12Per Hedbor  current_user_id_number = (int)current_user_id_file->read(100); current_user_id_file_last_mod = current_user_id_file->stat()[2];
81f8af1999-12-20Martin Nilsson  report_debug("Restoring unique user ID information. (" + current_user_id_number + ")\n");
3235691998-03-26Per Hedbor #ifdef FD_DEBUG
434bac2000-07-14Andreas Lange  mark_fd(current_user_id_file->query_fd(), "Unique user ID logfile.\n");
3235691998-03-26Per Hedbor #endif
b1fca01996-11-12Per Hedbor } int increase_id() { if(!current_user_id_file) { restore_current_user_id_number();
add34e2000-08-17Per Hedbor  return current_user_id_number+time(1);
b1fca01996-11-12Per Hedbor  } if(current_user_id_file->stat()[2] != current_user_id_file_last_mod) restore_current_user_id_number(); current_user_id_number++; current_user_id_file->seek(0); current_user_id_file->write((string)current_user_id_number); current_user_id_file_last_mod = current_user_id_file->stat()[2]; return current_user_id_number; }
1138fd2001-05-29Martin Nilsson #endif // ROXEN_COMPAT < 2.2
b1fca01996-11-12Per Hedbor 
38b81e2001-01-06Martin Nilsson private int unique_id_counter; string create_unique_id() { object md5 = Crypto.md5();
0569fa2001-09-29Martin Nilsson  md5->update(query("server_salt") + start_time + "|" + (unique_id_counter++) + "|" + time(1));
38b81e2001-01-06Martin Nilsson  return Crypto.string_to_hex(md5->digest()); }
c8ee712000-09-09Andreas Lange 
dbfe9d2000-04-13Per Hedbor #ifndef __NT__
ee8b201998-07-13David Hedbor static int abs_started;
6ca8f61998-10-13Per Hedbor 
81f8af1999-12-20Martin Nilsson void restart_if_stuck (int force)
622e7a2001-12-04Martin Stjernholm //! @note //! Must be called from the backend thread due to Linux peculiarities.
6ca8f61998-10-13Per Hedbor {
ee8b201998-07-13David Hedbor  remove_call_out(restart_if_stuck);
8552d92001-01-13Martin Nilsson  if (!(query("abs_engage") || force))
edc9af1998-07-11David Hedbor  return;
81f8af1999-12-20Martin Nilsson  if(!abs_started)
6ca8f61998-10-13Per Hedbor  {
ee8b201998-07-13David Hedbor  abs_started = 1;
81f8af1999-12-20Martin Nilsson  report_debug("Anti-Block System Enabled.\n");
ee8b201998-07-13David Hedbor  } call_out (restart_if_stuck,10);
e0e50e2001-11-23Anders Johansson // werror("call_out_info %O\n", filter(call_out_info(), lambda(array a) { // return a[2] == restart_if_stuck; }));
6ca8f61998-10-13Per Hedbor  signal(signum("SIGALRM"), lambda( int n ) {
0bbe652001-11-22Henrik Grubbström (Grubba)  if (!query("abs_engage")) { abs_started = 0; report_debug("Anti-Block System Disabled.\n"); return; }
81f8af1999-12-20Martin Nilsson  report_debug("**** %s: ABS engaged!\n" "Trying to dump backlog: \n", ctime(time()) - "\n");
b0659e1999-07-20Henrik Grubbström (Grubba)  catch { // Catch for paranoia reasons. describe_all_threads(); };
81f8af1999-12-20Martin Nilsson  report_debug("**** %s: ABS exiting roxen!\n\n", ctime(time()));
6ca8f61998-10-13Per Hedbor  _exit(1); // It might now quit correctly otherwise, if it's // locked up });
e0e50e2001-11-23Anders Johansson  int t = alarm (60*query("abs_timeout")+20); // werror("alarm: %d seconds left, set to %d\n", t, 60*query("abs_timeout")+20);
edc9af1998-07-11David Hedbor }
dbfe9d2000-04-13Per Hedbor #endif
edc9af1998-07-11David Hedbor 
4ed0992003-01-15Henrik Grubbström (Grubba) function(string:Sql.Sql) dbm_cached_get;
753a831999-08-30Per Hedbor 
2ff8461999-09-02Per Hedbor class ImageCache
93dd0e2000-07-27Johan Sundström //! The image cache handles the behind-the-scenes caching and //! regeneration features of graphics generated on the fly. Besides //! being a cache, however, it serves a wide variety of other //! interesting image conversion/manipulation functions as well.
2ff8461999-09-02Per Hedbor {
0b41772001-09-06Per Hedbor #define QUERY(X,Y...) get_db()->query(X,Y)
2ff8461999-09-02Per Hedbor  string name; string dir; function draw_function;
e7ac492003-04-06Anders Johansson  mapping(string:array(mapping|int)) meta_cache = ([]);
0f8b2f1999-03-27Henrik Grubbström (Grubba) 
23b7ec2001-08-27Per Hedbor  string documentation(void|string tag_n_args) {
29fac12001-01-04Martin Nilsson  string doc = Stdio.read_file("base_server/image_cache.xml"); if(!doc) return "";
6f76f12000-06-01Martin Nilsson  if(!tag_n_args)
4a45f12001-03-13Martin Nilsson  return Parser.HTML()->add_container("ex", "")-> add_quote_tag("!--","","--")->finish(doc)->read();
6f76f12000-06-01Martin Nilsson  return replace(doc, "###", tag_n_args); }
34d3fa1999-04-22Per Hedbor 
2ff8461999-09-02Per Hedbor  static mapping meta_cache_insert( string i, mapping what ) {
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("MD insert for %O: %O\n", i, what ); #endif if( sizeof( meta_cache ) > 1000 )
e7ac492003-04-06Anders Johansson  sync_meta(); if( what ) { meta_cache[i] = ({ what, 0 }); return what; }
4afcd42001-08-09Per Hedbor  else m_delete( meta_cache, i ); return 0;
2ff8461999-09-02Per Hedbor  }
81f8af1999-12-20Martin Nilsson 
2ff8461999-09-02Per Hedbor  static mixed frommapp( mapping what )
753a831999-08-30Per Hedbor  {
482ac32001-03-16Per Hedbor  if( !what ) error( "Got invalid argcache-entry\n" );
ede1352000-08-12Martin Stjernholm  if( !zero_type(what[""]) ) return what[""];
2ff8461999-09-02Per Hedbor  return what;
753a831999-08-30Per Hedbor  }
6a613a2002-06-17Anders Johansson  static void|mapping draw( string name, RequestID id )
2ff8461999-09-02Per Hedbor  {
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("draw %O\n", name ); #endif
4afcd42001-08-09Per Hedbor  mixed args = Array.map( Array.map( name/"$", argcache->lookup,
482ac32001-03-16Per Hedbor  id->client ), frommapp);
aea1652001-06-26Per Hedbor 
2ff8461999-09-02Per Hedbor  mapping meta; string data;
6f03962001-02-09Per Hedbor  array guides;
2ff8461999-09-02Per Hedbor  mixed reply = draw_function( @copy_value(args), id );
b1fca01996-11-12Per Hedbor 
aea1652001-06-26Per Hedbor  if( !reply ) return;
2ff8461999-09-02Per Hedbor  if( arrayp( args ) ) args = args[0];
b1fca01996-11-12Per Hedbor 
37ecea2000-02-21Per Hedbor  if( arrayp( reply ) ) // layers.
6f03962001-02-09Per Hedbor  { guides = reply->get_misc_value( "image_guides" )-({}); if( sizeof( guides ) ) guides = guides[0];
37ecea2000-02-21Per Hedbor  reply = Image.lay( reply );
6f03962001-02-09Per Hedbor  }
37ecea2000-02-21Per Hedbor  if( objectp( reply ) && reply->image ) // layer. {
6f03962001-02-09Per Hedbor  if( !guides ) guides = reply->get_misc_value( "image_guides" );
37ecea2000-02-21Per Hedbor  reply = ([ "img":reply->image(), "alpha":reply->alpha(), ]); }
b1fca01996-11-12Per Hedbor 
2ff8461999-09-02Per Hedbor  if( objectp( reply ) || (mappingp(reply) && reply->img) )
b1fca01996-11-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  int quant = (int)args->quant; string format = lower_case(args->format || "gif"); string dither = args->dither; Image.Colortable ct;
e9d7c51999-11-02Per Hedbor  Image.Color.Color bgcolor;
3e3bab2001-01-19Per Hedbor  Image.Image alpha;
81f8af1999-12-20Martin Nilsson  int true_alpha;
b1fca01996-11-12Per Hedbor 
2ff8461999-09-02Per Hedbor  if( args->fs || dither == "fs" ) dither = "floyd_steinberg";
b1fca01996-11-12Per Hedbor 
6f03962001-02-09Per Hedbor  if( dither == "random" )
2ff8461999-09-02Per Hedbor  dither = "random_dither";
b1fca01996-11-12Per Hedbor 
81f8af1999-12-20Martin Nilsson  if( format == "jpg" )
2ff8461999-09-02Per Hedbor  format = "jpeg";
b1fca01996-11-12Per Hedbor 
112b1c2000-02-02Per Hedbor  if( dither ) dither = replace( dither, "-", "_" );
2ff8461999-09-02Per Hedbor  if(mappingp(reply)) { alpha = reply->alpha; reply = reply->img;
93ebdd1999-03-11Martin Stjernholm  }
81f8af1999-12-20Martin Nilsson 
c3a53d1999-06-25Per Hedbor  if( args["true-alpha"] ) true_alpha = 1;
e7e6031999-11-05Per Hedbor  if( args["background"] || args["background-color"])
6f03962001-02-09Per Hedbor  bgcolor = Image.Color( args["background"]||args["background-color"] );
e9d7c51999-11-02Per Hedbor 
b79be21999-06-11Per Hedbor  if( args["opaque-value"] ) {
6f03962001-02-09Per Hedbor  if( !bgcolor ) true_alpha = 1;
b79be21999-06-11Per Hedbor  int ov = (int)(((float)args["opaque-value"])*2.55); if( ov < 0 ) ov = 0; else if( ov > 255 ) ov = 255; if( alpha )
3564842000-09-15Per Hedbor  alpha *= ov;
b79be21999-06-11Per Hedbor  else
2537c32000-02-14Per Hedbor  alpha = Image.Image( reply->xsize(), reply->ysize(), ov,ov,ov );
b79be21999-06-11Per Hedbor  }
c526921999-05-18Per Hedbor 
91a1562000-04-11Per Hedbor  if( args->gamma ) reply = reply->gamma( (float)args->gamma ); if( bgcolor && alpha && !true_alpha ) { reply = Image.Image( reply->xsize(), reply->ysize(), bgcolor ) ->paste_mask( reply, alpha ); alpha = alpha->threshold( 4 ); }
6f03962001-02-09Per Hedbor  int x0, y0, x1=reply->xsize(), y1=reply->ysize(), xc, yc;
3cbd0d2000-01-31Per Hedbor  if( args["x-offset"] || args["xoffset"] ) x0 = (int)(args["x-offset"]||args["xoffset"]); if( args["y-offset"] || args["yoffset"] ) y0 = (int)(args["y-offset"]||args["yoffset"]);
6f03962001-02-09Per Hedbor  if( args["width"] || args["x-size"] ) x1 = (int)(args["x-size"]||args["width"]); if( args["height"] || args["y-size"] ) y1 = (int)(args["y-size"]||args["height"]);
3cbd0d2000-01-31Per Hedbor 
6f03962001-02-09Per Hedbor  array xguides, yguides;
83f54a2003-01-16Martin Stjernholm  function sort_guides = lambda()
6f03962001-02-09Per Hedbor  { xguides = ({}); yguides = ({}); if( guides ) { foreach( guides, object g ) if( g->pos > 0 ) if( g->vertical ) { if( g->pos < reply->xsize() ) xguides += ({ g->pos }); } else if( g->pos < reply->ysize() ) yguides += ({ g->pos }); sort( xguides ); sort( yguides ); } };
3cbd0d2000-01-31Per Hedbor  if( args->crop ) {
6f03962001-02-09Per Hedbor  int gx=1, gy=1, gx2, gy2; if( sscanf( args["guides-index"]||"", "%d,%d", gx, gy ) == 2 ) { gx2 = gx+1; gy2 = gy+1; sscanf( args["guides-index"]||"", "%d,%d-%d,%d", gx, gy, gx2, gy2 ); } /* No, I did not forget the break statements. */ switch( args->crop ) { case "guides-cross": sort_guides(); if( sizeof(xguides) && sizeof(yguides) ) { xc = xguides[ min(sizeof(xguides),gx) - 1 ]; yc = yguides[ min(sizeof(yguides),gy) - 1 ]; break; } guides=0; case "guides-region": sort_guides(); if( (sizeof(xguides)>1) && (sizeof(yguides)>1) ) { gx = min(sizeof(xguides)-1,gx) - 1; gy = min(sizeof(yguides)-1,gy) - 1; gx2 = min(sizeof(xguides),gx2) - 1; gy2 = min(sizeof(yguides),gy2) - 1; x0 = xguides[gx]; x1 = xguides[gx2] - x0; y0 = yguides[gy]; y1 = yguides[gy2] - y0; break; } default: if( sscanf( args->crop, "%d,%d-%d,%d", x0, y0, x1, y1 ) == 4) { x1 -= x0; y1 -= y0; }
b0e2f82001-11-28Anders Johansson  break;
6f03962001-02-09Per Hedbor  case "auto": [ x0, y0, x1, y1 ] = reply->find_autocrop(); x1 -= x0; y1 -= y0; }
3cbd0d2000-01-31Per Hedbor  }
83f54a2003-01-16Martin Stjernholm  sort_guides = 0; // To avoid garbage.
3cbd0d2000-01-31Per Hedbor 
6f03962001-02-09Per Hedbor #define SCALEI 1 #define SCALEF 2
482ac32001-03-16Per Hedbor #define SCALEA 4 #define CROP 8
6f03962001-02-09Per Hedbor 
83f54a2003-01-16Martin Stjernholm  function do_scale_and_crop = lambda ( int x0, int y0, int x1, int y1, int|float w, int|float h, int type )
3cbd0d2000-01-31Per Hedbor  {
6f03962001-02-09Per Hedbor  if( (type & CROP) && x1 && y1 && ((x1 != reply->xsize()) || (y1 != reply->ysize()) || x0 || y0 ) ) { reply = reply->copy( x0, y0, x0+x1-1, y0+y1-1, (bgcolor?bgcolor->rgb():0) ); if( alpha ) alpha = alpha->copy( x0, y0, x0+x1-1,y0+y1-1, Image.Color.black); }
3cbd0d2000-01-31Per Hedbor 
6f03962001-02-09Per Hedbor  if( type & SCALEI ) { if( xc || yc ) { if( h && !w ) w = (reply->xsize() * h) / reply->ysize(); if( w && !h ) h = (reply->ysize() * w) / reply->xsize(); x0 = max( xc - w/2, 0 ); y0 = max( yc - h/2, 0 ); x1 = w; y1 = h; if( x0 + x1 > reply->xsize() ) { x0 = reply->xsize()-w; if( x0 < 0 ) { x0 = 0; x1 = reply->xsize(); } } if( y0 + y1 > reply->ysize() ) { y0 = reply->ysize()-h; if( y0 < 0 ) { y0 = 0; y1 = reply->ysize(); } } reply = reply->copy( x0, y0, x0+x1-1, y0+y1-1, (bgcolor?bgcolor->rgb():0) ); if( alpha ) alpha = alpha->copy( x0, y0, x0+x1-1,y0+y1-1, Image.Color.black); } }
482ac32001-03-16Per Hedbor  if( (type & SCALEF) && (w != 1.0) )
6f03962001-02-09Per Hedbor  { reply = reply->scale( w ); if( alpha ) alpha = alpha->scale( w ); }
482ac32001-03-16Per Hedbor  else if( (type & SCALEA) && ((reply->xsize() != w) || (reply->ysize() != h)) )
6f03962001-02-09Per Hedbor  {
482ac32001-03-16Per Hedbor  reply = reply->scale( w,h ); if( alpha ) alpha = alpha->scale( w,h ); } else if( (type & SCALEI) && ((reply->xsize() != w) || (reply->ysize() != h)) ) {
d228c72001-03-17Per Hedbor  if( w && h ) { if( w / (float)reply->xsize() < h / (float)reply->ysize() ) h = 0; else w = 0; }
e0dda02001-04-17Per Hedbor  w = min( w, reply->xsize() ); h = min( h, reply->ysize() );
6f03962001-02-09Per Hedbor  reply = reply->scale( w,h ); if( alpha ) alpha = alpha->scale( w,h ); } };
c526921999-05-18Per Hedbor  if( args->scale ) { int x, y; if( sscanf( args->scale, "%d,%d", x, y ) == 2)
482ac32001-03-16Per Hedbor  do_scale_and_crop( x0, y0, x1, y1, x, y, SCALEA|CROP );
3255b11999-05-19Per Hedbor  else if( (float)args->scale < 3.0)
6f03962001-02-09Per Hedbor  do_scale_and_crop( x0, y0, x1, y1, ((float)args->scale), ((float)args->scale), SCALEF|CROP );
c526921999-05-18Per Hedbor  }
6f03962001-02-09Per Hedbor  else if( args->maxwidth || args->maxheight || args["max-width"] || args["max-height"] )
c526921999-05-18Per Hedbor  {
6f03962001-02-09Per Hedbor  int x = (int)args->maxwidth|| (int)args["max-width"];
e7e6031999-11-05Per Hedbor  int y = (int)args->maxheight||(int)args["max-height"];
6f03962001-02-09Per Hedbor  do_scale_and_crop( x0, y0, x1, y1, x, y, SCALEI|CROP );
c526921999-05-18Per Hedbor  }
6f03962001-02-09Per Hedbor  else do_scale_and_crop( x0, y0, x1, y1, 0, 0, CROP );
83f54a2003-01-16Martin Stjernholm  do_scale_and_crop = 0; // To avoid garbage.
c526921999-05-18Per Hedbor 
6f03962001-02-09Per Hedbor  if( args["span-width"] || args["span-height"] )
d593702001-01-23Anders Johansson  { int width = (int)args["span-width"]; int height = (int)args["span-height"];
6f03962001-02-09Per Hedbor 
d593702001-01-23Anders Johansson  if( (width && reply->xsize() > width) || (height && reply->ysize() > height) ) { if( (width && height && (reply->xsize() / (float)width > reply->ysize() / (float)height)) || !height ) { reply = reply->scale( width, 0 ); if( alpha ) alpha = alpha->scale( width, 0 ); } else if( height ) { reply = reply->scale( 0, height ); if( alpha ) alpha = alpha->scale( 0, height ); } } int x1,x2,y1,y2; if( width ) { x1 = -((width - reply->xsize()) / 2); x2 = x1 + width - 1; } if( height ) { y1 = -((height - reply->ysize()) / 2); y2 = y1 + height - 1; } if( width && height ) { reply = reply->copy(x1,y1,x2,y2,(bgcolor?bgcolor->rgb():0)); if( alpha ) alpha = alpha->copy(x1,y1,x2,y2); } else if( width ) { reply = reply->copy(x1,0,x2,reply->ysize(),(bgcolor?bgcolor->rgb():0)); if ( alpha ) alpha = alpha->copy(x1,0,x2,alpha->ysize()); } else { reply = reply->copy(0,y1,reply->xsize(),y2,(bgcolor?bgcolor->rgb():0)); if( alpha ) alpha = alpha->copy(0,y1,alpha->xsize(),y2); } }
112b1c2000-02-02Per Hedbor  if( args["rotate-cw"] || args["rotate-ccw"]) { float degree = (float)(args["rotate-cw"] || args["rotate-ccw"]);
66868a2000-04-15Per Hedbor  switch( args["rotate-unit"] && args["rotate-unit"][0..0] )
112b1c2000-02-02Per Hedbor  {
9792c72000-05-16Henrik Grubbström (Grubba)  case "r": degree = (degree / (2*3.1415)) * 360; break;
f526692000-05-16Henrik Grubbström (Grubba)  case "d": break;
66868a2000-04-15Per Hedbor  case "n": degree = (degree / 400) * 360; break; case "p": degree = (degree / 1.0) * 360; break;
112b1c2000-02-02Per Hedbor  }
66868a2000-04-15Per Hedbor  if( args["rotate-cw"] )
112b1c2000-02-02Per Hedbor  degree = -degree;
f526692000-05-16Henrik Grubbström (Grubba)  if(!alpha)
66868a2000-04-15Per Hedbor  alpha = reply->copy()->clear(255,255,255); reply = reply->rotate_expand( degree );
f526692000-05-16Henrik Grubbström (Grubba)  alpha = alpha->rotate( degree, 0,0,0 );
112b1c2000-02-02Per Hedbor  } if( args["mirror-x"] ) { if( alpha ) alpha = alpha->mirrorx(); reply = reply->mirrorx(); } if( args["mirror-y"] ) { if( alpha ) alpha = alpha->mirrory(); reply = reply->mirrory(); }
91a1562000-04-11Per Hedbor  if( bgcolor && alpha && !true_alpha )
e7e6031999-11-05Per Hedbor  { reply = Image.Image( reply->xsize(), reply->ysize(), bgcolor ) ->paste_mask( reply, alpha ); }
91a1562000-04-11Per Hedbor  if( args["cs-rgb-hsv"] )reply = reply->rgb_to_hsv(); if( args["cs-grey"] ) reply = reply->grey(); if( args["cs-invert"] ) reply = reply->invert(); if( args["cs-hsv-rgb"] )reply = reply->hsv_to_rgb(); if( !true_alpha && alpha ) alpha = alpha->threshold( 4 );
69a8691999-05-18Per Hedbor  if( quant || (format=="gif") ) {
641a882000-06-01Martin Nilsson  int ncols = quant; if( format=="gif" ) { ncols = ncols||id->misc->defquant||32; if( ncols > 254 ) ncols = 254; }
000c781999-05-25Per Hedbor  ct = Image.Colortable( reply, ncols );
69a8691999-05-18Per Hedbor  if( dither )
23264c2000-10-17Per Hedbor  { if( dither == "random" ) dither = "random_grey";
000c781999-05-25Per Hedbor  if( ct[ dither ] ) ct[ dither ]();
69a8691999-05-18Per Hedbor  else ct->ordered();
23264c2000-10-17Per Hedbor  }
69a8691999-05-18Per Hedbor  } mapping enc_args = ([]); if( ct ) enc_args->colortable = ct;
a1c8692000-09-12Per Hedbor 
69a8691999-05-18Per Hedbor  if( alpha ) enc_args->alpha = alpha;
c526921999-05-18Per Hedbor  foreach( glob( "*-*", indices(args)), string n ) if(sscanf(n, "%*[^-]-%s", string opt ) == 2)
a1c8692000-09-12Per Hedbor  if( opt != "alpha" ) enc_args[opt] = (int)args[n];
c526921999-05-18Per Hedbor 
69a8691999-05-18Per Hedbor  switch(format) {
34fc592001-03-01Per Hedbor  case "wbf": format = "wbmp"; case "wbmp":
387fa62001-03-05Per Hedbor  Image.Colortable bw=Image.Colortable( ({ ({ 0,0,0 }), ({ 255,255,255 }) }) ); bw->floyd_steinberg(); data = Image.WBF.encode( bw->map( reply ), enc_args );
34fc592001-03-01Per Hedbor  break;
69a8691999-05-18Per Hedbor  case "gif":
f04b922000-09-13Jonas Wallden #if constant(Image.GIF) && constant(Image.GIF.encode)
c3a53d1999-06-25Per Hedbor  if( alpha && true_alpha )
b79be21999-06-11Per Hedbor  {
91a1562000-04-11Per Hedbor  Image.Colortable bw=Image.Colortable( ({ ({ 0,0,0 }), ({ 255,255,255 }) }) );
e9d7c51999-11-02Per Hedbor  bw->floyd_steinberg(); alpha = bw->map( alpha );
b79be21999-06-11Per Hedbor  }
000c781999-05-25Per Hedbor  if( catch { if( alpha ) data = Image.GIF.encode_trans( reply, ct, alpha ); else data = Image.GIF.encode( reply, ct ); }) data = Image.GIF.encode( reply );
23264c2000-10-17Per Hedbor  break;
f04b922000-09-13Jonas Wallden #else
23264c2000-10-17Per Hedbor  // Fall-through when there is no GIF encoder available -- // use PNG with a colortable instead. format = "png";
f04b922000-09-13Jonas Wallden #endif
e9d7c51999-11-02Per Hedbor 
69a8691999-05-18Per Hedbor  case "png":
e9d7c51999-11-02Per Hedbor  if( ct ) enc_args->palette = ct;
69a8691999-05-18Per Hedbor  m_delete( enc_args, "colortable" );
36a6ad2000-09-15Per Hedbor  if( !(args["png-use-alpha"] || args["true-alpha"]) ) m_delete( enc_args, "alpha" ); else if( enc_args->alpha ) // PNG encoder doesn't handle alpha and palette simultaneously // which is rather sad, since that's the only thing 100% supported // by all common browsers. m_delete( enc_args, "palette"); else
f04b922000-09-13Jonas Wallden  m_delete( enc_args, "alpha" );
81f8af1999-12-20Martin Nilsson 
69a8691999-05-18Per Hedbor  default:
23264c2000-10-17Per Hedbor  if(!Image[upper_case( format )] || !Image[upper_case( format )]->encode )
34fc592001-03-01Per Hedbor  error("Image format "+format+" not supported\n");
f04b922000-09-13Jonas Wallden  data = Image[upper_case( format )]->encode( reply, enc_args );
69a8691999-05-18Per Hedbor  }
81f8af1999-12-20Martin Nilsson  meta = ([
69a8691999-05-18Per Hedbor  "xsize":reply->xsize(), "ysize":reply->ysize(),
34fc592001-03-01Per Hedbor  "type":(format == "wbmp" ? "image/vnd.wap.wbmp" : "image/"+format ),
69a8691999-05-18Per Hedbor  ]);
c526921999-05-18Per Hedbor  }
81f8af1999-12-20Martin Nilsson  else if( mappingp(reply) )
69a8691999-05-18Per Hedbor  {
6a613a2002-06-17Anders Johansson  // This could be an error from get_file() if(reply->error) return reply;
69a8691999-05-18Per Hedbor  meta = reply->meta; data = reply->data; if( !meta || !data ) error("Invalid reply mapping.\n"
29fac12001-01-04Martin Nilsson  "Expected ([ \"meta\": ([metadata]), \"data\":\"data\" ])\n" "Got %O\n", reply);
69a8691999-05-18Per Hedbor  }
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("draw %O done\n", name ); #endif
31885f2001-01-02Per Hedbor  // Avoid throwing and error if the same image is rendered twice.
29fac12001-01-04Martin Nilsson  catch(store_data( name, data, meta ));
69a8691999-05-18Per Hedbor  }
29fac12001-01-04Martin Nilsson  static void store_data( string id, string data, mapping meta )
69a8691999-05-18Per Hedbor  {
55fc492000-12-30Per Hedbor  if(!stringp(data)) return;
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("store %O (%d bytes)\n", id, strlen(data) ); #endif
29fac12001-01-04Martin Nilsson  meta_cache_insert( id, meta ); string meta_data = encode_value( meta );
23b7ec2001-08-27Per Hedbor  if( catch { QUERY( "UPDATE "+name+" SET size=%d, atime=%d, meta=%s, " "data=%s WHERE id=%s", strlen(data)+strlen(meta_data), time(1), meta_data, data, id ); #ifdef ARG_CACHE_DEBUG werror("updated entry for %O, existed before\n", id ); #endif } ) { #ifdef ARG_CACHE_DEBUG werror("new entry for %O\n", id ); #endif QUERY("INSERT INTO "+name+ " (id,size,atime,meta,data) VALUES (%s,%d,%d,%s,%s)", id, strlen(data)+strlen(meta_data), time(1), meta_data, data ); }
69a8691999-05-18Per Hedbor  }
aea1652001-06-26Per Hedbor  static mapping restore_meta( string id, RequestID rid )
69a8691999-05-18Per Hedbor  { if( meta_cache[ id ] )
e7ac492003-04-06Anders Johansson  { meta_cache[ id ][ 1 ] = time(1); // Update cached atime. return meta_cache[ id ][ 0 ]; }
6e05b22001-01-01Martin Nilsson 
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG
6789262001-09-06Per Hedbor  werror("restore meta %O\n", id );
23b7ec2001-08-27Per Hedbor #endif array(mapping(string:string)) q = QUERY("SELECT meta FROM "+name+" WHERE id=%s", id );
4afcd42001-08-09Per Hedbor 
3294072001-09-06Per Hedbor  string s; if(!sizeof(q) || !strlen(s = q[0]->meta))
69a8691999-05-18Per Hedbor  return 0;
9fcbed2001-01-20Per Hedbor 
18e19c2000-06-12Martin Stjernholm  mapping m;
55fc492000-12-30Per Hedbor  if (catch (m = decode_value (s))) { report_error( "Corrupt data in cache-entry "+id+".\n" );
4afcd42001-08-09Per Hedbor  QUERY( "DELETE FROM "+name+" WHERE id=%s", id);
18e19c2000-06-12Martin Stjernholm  return 0; }
6789262001-09-06Per Hedbor  QUERY("UPDATE "+name+" SET atime=UNIX_TIMESTAMP() WHERE id=%s",id );
18e19c2000-06-12Martin Stjernholm  return meta_cache_insert( id, m );
69a8691999-05-18Per Hedbor  }
e7ac492003-04-06Anders Johansson  static void sync_meta() { // Sync cached atimes. foreach(indices(meta_cache), string id) { if (meta_cache[id][1]) QUERY("UPDATE "+name+" SET atime=%d WHERE id=%s", meta_cache[id][1], id); } meta_cache = ([]); }
93dd0e2000-07-27Johan Sundström  void flush(int|void age) //! Flush the cache. If an age (an integer as returned by
23b7ec2001-08-27Per Hedbor  //! @[time()]) is provided, only images with their latest access before
b5e3862000-12-31Martin Nilsson  //! that time are flushed.
4f2d5f2000-03-13Per Hedbor  {
3548012001-08-10Per Hedbor  int num; #ifdef DEBUG int t = gethrtime(); report_debug("Cleaning "+name+" image cache ... "); #endif
e7ac492003-04-06Anders Johansson  sync_meta();
5c1b622001-08-01Per Hedbor  uid_cache = ([]); rst_cache = ([]);
55fc492000-12-30Per Hedbor  if( !age ) {
e7ac492003-04-06Anders Johansson #ifdef DEBUG report_debug("cleared\n"); #endif
21207e2001-06-13Per Hedbor  QUERY( "DELETE FROM "+name );
3548012001-08-10Per Hedbor  num = -1;
b5e3862000-12-31Martin Nilsson  return;
55fc492000-12-30Per Hedbor  }
b5e3862000-12-31Martin Nilsson 
5c1b622001-08-01Per Hedbor  array(string) ids = QUERY( "SELECT id FROM "+name+" WHERE atime < "+age)->id;
b5e3862000-12-31Martin Nilsson 
3548012001-08-10Per Hedbor  num = sizeof( ids );
b5e3862000-12-31Martin Nilsson  int q; while(q<sizeof(ids)) {
2c64162002-12-04Marcus Wellhardh  string list = map(ids[q..q+99], get_db()->quote) * "','";
b5e3862000-12-31Martin Nilsson  q+=100;
4afcd42001-08-09Per Hedbor  QUERY( "DELETE FROM "+name+" WHERE id in ('"+list+"')" );
55fc492000-12-30Per Hedbor  }
b5e3862000-12-31Martin Nilsson 
3548012001-08-10Per Hedbor  if( num ) catch { // Old versions of Mysql lacks OPTIMIZE. Not that we support // them, really, but it might be nice not to throw an error, at // least. QUERY( "OPTIMIZE TABLE "+name ); };
29fac12001-01-04Martin Nilsson 
3548012001-08-10Per Hedbor #ifdef DEBUG report_debug("%s removed, %dms\n",
eae43a2001-08-13Per Hedbor  (num==-1?"all":num?(string)num:"none"),
3548012001-08-10Per Hedbor  (gethrtime()-t)/1000); #endif
1f58d02000-01-02Martin Nilsson  }
93dd0e2000-07-27Johan Sundström  array(int) status(int|void age) //! Return the total number of images in the cache, their cumulative //! sizes in bytes and, if an age time_t was supplied, the number of
b5e3862000-12-31Martin Nilsson  //! images that has not been accessed after that time is returned //! (see <ref>flush()</ref>). (Three integers are returned //! regardless of whether an age parameter was given.)
93dd0e2000-07-27Johan Sundström  {
b5e3862000-12-31Martin Nilsson  int imgs=0, size=0, aged=0; array(mapping(string:string)) q;
55fc492000-12-30Per Hedbor 
21207e2001-06-13Per Hedbor  q=QUERY("SHOW TABLE STATUS");
6e05b22001-01-01Martin Nilsson  foreach(q, mapping qq) if(has_prefix(qq->Name, name)) { imgs = (int)qq->Rows; size += (int)qq->Data_length; }
b5e3862000-12-31Martin Nilsson 
6e05b22001-01-01Martin Nilsson  if(age) {
21207e2001-06-13Per Hedbor  q=QUERY("select SUM(1) as num from "+name+" where atime < "+age);
6e05b22001-01-01Martin Nilsson  aged = (int)q[0]->num; }
b5e3862000-12-31Martin Nilsson  return ({ imgs, size, aged });
1f58d02000-01-02Martin Nilsson  }
aea1652001-06-26Per Hedbor  static mapping(string:mapping) rst_cache = ([ ]); static mapping(string:string) uid_cache = ([ ]);
4afcd42001-08-09Per Hedbor 
aea1652001-06-26Per Hedbor  static mapping restore( string id, RequestID rid )
69a8691999-05-18Per Hedbor  {
aea1652001-06-26Per Hedbor  array q; string uid; if( zero_type(uid = uid_cache[id]) ) {
4afcd42001-08-09Per Hedbor  q = QUERY( "SELECT uid FROM "+name+" WHERE id=%s",id);
aea1652001-06-26Per Hedbor  if( sizeof(q) ) uid = q[0]->uid; else uid = 0; uid_cache[id] = uid; }
90274d2001-08-09Per Hedbor  if( uid && strlen(uid) )
aea1652001-06-26Per Hedbor  { User u; if( !(u=rid->conf->authenticate(rid)) || (u->name() != uid ) ) return rid->conf->authenticate_throw(rid, "User"); }
9fcbed2001-01-20Per Hedbor  if( rst_cache[ id ] ) return rst_cache[ id ] + ([]);
29fac12001-01-04Martin Nilsson 
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("restore %O\n", id ); #endif
23f8432001-09-05Per Hedbor  q = QUERY( "SELECT meta,atime,data FROM "+name+" WHERE id=%s",id);
9fcbed2001-01-20Per Hedbor  if( sizeof(q) )
55fc492000-12-30Per Hedbor  {
4afcd42001-08-09Per Hedbor  if( sizeof(q[0]->data) ) {
23b7ec2001-08-27Per Hedbor  // Case 1: We have cache entry and image.
4afcd42001-08-09Per Hedbor  string f = q[0]->data;
23b7ec2001-08-27Per Hedbor  mapping m; catch( m = decode_value( q[0]->meta ) );
4afcd42001-08-09Per Hedbor  if( !m ) return 0;
9fcbed2001-01-20Per Hedbor 
4afcd42001-08-09Per Hedbor  m = Roxen.http_string_answer( f, m->type||("image/gif") );
23f8432001-09-05Per Hedbor 
4afcd42001-08-09Per Hedbor  if( strlen( f ) > 6000 ) return m; rst_cache[ id ] = m; if( sizeof( rst_cache ) > 100 )
41240d2001-09-03Stefan Wallström  rst_cache = ([ id : m ]);
4afcd42001-08-09Per Hedbor  return rst_cache[ id ] + ([]); }
23b7ec2001-08-27Per Hedbor  // Case 2: We have cache entry, but no data. return 0;
29fac12001-01-04Martin Nilsson  }
4afcd42001-08-09Per Hedbor  else {
23b7ec2001-08-27Per Hedbor  // Case 3: No cache entry. Create one
4afcd42001-08-09Per Hedbor  User u = rid->conf->authenticate(rid); string uid = ""; if( u ) uid = u->name();
23b7ec2001-08-27Per Hedbor  // Might have been insterted from elsewhere. catch { QUERY("INSERT INTO "+name+ " (id,uid,atime) VALUES (%s,%s,UNIX_TIMESTAMP())", id, uid ); };
4afcd42001-08-09Per Hedbor  } return 0;
69a8691999-05-18Per Hedbor  }
93dd0e2000-07-27Johan Sundström  string data( array|string|mapping args, RequestID id, int|void nodraw ) //! Returns the actual raw image data of the image rendered from the
23b7ec2001-08-27Per Hedbor  //! @[args] instructions.
93dd0e2000-07-27Johan Sundström  //!
23b7ec2001-08-27Per Hedbor  //! A non-zero @[nodraw] parameter means an image not already in the
93dd0e2000-07-27Johan Sundström  //! cache will not be rendered on the fly, but instead return zero.
69a8691999-05-18Per Hedbor  {
23b7ec2001-08-27Per Hedbor  mapping res = http_file_answer( args, id, nodraw ); return res && res->data;
69a8691999-05-18Per Hedbor  }
93dd0e2000-07-27Johan Sundström  mapping http_file_answer( array|string|mapping data,
81f8af1999-12-20Martin Nilsson  RequestID id,
c3a53d1999-06-25Per Hedbor  int|void nodraw )
23b7ec2001-08-27Per Hedbor  //! Returns a @[result mapping] like one generated by //! @[Roxen.http_file_answer()] but for the image file
93dd0e2000-07-27Johan Sundström  //! rendered from the `data' instructions. //!
23b7ec2001-08-27Per Hedbor  //! Like @[metadata], a non-zero @[nodraw]parameter means an
93dd0e2000-07-27Johan Sundström  //! image not already in the cache will not be rendered on the fly,
aea1652001-06-26Per Hedbor  //! but instead zero will be returned (this will be seen as a 'File //! not found' error)
69a8691999-05-18Per Hedbor  { string na = store( data,id ); mixed res;
23b7ec2001-08-27Per Hedbor #ifdef ARG_CACHE_DEBUG werror("data %O\n", na ); #endif
aea1652001-06-26Per Hedbor  if(! (res=restore( na,id )) )
69a8691999-05-18Per Hedbor  {
9ea9b72001-11-27Henrik Grubbström (Grubba)  mixed err; if (nodraw || (err = catch {
6a613a2002-06-17Anders Johansson  if (mapping res = draw( na, id )) return res;
9ea9b72001-11-27Henrik Grubbström (Grubba)  })) {
e696672001-11-13Henrik Grubbström (Grubba)  // File not found.
2354f12001-12-19Marcus Wellhardh  if(arrayp(err) && sizeof(err) && stringp(err[0]) && err[0][0..21] == "Requesting unknown key") report_debug("Requesting unknown key %O from %O\n", id->not_query, (sizeof(id->referer)?id->referer[0]:"unknown page")); else report_debug("Error in draw: %s\n", describe_backtrace(err));
e696672001-11-13Henrik Grubbström (Grubba)  return 0; }
23b7ec2001-08-27Per Hedbor  if( !(res = restore( na,id )) ) error("Draw callback did not generate any data\n");
69a8691999-05-18Per Hedbor  }
6fd56d2001-09-05Per Hedbor  res->stat = ({ 0, 0, 0, 900000000, 0, 0, 0, 0, 0 });
000ae22002-06-13Jonas Wallden  // Setting the cacheable flag is done in order to get headers sent which // cause the image to be cached in the client even when using https // sessions. However, this flag also controls whether the file should
9896932002-10-11Jonas Wallden  // be placed in the protocol-level cache, so we'll counter by setting a // separate flag.
bad2c02001-08-21Jonas Wallden  id->misc->cacheable = INITIAL_CACHEABLE;
9896932002-10-11Jonas Wallden  id->misc->no_proto_cache = 1;
69a8691999-05-18Per Hedbor  return res; }
93dd0e2000-07-27Johan Sundström  mapping metadata( array|string|mapping data, RequestID id, int|void nodraw ) //! Returns a mapping of image metadata for an image generated from
23b7ec2001-08-27Per Hedbor  //! the data given (as sent to @[store()]). If a non-zero //! @[nodraw] parameter is given and the image was not in the cache,
93dd0e2000-07-27Johan Sundström  //! it will not be rendered on the fly to get the correct data.
2ff8461999-09-02Per Hedbor  { string na = store( data,id );
23b7ec2001-08-27Per Hedbor  mapping res; #ifdef ARG_CACHE_DEBUG werror("meta %O\n", na ); #endif if(! (res = restore_meta( na,id )) )
2ff8461999-09-02Per Hedbor  { if(nodraw) return 0; draw( na, id );
aea1652001-06-26Per Hedbor  return restore_meta( na,id );
2ff8461999-09-02Per Hedbor  }
23b7ec2001-08-27Per Hedbor  return res;
2ff8461999-09-02Per Hedbor  } mapping tomapp( mixed what ) { if( mappingp( what )) return what; return ([ "":what ]); } string store( array|string|mapping data, RequestID id )
93dd0e2000-07-27Johan Sundström  //! Store the data your draw callback expects to receive as its //! first argument(s). If the data is an array, the draw callback
6533f22001-08-23Martin Nilsson  //! will be called like <pi>callback( @@data, id )</pi>.
2ff8461999-09-02Per Hedbor  {
aea1652001-06-26Per Hedbor  string ci, user;
83f54a2003-01-16Martin Stjernholm  function update_args = lambda ( mapping a )
aea1652001-06-26Per Hedbor  { if (!a->format)
f04b922000-09-13Jonas Wallden  // Make implicit format choice explicit #if constant(Image.GIF) && constant(Image.GIF.encode)
aea1652001-06-26Per Hedbor  a->format = "gif";
f04b922000-09-13Jonas Wallden #else
aea1652001-06-26Per Hedbor  a->format = "png";
f04b922000-09-13Jonas Wallden #endif
e89d022001-09-21Per Hedbor  if( id->misc->authenticated_user && !id->misc->authenticated_user->is_transient )
aea1652001-06-26Per Hedbor  // This entry is not actually used, it's only there to // generate a unique key. a["\0u"] = user = id->misc->authenticated_user->name(); };
4afcd42001-08-09Per Hedbor  if( mappingp( data ) ) {
aea1652001-06-26Per Hedbor  update_args( data );
2ff8461999-09-02Per Hedbor  ci = argcache->store( data );
4afcd42001-08-09Per Hedbor  } else if( arrayp( data ) ) {
aea1652001-06-26Per Hedbor  if( !mappingp( data[0] ) ) error("Expected mapping as the first element of the argument array\n"); update_args( data[0] );
93dd0e2000-07-27Johan Sundström  ci = map( map( data, tomapp ), argcache->store )*"$";
f04b922000-09-13Jonas Wallden  } else
2ff8461999-09-02Per Hedbor  ci = data;
83f54a2003-01-16Martin Stjernholm  update_args = 0; // To avoid garbage.
aea1652001-06-26Per Hedbor 
23b7ec2001-08-27Per Hedbor  if( zero_type( uid_cache[ ci ] ) )
aea1652001-06-26Per Hedbor  { uid_cache[ci] = user;
74241c2001-08-27Per Hedbor  if( catch(QUERY( "UPDATE "+name+" SET uid=%s WHERE id=%s", user||"", ci )) )
4afcd42001-08-09Per Hedbor  QUERY("INSERT INTO "+name+" (id,uid,atime) VALUES (%s,%s,%d)",
23b7ec2001-08-27Per Hedbor  ci, user||"", time(1) );
aea1652001-06-26Per Hedbor  }
9ea9b72001-11-27Henrik Grubbström (Grubba) 
29e7fe2001-11-27Marcus Wellhardh #ifndef NO_ARG_CACHE_SB_REPLICATE
9ea9b72001-11-27Henrik Grubbström (Grubba)  if(id->misc->persistent_cache_crawler) { // Force an update of atime for the requested arg cache id. foreach(ci/"$", string key) { #if REPLICATE_DEBUG werror("Request for id %O from prefetch crawler.\n", key);
07fbe12001-11-27Marcus Wellhardh #endif /* REPLICATE_DEBUG */
9ea9b72001-11-27Henrik Grubbström (Grubba)  argcache->refresh_arg(key); } }
07fbe12001-11-27Marcus Wellhardh #endif /* NO_ARG_CACHE_SB_REPLICATE */
2ff8461999-09-02Per Hedbor  return ci; } void set_draw_function( function to )
93dd0e2000-07-27Johan Sundström  //! Set a new draw function.
2ff8461999-09-02Per Hedbor  { draw_function = to; }
55fc492000-12-30Per Hedbor  static void setup_tables() {
4afcd42001-08-09Per Hedbor  if(catch(QUERY("SELECT DATA FROM "+name+" WHERE id=''")))
55fc492000-12-30Per Hedbor  {
aea1652001-06-26Per Hedbor  werror("Creating image-cache tables for '"+name+"'\n"); catch(QUERY("DROP TABLE "+name));
4afcd42001-08-09Per Hedbor  // The old tables. This is only useful for people who have run // Roxen 2.2 from cvs before
aea1652001-06-26Per Hedbor  catch(QUERY("DROP TABLE "+name+"_data"));
eae43a2001-08-13Per Hedbor  master()->resolv("DBManager.is_module_table") ( 0,"local",name,"Image cache for "+name);
21207e2001-06-13Per Hedbor  QUERY("CREATE TABLE "+name+" ("
aea1652001-06-26Per Hedbor  "id CHAR(64) NOT NULL PRIMARY KEY, " "size INT UNSIGNED NOT NULL DEFAULT 0, " "uid CHAR(32) NOT NULL DEFAULT '', "
4afcd42001-08-09Per Hedbor  "atime INT UNSIGNED NOT NULL DEFAULT 0,"
aea1652001-06-26Per Hedbor  "meta MEDIUMBLOB NOT NULL DEFAULT '',"
4afcd42001-08-09Per Hedbor  "data MEDIUMBLOB NOT NULL DEFAULT ''," "INDEX atime (atime)" ")" );
55fc492000-12-30Per Hedbor  } }
0b41772001-09-06Per Hedbor  Sql.Sql get_db() { return dbm_cached_get("local"); }
03156d2001-01-29Per Hedbor  static void init_db( ) {
e7ac492003-04-06Anders Johansson  catch(sync_meta());
aea1652001-06-26Per Hedbor  setup_tables();
03156d2001-01-29Per Hedbor  }
5c1b622001-08-01Per Hedbor  void do_cleanup( ) {
e7ac492003-04-06Anders Johansson  background_run( 3600*10+random(4711), do_cleanup );
5c1b622001-08-01Per Hedbor  flush(time()-7*3600*24); }
55fc492000-12-30Per Hedbor  void create( string id, function draw_func )
93dd0e2000-07-27Johan Sundström  //! Instantiate an image cache of your own, whose image files will
55fc492000-12-30Per Hedbor  //! be stored in a table `id' in the cache mysql database,
93dd0e2000-07-27Johan Sundström  //! //! The `draw_func' callback passed will be responsible for //! (re)generation of the images in the cache. Your draw callback //! may take any arguments you want, depending on the first argument //! you give the <ref>store()</ref> method, but its final argument //! will be the RequestID object.
2ff8461999-09-02Per Hedbor  { name = id; draw_function = draw_func;
03156d2001-01-29Per Hedbor  init_db();
e082212001-08-28Per Hedbor  // Support that the 'local' database moves.
03156d2001-01-29Per Hedbor  master()->resolv( "DBManager.add_dblist_changed_callback" )( init_db );
5c1b622001-08-01Per Hedbor  // Always remove entries that are older than one week.
e7ac492003-04-06Anders Johansson  background_run( 10, do_cleanup ); } void destroy() { if (mixed err = catch(sync_meta())) report_warning("Failed to sync cached atimes for "+name+"\n");
2ff8461999-09-02Per Hedbor  } } class ArgCache
93dd0e2000-07-27Johan Sundström //! Generic cache for storing away a persistent mapping of data to be //! refetched later by a short string key. This being a cache, your //! data may be thrown away at random when the cache is full.
2ff8461999-09-02Per Hedbor {
0b41772001-09-06Per Hedbor #undef QUERY #define QUERY(X,Y...) db->query(X,Y)
54f3402001-09-06Per Hedbor  Sql.Sql db;
6dde082001-08-20Per Hedbor  string name;
2ff8461999-09-02Per Hedbor  #define CACHE_VALUE 0 #define CACHE_SKEY 1
0e00082001-03-05Per Hedbor #define CACHE_SIZE 900
2ff8461999-09-02Per Hedbor #define CLEAN_SIZE 100
a662d52000-12-31Per Hedbor  static string lq, ulq;
6789262001-09-06Per Hedbor 
21207e2001-06-13Per Hedbor #ifdef THREADS Thread.Mutex mutex = Thread.Mutex();
76fac82001-10-01Per Hedbor  // Allow recursive locks, since it's normal here. # define LOCK() mixed __; catch( __ = mutex->lock() )
de4a0c2001-03-16Per Hedbor #else # define LOCK() #endif
2ff8461999-09-02Per Hedbor  static mapping (string:mixed) cache = ([ ]);
b870ac1999-09-03Henrik Grubbström (Grubba)  static void setup_table()
2ff8461999-09-02Per Hedbor  {
af9bac2001-08-21Per Hedbor  if(catch(QUERY("SELECT md5 FROM "+name+" WHERE id=0")))
eae43a2001-08-13Per Hedbor  {
af9bac2001-08-21Per Hedbor  master()->resolv("DBManager.is_module_table")
3c67642001-08-22Per Hedbor  ( 0, "local", name,
af9bac2001-08-21Per Hedbor  "The argument cache, used to map between " "a short unique string and an argument " "mapping" ); catch(QUERY("DROP TABLE "+name ));
19e34e2001-06-15Per Hedbor  QUERY("CREATE TABLE "+name+" ("
9ea9b72001-11-27Henrik Grubbström (Grubba)  "id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, " "index_id INT UNSIGNED NULL DEFAULT NULL, " "md5 CHAR(32) NOT NULL DEFAULT '', " "atime INT UNSIGNED NOT NULL DEFAULT 0, " "contents BLOB NOT NULL DEFAULT '', "
af9bac2001-08-21Per Hedbor  "INDEX hind (md5))");
eae43a2001-08-13Per Hedbor  }
9ea9b72001-11-27Henrik Grubbström (Grubba)  // Add column index_id if it doesn't exists. if(catch(QUERY("SELECT index_id FROM "+name+" WHERE id=0"))) { QUERY("ALTER TABLE "+name+" " "ADD index_id INT UNSIGNED NULL DEFAULT NULL"); }
2ff8461999-09-02Per Hedbor  }
03156d2001-01-29Per Hedbor  static void init_db()
2ff8461999-09-02Per Hedbor  {
2c0b612001-01-29Per Hedbor  // Delay DBManager resolving to before the 'roxen' object is // compiled.
03156d2001-01-29Per Hedbor  cache = ([]);
0b41772001-09-06Per Hedbor  db = dbm_cached_get("local");
a662d52000-12-31Per Hedbor  setup_table( );
2ff8461999-09-02Per Hedbor  }
03156d2001-01-29Per Hedbor  static void create( string _name ) { name = _name; init_db(); // Support that the 'local' database moves (not really nessesary, // but it won't hurt either) master()->resolv( "DBManager.add_dblist_changed_callback" )( init_db );
98eac92002-02-06Marcus Wellhardh  get_plugins();
03156d2001-01-29Per Hedbor  }
6dde082001-08-20Per Hedbor  string read_args( int id )
2ff8461999-09-02Per Hedbor  {
63c4562001-09-06Per Hedbor  LOCK();
19e34e2001-06-15Per Hedbor  array res = QUERY("SELECT contents FROM "+name+" WHERE id="+id);
a662d52000-12-31Per Hedbor  if( sizeof(res) )
2ff8461999-09-02Per Hedbor  {
19e34e2001-06-15Per Hedbor  QUERY("UPDATE "+name+" SET atime='"+time(1)+"' WHERE id="+id);
a662d52000-12-31Per Hedbor  return res[0]->contents;
2ff8461999-09-02Per Hedbor  } return 0; }
9ea9b72001-11-27Henrik Grubbström (Grubba)  int read_index_id( int id ) { LOCK(); array res = QUERY("SELECT index_id FROM "+name+" WHERE id="+id); if(sizeof(res) && res[0]->index_id) return (int)res[0]->index_id; return -1; } int create_key( string long_key, string|void md, int|void index_id )
af9bac2001-08-21Per Hedbor  { if( !md ) md = md5(long_key);
2c426e2002-02-01Henrik Grubbström (Grubba)  LOCK();
af9bac2001-08-21Per Hedbor  array data = QUERY("SELECT id,contents FROM "+name+" WHERE md5=%s", md );
a662d52000-12-31Per Hedbor  foreach( data, mapping m ) if( m->contents == long_key )
54f3402001-09-06Per Hedbor  return (int)m->id;
9ea9b72001-11-27Henrik Grubbström (Grubba)  if(zero_type(index_id)) index_id = -1;
54f3402001-09-06Per Hedbor 
9ea9b72001-11-27Henrik Grubbström (Grubba)  string index_id_value = (index_id == -1? "NULL": index_id); QUERY( "INSERT INTO "+name+" (contents,md5,atime,index_id) VALUES " "(%s,%s,UNIX_TIMESTAMP(),"+index_id_value+")", long_key, md );
54f3402001-09-06Per Hedbor  int id = (int)db->master_sql->insert_id();
8f2e822003-04-14Marcus Wellhardh  if(!id) error("ArgCache::create_key() insert_id returned 0.\n");
9ea9b72001-11-27Henrik Grubbström (Grubba) #ifdef REPLICATE_DEBUG werror("Create new local key: id: %d, index_id: %d.\n", id, index_id); #endif
44200a2001-08-22Per Hedbor 
9ea9b72001-11-27Henrik Grubbström (Grubba)  (plugins->create_key-({0}))( id, long_key );
af9bac2001-08-21Per Hedbor 
3e2e092001-03-06Per Hedbor  return id;
2ff8461999-09-02Per Hedbor  }
6dde082001-08-20Per Hedbor 
387fa62001-03-05Per Hedbor  static int low_key_exists( string key ) {
63c4562001-09-06Per Hedbor  LOCK(); int res = sizeof( QUERY( "SELECT id FROM "+name+" WHERE id="+(int)key)); // Fool optimizer. if( res ) return res;
387fa62001-03-05Per Hedbor  }
6dde082001-08-20Per Hedbor  string secret;
387fa62001-03-05Per Hedbor  static void ensure_secret() { if( !secret ) secret = query( "argcache_secret" ); }
af9bac2001-08-21Per Hedbor 
9ea9b72001-11-27Henrik Grubbström (Grubba)  string encode_id( int a, int b, string|void server )
387fa62001-03-05Per Hedbor  { ensure_secret(); object crypto = Crypto.arcfour();
9ea9b72001-11-27Henrik Grubbström (Grubba)  crypto->set_encrypt_key( server||secret );
af9bac2001-08-21Per Hedbor  string res = crypto->crypt( a+"\327"+b );
b579b42002-02-06Henrik Grubbström (Grubba)  // Ensure that we do not have a leading NUL. res[0] |= 0x80;
387fa62001-03-05Per Hedbor  res = Gmp.mpz( res, 256 )->digits( 36 ); return res; }
af9bac2001-08-21Per Hedbor 
3e2e092001-03-06Per Hedbor  static array plugins; static void get_plugins() {
6dde082001-08-20Per Hedbor  ensure_secret();
3e2e092001-03-06Per Hedbor  plugins = ({}); foreach( ({ "../local/arg_cache_plugins", "arg_cache_plugins" }), string d) if( file_stat( d ) )
270b2e2001-08-20Marcus Wellhardh  foreach( glob("*.pike", get_dir( d )), string f )
6dde082001-08-20Per Hedbor  { object plug = ((program)(d+"/"+f))(this_object()); if( !plug->disabled ) plugins += ({ plug }); }
3e2e092001-03-06Per Hedbor  } static array plugin_decode_id( string id ) { mixed r; foreach( (plugins->decode_id-({0})), function(string:array(int)) f ) if( r = f( id ) ) return r; return 0; }
2ff8461999-09-02Per Hedbor 
b579b42002-02-06Henrik Grubbström (Grubba)  array(int) low_decode_id(string a, string key)
387fa62001-03-05Per Hedbor  {
3c67642001-08-22Per Hedbor  if( catch( a = Gmp.mpz( a, 36 )->digits( 256 ) ) ) return 0; // Not very likely to work...
b579b42002-02-06Henrik Grubbström (Grubba)  object crypto = Crypto.arcfour(); crypto->set_encrypt_key(key);
f4c4cf2002-02-06Henrik Grubbström (Grubba)  string msg = crypto->crypt(a);
b579b42002-02-06Henrik Grubbström (Grubba)  // Fix the high-order bit altered by encode_id().
f4c4cf2002-02-06Henrik Grubbström (Grubba)  msg[0] &= 0x7f;
387fa62001-03-05Per Hedbor  int i, j;
f4c4cf2002-02-06Henrik Grubbström (Grubba)  if((sscanf(msg, "%d\327%d", i, j) == 2) && (msg == i + "\327" + j)) {
b579b42002-02-06Henrik Grubbström (Grubba)  return ({ i, j }); }
f4c4cf2002-02-06Henrik Grubbström (Grubba) #ifndef NO_BROKEN_ARGCACHE_FALLBACK // Fallback -- Check if it's an old broken key. crypto->set_encrypt_key(key); msg = crypto->crypt("\0"+a); if((sscanf(msg, "%d\327%d", i, j) == 2) && (msg == i + "\327" + j)) { return ({ i, j }); } #endif /* !NO_BROKEN_ARGCACHE_FALLBACK */
b579b42002-02-06Henrik Grubbström (Grubba)  return 0; } static array(int) decode_id( string a ) { array(int) res; ensure_secret(); if (res = low_decode_id(a, secret)) { return res; } return plugin_decode_id(a);
387fa62001-03-05Per Hedbor  }
2ff8461999-09-02Per Hedbor  int key_exists( string key )
93dd0e2000-07-27Johan Sundström  //! Does the key 'key' exist in the cache? Returns 1 if it does, 0 //! if it was not present.
2ff8461999-09-02Per Hedbor  {
de4a0c2001-03-16Per Hedbor  if( cache[key] ) return 1;
387fa62001-03-05Per Hedbor  array i = decode_id( key ); if(!i) return 0; return low_key_exists( i[0] ) && low_key_exists( i[1] );
2ff8461999-09-02Per Hedbor  } string store( mapping args )
93dd0e2000-07-27Johan Sundström  //! Store a mapping (of purely encode_value:able data) in the //! argument cache. The string returned is your key to retrieve the //! data later.
69a8691999-05-18Per Hedbor  {
2ff8461999-09-02Per Hedbor  array b = values(args), a = sort(indices(args),b);
6789262001-09-06Per Hedbor  LOCK();
9ea9b72001-11-27Henrik Grubbström (Grubba)  int index_id = low_store( a ); string id = encode_id( index_id, low_store( b, index_id ) );
10bb982001-03-16Per Hedbor  if( !cache[ id ] ) cache[ id ] = args+([]);
387fa62001-03-05Per Hedbor  return id; }
2ff8461999-09-02Per Hedbor 
9ea9b72001-11-27Henrik Grubbström (Grubba)  int low_store( array a, int|void index_id )
387fa62001-03-05Per Hedbor  {
10bb982001-03-16Per Hedbor  string data = encode_value_canonic( a );
af9bac2001-08-21Per Hedbor  string hv = md5( data );
0e00082001-03-05Per Hedbor  if( mixed q = cache[ hv ] ) return q;
eae43a2001-08-13Per Hedbor 
0e00082001-03-05Per Hedbor #ifdef THREADS if( mixed q = cache[ hv ] ) return q; #endif
8693b22000-01-08Martin Stjernholm  if( sizeof( cache ) >= CACHE_SIZE )
af9bac2001-08-21Per Hedbor  cache = ([]);
8693b22000-01-08Martin Stjernholm 
9ea9b72001-11-27Henrik Grubbström (Grubba)  int id = create_key( data, hv, index_id );
0e00082001-03-05Per Hedbor  cache[ hv ] = id; cache[ id ] = a;
2ff8461999-09-02Per Hedbor  return id;
69a8691999-05-18Per Hedbor  }
3e2e092001-03-06Per Hedbor  mapping lookup( string id ) //! Recall a mapping stored in the cache.
c3a53d1999-06-25Per Hedbor  {
de4a0c2001-03-16Per Hedbor  if( cache[id] ) return cache[id]+([]);
387fa62001-03-05Per Hedbor  array i = decode_id( id );
44200a2001-08-22Per Hedbor  if( !i ) error("Requesting unknown key\n");
387fa62001-03-05Per Hedbor  array a = low_lookup( i[0] ); array b = low_lookup( i[1] );
84d7512003-01-13Martin Stjernholm  if (!arrayp (a) || !arrayp (b) || sizeof (a) != sizeof (b)) // Got lookup with ids from an old cache which has been zapped, // and the entries are now used for something else. error("Requesting unknown key\n"); return (cache[id] = mkmapping( a, b ))+([]);
387fa62001-03-05Per Hedbor  }
6dde082001-08-20Per Hedbor  array low_lookup( int id )
387fa62001-03-05Per Hedbor  { mixed v;
0e00082001-03-05Per Hedbor  if( v = cache[id] ) return v;
2ff8461999-09-02Per Hedbor  string q = read_args( id );
3e2e092001-03-06Per Hedbor  if( !q ) error("Requesting unknown key\n");
a662d52000-12-31Per Hedbor  mixed data = decode_value(q);
de4a0c2001-03-16Per Hedbor  string hl = Crypto.md5()->update( q )->digest(); cache[ hl ] = id;
0e00082001-03-05Per Hedbor  cache[ id ] = data;
2ff8461999-09-02Per Hedbor  return data;
c3a53d1999-06-25Per Hedbor  }
2ff8461999-09-02Per Hedbor  void delete( string id )
93dd0e2000-07-27Johan Sundström  //! Remove the data element stored under the key 'id'.
69a8691999-05-18Per Hedbor  {
8f2e822003-04-14Marcus Wellhardh  LOCK();
3e2e092001-03-06Per Hedbor  (plugins->delete-({0}))( id );
387fa62001-03-05Per Hedbor  m_delete( cache, id );
0e00082001-03-05Per Hedbor  foreach( decode_id( id ), int id )
2ff8461999-09-02Per Hedbor  {
3e2e092001-03-06Per Hedbor  (plugins->low_delete-({0}))( id );
387fa62001-03-05Per Hedbor  if(cache[id]) { m_delete( cache, cache[id] ); m_delete( cache, id ); }
19e34e2001-06-15Per Hedbor  QUERY( "DELETE FROM "+name+" WHERE id="+id );
2ff8461999-09-02Per Hedbor  }
69a8691999-05-18Per Hedbor  }
9ea9b72001-11-27Henrik Grubbström (Grubba)  int write_dump(Stdio.File file, int|void from_time) // Write a mapping from id to encoded arg string for all local arg // entries created after from_time to a file. Returns 0 if faled, 1 // otherwise. { array(int) ids; if(!sizeof(plugins)) { if(from_time) ids = (array(int)) QUERY( "SELECT id from "+name+ " WHERE atime >= %d " " AND index_id IS NOT NULL", from_time )->id; else ids = (array(int)) QUERY( "SELECT id from "+name )->id; } else ids = ((plugins->get_local_ids-({0}))(from_time)) * ({}); if(sizeof(secret+"\n") != file->write(secret+"\n")) return 0; foreach(ids, int id) { int index_id = read_index_id(id); #ifdef REPLICATE_DEBUG werror("write_dump: argcache id: %d, index_id: %d.\n", id, index_id); #endif string s = MIME.encode_base64(encode_value(({ id, read_args(id), index_id, read_args(index_id) })), 1)+"\n"; if(sizeof(s) != file->write(s)) return 0; } return file->write("EOF\n") == 4; } static void create_remote_key(int id, string key, int index_id, string index_key, string server) { (plugins->create_remote_key-({0}))( id, key, index_id, index_key, server ); } string read_dump (Stdio.FILE file) // Returns an error message if there was a parse error, 0 otherwise. { string secret = file->gets(); if(!secret || !sizeof(secret)) return "Server secret is missing\n"; string s; while(s = file->gets()) { if(s == "EOF") return 0; array a; if(catch { a = decode_value(MIME.decode_base64(s)); }) return "Decode failed for argcache record\n"; if(sizeof(a) != 4) return "Decode failed for argcache record (wrong size on key array)\n"; #ifdef REPLICATE_DEBUG werror("read_dump: argcache id: %d, index_id: %d.\n", a[0], a[2]); #endif create_remote_key(a[0], a[1], a[2], a[3], secret); } if(s != "EOF") return "Missing data in argcache file\n"; return 0; } void refresh_arg(string id) { array i = decode_id( id ); if( !i ) error("Requesting unknown key\n");
8f2e822003-04-14Marcus Wellhardh  LOCK();
9ea9b72001-11-27Henrik Grubbström (Grubba)  QUERY("UPDATE "+name+" SET atime='"+time(1)+"' WHERE id="+i[0]); QUERY("UPDATE "+name+" SET atime='"+time(1)+"' WHERE id="+i[1]); }
2ff8461999-09-02Per Hedbor }
69a8691999-05-18Per Hedbor 
95b69e1999-09-05Per Hedbor mapping cached_decoders = ([]); string decode_charset( string charset, string data ) {
808e171999-09-05Henrik Grubbström (Grubba)  // FIXME: This code is probably not thread-safe!
95b69e1999-09-05Per Hedbor  if( charset == "iso-8859-1" ) return data; if( !cached_decoders[ charset ] ) cached_decoders[ charset ] = Locale.Charset.decoder( charset ); data = cached_decoders[ charset ]->feed( data )->drain();
dc3a471999-12-15Marcus Comstedt  cached_decoders[ charset ]->clear();
95b69e1999-09-05Per Hedbor  return data; }
2ff8461999-09-02Per Hedbor void create() {
23414a2000-07-21Andreas Lange  // Register localization projects #define __REG_PROJ Locale.register_project
0569fa2001-09-29Martin Nilsson  __REG_PROJ("roxen_""start", "translations/%L/roxen_start.xml"); __REG_PROJ("roxen_""config", "translations/%L/roxen_config.xml"); __REG_PROJ("roxen_""message", "translations/%L/roxen_message.xml"); __REG_PROJ("admin_""tasks", "translations/%L/admin_tasks.xml");
c12fcc2001-01-01Martin Nilsson  Locale.set_default_project_path("translations/%L/%P.xml");
23414a2000-07-21Andreas Lange #undef __REG_PROJ
5629e62000-07-11Andreas Lange  define_global_variables();
cde8d62000-02-16Per Hedbor 
2ff8461999-09-02Per Hedbor  // for module encoding stuff
d3b98e1999-11-28Per Hedbor 
6fa4d12001-02-23Per Hedbor  add_constant( "CFUserDBModule",config_userdb_module );
50b6972001-08-31Per Hedbor  //add_constant( "ArgCache", ArgCache );
27008b2000-03-20Martin Stjernholm  //add_constant( "roxen.load_image", load_image );
2ff8461999-09-02Per Hedbor 
4ed0992003-01-15Henrik Grubbström (Grubba)  if (all_constants()["roxen"]) { error("Duplicate Roxen object!\n"); }
50b6972001-08-31Per Hedbor  // simplify dumped strings.
95b69e1999-09-05Per Hedbor  add_constant( "roxen", this_object());
27008b2000-03-20Martin Stjernholm  //add_constant( "roxen.decode_charset", decode_charset);
855c391999-11-29Per Hedbor 
2c0b612001-01-29Per Hedbor // add_constant( "DBManager", ((object)"base_server/dbs.pike") );
c08c162001-01-02Per Hedbor 
b52e862001-08-24Martin Stjernholm  // This is currently needed to resolve the circular references in // RXML.pmod correctly. :P master()->resolv ("RXML.refs"); master()->resolv ("RXML.PXml"); master()->resolv ("RXML.PEnt"); foreach(({ "module.pmod","PEnt.pike", "PExpr.pike","PXml.pike", "refs.pmod","utils.pmod" }), string q ) dump( "etc/modules/RXML.pmod/"+ q ); dump( "etc/modules/RXML.pmod/module.pmod" );
5574b22002-05-06Martin Stjernholm  master()->add_dump_constant ("RXML.empty_tag_set", master()->resolv ("RXML.empty_tag_set"));
b52e862001-08-24Martin Stjernholm  // Already loaded. No delayed dump possible. dump( "etc/roxen_master.pike" ); dump( "etc/modules/Roxen.pmod" ); dump( "base_server/config_userdb.pike" ); dump( "base_server/disk_cache.pike" ); dump( "base_server/roxen.pike" ); dump( "base_server/basic_defvar.pike" ); dump( "base_server/newdecode.pike" ); dump( "base_server/read_config.pike" ); dump( "base_server/global_variables.pike" ); dump( "base_server/module_support.pike" ); dump( "base_server/socket.pike" ); dump( "base_server/cache.pike" ); dump( "base_server/supports.pike" ); dump( "base_server/hosts.pike"); dump( "base_server/language.pike");
db47c92001-08-24Martin Stjernholm #ifndef __NT__ if(!getuid()) add_constant("Privs", Privs); else #endif /* !__NT__ */ add_constant("Privs", class { void create(string reason, int|string|void uid, int|string|void gid) {} }); DDUMP( "base_server/roxenlib.pike"); DDUMP( "etc/modules/Dims.pmod"); DDUMP( "config_interface/boxes/Box.pmod" );
50b6972001-08-31Per Hedbor  dump( "base_server/html.pike");
db47c92001-08-24Martin Stjernholm  add_constant( "RoxenModule", RoxenModule); add_constant( "ModuleInfo", ModuleInfo ); add_constant( "load", load);
50b6972001-08-31Per Hedbor 
db47c92001-08-24Martin Stjernholm  add_constant( "Roxen.set_locale", set_locale ); add_constant( "Roxen.get_locale", get_locale ); add_constant( "roxen.locale", locale ); //add_constant( "roxen.ImageCache", ImageCache );
2c0b612001-01-29Per Hedbor //int s = gethrtime();
9a8a152000-09-25Per Hedbor  _configuration = (program)"configuration";
7e596b2000-02-13Per Hedbor  dump( "base_server/configuration.pike" ); dump( "base_server/rxmlhelp.pike" );
2c0b612001-01-29Per Hedbor  // Override the one from prototypes.pike add_constant( "Configuration", _configuration ); //report_debug( "[Configuration: %.2fms] ", (gethrtime()-s)/1000.0);
69a8691999-05-18Per Hedbor }
55a8662000-11-20Per Hedbor mixed get_locale( ) { return locale->get(); }
aa78722001-08-24Martin Stjernholm int set_u_and_gid (void|int from_handler_thread)
93dd0e2000-07-27Johan Sundström //! Set the uid and gid to the ones requested by the user. If the //! sete* functions are available, and the define SET_EFFECTIVE is //! enabled, the euid and egid is set. This might be a minor security //! hole, but it will enable roxen to start CGI scripts with the //! correct permissions (the ones the owner of that script have).
2ff8461999-09-02Per Hedbor { #ifndef __NT__ string u, g; int uid, gid; array pw;
81f8af1999-12-20Martin Nilsson 
aa78722001-08-24Martin Stjernholm  if (from_handler_thread && geteuid()) { // The euid switch in the backend thread worked here too, so // there's no need to do anything. #ifdef TEST_EUID_CHANGE werror ("euid change effective in handler thread.\n"); #endif return 1; }
8552d92001-01-13Martin Nilsson  u=query("User");
2ff8461999-09-02Per Hedbor  sscanf(u, "%s:%s", u, g); if(strlen(u)) { if(getuid()) {
aa78722001-08-24Martin Stjernholm  if (!from_handler_thread)
81f1a42002-02-27Martin Stjernholm  report_error(LOC_M(24, "It is possible to change uid and gid only "
aa78722001-08-24Martin Stjernholm  "if the server is running as root.")+"\n");
2ff8461999-09-02Per Hedbor  } else {
aa78722001-08-24Martin Stjernholm #ifdef TEST_EUID_CHANGE if (Stdio.write_file ("rootonly", "Only root should be able to read this.\n", 0600)) test_euid_change = 1; #endif
2ff8461999-09-02Per Hedbor  if (g) { #if constant(getgrnam) pw = getgrnam (g); if (!pw) if (sscanf (g, "%d", gid)) pw = getgrgid (gid), g = (string) gid; else report_error ("Couldn't resolve group " + g + ".\n"), g = 0; if (pw) g = pw[0], gid = pw[2];
a22f6f1999-05-12Per Hedbor #else
2ff8461999-09-02Per Hedbor  if (!sscanf (g, "%d", gid)) report_warning ("Can't resolve " + g + " to gid on this system; " "numeric gid required.\n");
a22f6f1999-05-12Per Hedbor #endif
2ff8461999-09-02Per Hedbor  }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor  pw = getpwnam (u); if (!pw) if (sscanf (u, "%d", uid)) pw = getpwuid (uid), u = (string) uid; else { report_error ("Couldn't resolve user " + u + ".\n"); return 0; } if (pw) { u = pw[0], uid = pw[2]; if (!g) gid = pw[3]; }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor #ifdef THREADS
3e3bab2001-01-19Per Hedbor  Thread.MutexKey mutex_key;
aa78722001-08-24Martin Stjernholm  object threads_disabled; if (!from_handler_thread) { // If this is necessary from every handler thread, these // things are thread local and thus are no locks necessary. catch { mutex_key = euid_egid_lock->lock(); }; threads_disabled = _disable_threads(); }
2ff8461999-09-02Per Hedbor #endif
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor #if constant(seteuid) if (geteuid() != getuid()) seteuid (getuid()); #endif
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor #if constant(initgroups) catch { initgroups(pw[0], gid); // Doesn't always work - David. }; #endif
8552d92001-01-13Martin Nilsson  if (query("permanent_uid")) {
2ff8461999-09-02Per Hedbor #if constant(setuid) if (g) { # if constant(setgid) setgid(gid);
23414a2000-07-21Andreas Lange  if (getgid() != gid) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(25, "Failed to set gid.")+"\n");
23414a2000-07-21Andreas Lange  g = 0; }
2ff8461999-09-02Per Hedbor # else
aa78722001-08-24Martin Stjernholm  if (!from_handler_thread) report_warning(LOC_M(26, "Setting gid not supported on this system.") +"\n");
2ff8461999-09-02Per Hedbor  g = 0; # endif } setuid(uid);
23414a2000-07-21Andreas Lange  if (getuid() != uid) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(27, "Failed to set uid.")+"\n");
23414a2000-07-21Andreas Lange  u = 0; }
aa78722001-08-24Martin Stjernholm  if (u && !from_handler_thread) report_notice(CALL_M("setting_uid_gid_permanently", "eng") (uid, gid, u, g));
2ff8461999-09-02Per Hedbor #else
aa78722001-08-24Martin Stjernholm  if (!from_handler_thread) report_warning(LOC_M(28, "Setting uid not supported on this system.") +"\n");
2ff8461999-09-02Per Hedbor  u = g = 0; #endif }
b84a161999-06-07Martin Stjernholm  else {
2ff8461999-09-02Per Hedbor #if constant(seteuid) if (g) { # if constant(setegid) setegid(gid);
23414a2000-07-21Andreas Lange  if (getegid() != gid) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(29, "Failed to set effective gid.")+"\n");
23414a2000-07-21Andreas Lange  g = 0; }
2ff8461999-09-02Per Hedbor # else
aa78722001-08-24Martin Stjernholm  if (!from_handler_thread) report_warning(LOC_M(30, "Setting effective gid not supported on " "this system.")+"\n");
2ff8461999-09-02Per Hedbor  g = 0; # endif } seteuid(uid);
23414a2000-07-21Andreas Lange  if (geteuid() != uid) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(31, "Failed to set effective uid.")+"\n");
23414a2000-07-21Andreas Lange  u = 0; }
aa78722001-08-24Martin Stjernholm  if (u && !from_handler_thread) report_notice(CALL_M("setting_uid_gid", "eng")(uid, gid, u, g));
2ff8461999-09-02Per Hedbor #else
aa78722001-08-24Martin Stjernholm  if (!from_handler_thread) report_warning(LOC_M(32, "Setting effective uid not supported on " "this system.")+"\n");
2ff8461999-09-02Per Hedbor  u = g = 0; #endif
b84a161999-06-07Martin Stjernholm  }
2ff8461999-09-02Per Hedbor 
0217b82003-03-03Henrik Grubbström (Grubba)  enable_coredumps(1);
ab0cb12001-09-27Anders Johansson #ifdef THREADS
9963842001-09-26Martin Stjernholm  // Paranoia. mutex_key = 0; threads_disabled = 0;
ab0cb12001-09-27Anders Johansson #endif
9963842001-09-26Martin Stjernholm 
2ff8461999-09-02Per Hedbor  return !!u;
a22f6f1999-05-12Per Hedbor  } }
2ff8461999-09-02Per Hedbor #endif return 0; }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor void reload_all_configurations() {
4c12d72000-12-10Per Hedbor  Configuration conf;
2ff8461999-09-02Per Hedbor  array (object) new_confs = ({}); mapping config_cache = ([]); int modified; setvars(retrieve("Variables", 0));
e082212001-08-28Per Hedbor 
2ff8461999-09-02Per Hedbor  foreach(list_all_configurations(), string config)
a22f6f1999-05-12Per Hedbor  {
1f4a6c2000-08-28Per Hedbor  mixed err; Stat st;
4cf7832000-02-16Per Hedbor  conf = find_configuration( config );
2ff8461999-09-02Per Hedbor  if(!(st = config_is_modified(config))) { if(conf) { config_cache[config] = config_stat_cache[config]; new_confs += ({ conf });
a22f6f1999-05-12Per Hedbor  }
2ff8461999-09-02Per Hedbor  continue;
a22f6f1999-05-12Per Hedbor  }
2ff8461999-09-02Per Hedbor  modified = 1; config_cache[config] = st;
4cf7832000-02-16Per Hedbor  if(conf) {
2ff8461999-09-02Per Hedbor  conf->stop(); conf->invalidate_cache(); conf->create(conf->name);
a22f6f1999-05-12Per Hedbor  } else {
2ff8461999-09-02Per Hedbor  if(err = catch
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  conf = enable_configuration(config); }) {
67f60e2000-07-09Martin Nilsson  string bt=describe_backtrace(err);
49cd282000-08-11Andreas Lange  report_error(LOC_M(33, "Error while enabling configuration %s%s"),
23414a2000-07-21Andreas Lange  config, (bt ? ":\n"+bt : "\n"));
2ff8461999-09-02Per Hedbor  continue;
a22f6f1999-05-12Per Hedbor  }
7639902001-01-29Per Hedbor  function sp = master()->resolv("DBManager.set_permission"); catch(sp( "docs", conf, 1 )); // the docs db can be non-existant sp( "local", conf, 2 );
a22f6f1999-05-12Per Hedbor  }
2ff8461999-09-02Per Hedbor  if(err = catch {
4c12d72000-12-10Per Hedbor  conf->start( 0 );
2ff8461999-09-02Per Hedbor  conf->enable_all_modules(); }) {
67f60e2000-07-09Martin Nilsson  string bt=describe_backtrace(err);
49cd282000-08-11Andreas Lange  report_error(LOC_M(33, "Error while enabling configuration %s%s"),
23414a2000-07-21Andreas Lange  config, (bt ? ":\n"+bt : "\n" ));
2ff8461999-09-02Per Hedbor  continue; } new_confs += ({ conf });
a22f6f1999-05-12Per Hedbor  }
81f8af1999-12-20Martin Nilsson 
2ff8461999-09-02Per Hedbor  foreach(configurations - new_confs, conf)
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  modified = 1;
c8ee712000-09-09Andreas Lange  report_notice(LOC_M(34,"Disabling old configuration %s")+"\n", conf->name);
2ff8461999-09-02Per Hedbor  conf->stop(); destruct(conf); } if(modified) { configurations = new_confs;
f02f5c2000-08-03Martin Stjernholm  fix_config_lookup();
2ff8461999-09-02Per Hedbor  config_stat_cache = config_cache;
a22f6f1999-05-12Per Hedbor  }
2ff8461999-09-02Per Hedbor }
a22f6f1999-05-12Per Hedbor 
d093992000-09-25Per Hedbor private mapping(string:Configuration) config_lookup = ([]);
f02f5c2000-08-03Martin Stjernholm // Maps config name to config object.
3557f52001-06-30Martin Stjernholm Thread.Local bootstrap_info = Thread.Local(); // Used temporarily at configuration and module initialization to hold // some info so that it's available even before create() in the // configuration/module is called.
2e8d0f2001-06-28Martin Stjernholm 
f02f5c2000-08-03Martin Stjernholm void fix_config_lookup() { config_lookup = mkmapping (configurations->name, configurations); #ifdef DEBUG if (sizeof (configurations) != sizeof (config_lookup)) error ("Duplicate configuration names in configurations array: %O", configurations->name); #endif }
9a8a152000-09-25Per Hedbor Configuration get_configuration (string name)
45dc022000-08-16Martin Stjernholm //! Gets the configuration with the given identifier name.
f02f5c2000-08-03Martin Stjernholm { #ifdef DEBUG if (sizeof (configurations) != sizeof (config_lookup)) error ("config_lookup out of synch with configurations.\n"); #endif return config_lookup[name]; }
9a8a152000-09-25Per Hedbor Configuration enable_configuration(string name)
2ff8461999-09-02Per Hedbor {
f02f5c2000-08-03Martin Stjernholm #ifdef DEBUG if (get_configuration (name)) error ("A configuration called %O already exists.\n", name); #endif
3557f52001-06-30Martin Stjernholm  bootstrap_info->set (name); Configuration cf = _configuration();
2ff8461999-09-02Per Hedbor  configurations += ({ cf });
bc5c2a2000-08-23Per Hedbor  fix_config_lookup();
2ff8461999-09-02Per Hedbor  return cf; }
a22f6f1999-05-12Per Hedbor 
45dc022000-08-16Martin Stjernholm void disable_configuration (string name) {
3e3bab2001-01-19Per Hedbor  if (Configuration conf = config_lookup[ name ]) {
45dc022000-08-16Martin Stjernholm  configurations -= ({conf});
bc5c2a2000-08-23Per Hedbor  fix_config_lookup();
45dc022000-08-16Martin Stjernholm  } } void remove_configuration (string name) { disable_configuration (name); ::remove_configuration (name); }
2ff8461999-09-02Per Hedbor // Enable all configurations void enable_configurations() { array err; configurations = ({});
f02f5c2000-08-03Martin Stjernholm  config_lookup = ([]);
15635b1999-10-10Per Hedbor 
2ff8461999-09-02Per Hedbor  foreach(list_all_configurations(), string config)
1d7d6d2000-02-16Per Hedbor  { int t = gethrtime();
2dd46b2000-03-24Per Hedbor  report_debug("\nEnabling the configuration %s ...\n", config);
9a8a152000-09-25Per Hedbor  if(err=catch( enable_configuration(config)->start(0) ))
c8ee712000-09-09Andreas Lange  report_error("\n"+LOC_M(35, "Error while loading configuration %s%s"), config+":\n", describe_backtrace(err)+"\n");
2dd46b2000-03-24Per Hedbor  report_debug("Enabled %s in %.1fms\n", config, (gethrtime()-t)/1000.0 );
1d7d6d2000-02-16Per Hedbor  }
d093992000-09-25Per Hedbor  foreach( configurations, Configuration c )
abc59a2000-08-23Per Hedbor  { if(sizeof( c->registered_urls ) ) return; } report_fatal("No configurations could open any ports. Will shutdown.\n");
cf769a2002-10-14Henrik Grubbström (Grubba)  restart(0.0, 50); /* Actually a shutdown, but... */
2ff8461999-09-02Per Hedbor }
a22f6f1999-05-12Per Hedbor 
1d7d6d2000-02-16Per Hedbor int all_modules_loaded;
2ff8461999-09-02Per Hedbor void enable_configurations_modules() {
4cf7832000-02-16Per Hedbor  if( all_modules_loaded++ ) return;
d093992000-09-25Per Hedbor  foreach(configurations, Configuration config)
4cf7832000-02-16Per Hedbor  if(mixed err=catch( config->enable_all_modules() ))
49cd282000-08-11Andreas Lange  report_error(LOC_M(36, "Error while loading modules in "
c8ee712000-09-09Andreas Lange  "configuration %s%s"), config->name+":\n", describe_backtrace(err)+"\n");
a22f6f1999-05-12Per Hedbor }
d681842000-02-08Per Hedbor mapping low_decode_image(string data, void|mixed tocolor)
a22f6f1999-05-12Per Hedbor {
91a1562000-04-11Per Hedbor  mapping w = Image._decode( data, tocolor ); if( w->image ) return w; return 0;
a22f6f1999-05-12Per Hedbor }
10ed6d2000-11-21Per Hedbor constant decode_layers = Image.decode_layers;
d681842000-02-08Per Hedbor 
81f8af1999-12-20Martin Nilsson mapping low_load_image(string f, RequestID id)
a22f6f1999-05-12Per Hedbor { string data;
81f8af1999-12-20Martin Nilsson  Stdio.File file; if(id->misc->_load_image_called < 5)
69a8691999-05-18Per Hedbor  {
3ffe061999-05-16David Hedbor  // We were recursing very badly with the demo module here... id->misc->_load_image_called++;
c526921999-05-18Per Hedbor  if(!(data=id->conf->try_get_file(f, id))) {
d2a4692002-05-08Jonas Wallden  // This is a major security hole! It can load any (image) file // in the low-level file system using the server's user privileges. // // file=Stdio.File(); // if(!file->open(f,"r") || !(data=file->read()))
76ad0a2000-09-14Martin Nilsson #ifdef THREADS
56b7fc1999-12-21Per Hedbor  catch {
91a1562000-04-11Per Hedbor  string host = ""; sscanf( f, "http://%[^/]", host ); if( sscanf( host, "%*s:%*d" ) != 2) host += ":80"; mapping hd = ([ "User-Agent":version(), "Host":host, ]); data = Protocols.HTTP.get_url_data( f, 0, hd );
56b7fc1999-12-21Per Hedbor  };
76ad0a2000-09-14Martin Nilsson #endif
56b7fc1999-12-21Per Hedbor  if( !data )
3ffe061999-05-16David Hedbor  return 0; } } id->misc->_load_image_called = 0;
56b7fc1999-12-21Per Hedbor  if(!data) return 0;
a22f6f1999-05-12Per Hedbor  return low_decode_image( data ); }
6a613a2002-06-17Anders Johansson array(Image.Layer)|mapping load_layers(string f, RequestID id, mapping|void opt)
d681842000-02-08Per Hedbor { string data; Stdio.File file;
6a613a2002-06-17Anders Johansson  mapping res = ([]);
d681842000-02-08Per Hedbor  if(id->misc->_load_image_called < 5) { // We were recursing very badly with the demo module here... id->misc->_load_image_called++;
6a613a2002-06-17Anders Johansson  if(!(data=id->conf->try_get_file(f, id, 0, 0, 0, res)))
d681842000-02-08Per Hedbor  {
d2a4692002-05-08Jonas Wallden  // This is a major security hole! It can load any (image) file // in the low-level file system using the server's user privileges. // // file=Stdio.File(); // if(!file->open(f,"r") || !(data=file->read()))
f764502001-08-15Per Hedbor // #ifdef THREADS
d681842000-02-08Per Hedbor  catch { data = Protocols.HTTP.get_url_nice( f )[1]; };
f764502001-08-15Per Hedbor // #endif
d681842000-02-08Per Hedbor  if( !data )
6a613a2002-06-17Anders Johansson  return res;
d681842000-02-08Per Hedbor  } } id->misc->_load_image_called = 0;
6a613a2002-06-17Anders Johansson  if(!data) return res;
a075f42000-11-21Per Hedbor  return decode_layers( data, opt );
d681842000-02-08Per Hedbor }
a22f6f1999-05-12Per Hedbor 
2537c32000-02-14Per Hedbor Image.Image load_image(string f, RequestID id)
a22f6f1999-05-12Per Hedbor { mapping q = low_load_image( f, id ); if( q ) return q->img; return 0; }
b1fca01996-11-12Per Hedbor // do the chroot() call. This is not currently recommended, since // roxen dynamically loads modules, all module files must be // available at the new location. private void fix_root(string to) {
c79b261998-02-05Johan Schön #ifndef __NT__
b1fca01996-11-12Per Hedbor  if(getuid()) {
81f8af1999-12-20Martin Nilsson  report_debug("It is impossible to chroot() if the server is not run as root.\n");
b1fca01996-11-12Per Hedbor  return; } if(!chroot(to)) {
81f8af1999-12-20Martin Nilsson  report_debug("Roxen: Cannot chroot to "+to+": ");
b1fca01996-11-12Per Hedbor #if efun(real_perror) real_perror(); #endif return; }
81f8af1999-12-20Martin Nilsson  report_debug("Root is now "+to+".\n");
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor } void create_pid_file(string where) {
c79b261998-02-05Johan Schön #ifndef __NT__
b1fca01996-11-12Per Hedbor  if(!where) return;
a3a0ec2001-03-17Martin Stjernholm // where = replace(where, ({ "$pid", "$uid" }), // ({ (string)getpid(), (string)getuid() }));
b1fca01996-11-12Per Hedbor 
4f2d5f2000-03-13Per Hedbor  r_rm(where);
1e5cc42001-03-17Martin Stjernholm  if(catch(Stdio.write_file(where, sprintf("%d\n%d\n", getpid(), getppid()))))
81f8af1999-12-20Martin Nilsson  report_debug("I cannot create the pid file ("+where+").\n");
c79b261998-02-05Johan Schön #endif
b1fca01996-11-12Per Hedbor }
3e3bab2001-01-19Per Hedbor Pipe.pipe shuffle(Stdio.File from, Stdio.File to,
ee82002001-02-03Per Hedbor  Stdio.File|void to2, function(:void)|void callback)
14179b1997-01-29Per Hedbor {
beaca01998-02-20Per Hedbor #if efun(spider.shuffle) if(!to2)
5bc1991997-01-29Per Hedbor  {
ee82002001-02-03Per Hedbor  object p = fastpipe( );
beaca01998-02-20Per Hedbor  p->input(from);
95e2b41997-05-25Wilhelm Köhler  p->set_done_callback(callback);
beaca01998-02-20Per Hedbor  p->output(to);
ccffe71999-03-05Wilhelm Köhler  return p;
a60c4c1997-07-03Henrik Grubbström (Grubba)  } else {
beaca01998-02-20Per Hedbor #endif
ee82002001-02-03Per Hedbor  // 'fastpipe' does not support multiple outputs.
3e3bab2001-01-19Per Hedbor  Pipe.pipe p = Pipe.pipe();
beaca01998-02-20Per Hedbor  if (callback) p->set_done_callback(callback); p->output(to);
33b7701998-03-20Johan Schön  if(to2) p->output(to2);
beaca01998-02-20Per Hedbor  p->input(from);
ccffe71999-03-05Wilhelm Köhler  return p;
beaca01998-02-20Per Hedbor #if efun(spider.shuffle)
b1fca01996-11-12Per Hedbor  }
beaca01998-02-20Per Hedbor #endif
b1fca01996-11-12Per Hedbor }
3aaaa71997-06-12Wilhelm Köhler 
1c9d081999-07-19Henrik Grubbström (Grubba) // Dump all threads to the debug log. void describe_all_threads() {
2eccfa2001-07-12Martin Stjernholm #if constant (thread_create) // Disable all threads to avoid potential locking problems while we // have the backtraces. It also gives an atomic view of the state. object threads_disabled = _disable_threads();
8a79f82003-02-05Jonas Wallden  report_debug("### Describing all Pike threads:\n\n");
2eccfa2001-07-12Martin Stjernholm  array(Thread.Thread) threads = all_threads(); array(string|int) thread_ids = map (threads, lambda (Thread.Thread t) { string desc = sprintf ("%O", t); if (sscanf (desc, "Thread.Thread(%d)", int i)) return i; else return desc; }); sort (thread_ids, threads);
1c9d081999-07-19Henrik Grubbström (Grubba)  int i;
2eccfa2001-07-12Martin Stjernholm  for(i=0; i < sizeof(threads); i++) { report_debug("### Thread %s%s:\n", (string) thread_ids[i],
993d5c2001-07-16Martin Stjernholm #ifdef THREADS threads[i] == backend_thread ? " (backend thread)" : "" #else "" #endif );
2eccfa2001-07-12Martin Stjernholm  report_debug(describe_backtrace(threads[i]->backtrace()) + "\n");
1c9d081999-07-19Henrik Grubbström (Grubba)  }
2eccfa2001-07-12Martin Stjernholm 
8a79f82003-02-05Jonas Wallden  report_debug ("### Total %d Pike threads\n", sizeof (threads));
2eccfa2001-07-12Martin Stjernholm  threads = 0; threads_disabled = 0; #else
5310752002-09-25Martin Stjernholm  report_debug("Describing single thread:\n%s\n", describe_backtrace (backtrace()));
2eccfa2001-07-12Martin Stjernholm #endif
1c9d081999-07-19Henrik Grubbström (Grubba) }
4f54272001-01-03Per Hedbor constant dump = roxenloader.dump;
753a831999-08-30Per Hedbor 
a577791999-11-24Per Hedbor program slowpipe, fastpipe;
1d7d6d2000-02-16Per Hedbor void initiate_argcache() { int t = gethrtime();
56509c2001-06-30Martin Stjernholm  report_debug( "Initiating argument cache ... \b");
a662d52000-12-31Per Hedbor  if( mixed e = catch( argcache = ArgCache("arguments") ) )
1d7d6d2000-02-16Per Hedbor  {
06e5502001-09-12Henrik Grubbström (Grubba)  report_fatal( "Failed to initialize the global argument cache:\n" + #ifdef DEBUG describe_backtrace(e) + #else /* !DEBUG */ describe_error(e) + #endif /* DEBUG */ "\n");
4eeda62003-09-15Martin Stjernholm  roxenloader.real_exit(1);
1d7d6d2000-02-16Per Hedbor  } add_constant( "roxen.argcache", argcache );
56509c2001-06-30Martin Stjernholm  report_debug("\bDone [%.2fms]\n", (gethrtime()-t)/1000.0);
1d7d6d2000-02-16Per Hedbor }
9c19002001-02-27Per Hedbor #ifdef TIMERS void show_timers() { call_out( show_timers, 30 ); array a = values(timers); array b = indices( timers ); sort( a, b ); reverse(a); reverse(b);
3e6f6b2001-03-11Martin Nilsson  report_notice("Timers:\n");
9c19002001-02-27Per Hedbor  for( int i = 0; i<sizeof(b); i++ )
3e6f6b2001-03-11Martin Nilsson  report_notice( " %-30s : %10.1fms\n", b[i], a[i]/1000.0 ); report_notice("\n\n");
9c19002001-02-27Per Hedbor } #endif
4717052001-05-07Per Hedbor 
f109262001-01-31Per Hedbor array argv;
7339a02000-02-10Per Hedbor int main(int argc, array tmp)
b1fca01996-11-12Per Hedbor {
4ed0992003-01-15Henrik Grubbström (Grubba)  // __builtin.gc_parameters((["enabled": 0]));
f109262001-01-31Per Hedbor  argv = tmp;
1e5cc42001-03-17Martin Stjernholm  tmp = 0;
8dff0d2001-09-03Per Hedbor  // For RBF catch(mkdir(