24c6c12000-02-20Martin Nilsson // The Roxen Webserver main program. // Copyright © 1996 - 2000, Roxen IS. //
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
1138fd2001-05-29Martin Nilsson constant cvs_version="$Id: roxen.pike,v 1.669 2001/05/29 21:52:58 nilsson Exp $";
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";
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) 
8514832000-11-27Per Hedbor #define DDUMP(X) sol( combine_path( __FILE__, "../../" + X ), dump ) static function sol = master()->set_on_load;
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 }
b0a9ea2001-04-18Martin Stjernholm array(string) compat_levels = ({"2.1", "2.2"});
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) { #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++; if(getuid()) return; /* 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]; }
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)  #if efun(cleargroups) catch { cleargroups(); }; #endif /* cleargroups */ #if efun(initgroups) catch { initgroups(u[0], u[3]); }; #endif 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");
0f8b2f1999-03-27Henrik Grubbström (Grubba)  throw(({ sprintf("Failed to set EGID to %d\n", gid), backtrace() })); }
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); #endif /* HAVE_EFFECTIVE_USER */ } void destroy() { #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)  } }; } if(getuid()) return;
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); #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); array(string) available_fonts( ); } 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
1e5cc42001-03-17Martin Stjernholm  catch {
3107101998-09-01Marcus Comstedt #ifdef THREADS
1e5cc42001-03-17Martin Stjernholm  stop_handler_threads();
3107101998-09-01Marcus Comstedt #endif /* THREADS */
1e5cc42001-03-17Martin Stjernholm  if (exit_code) report_notice("Restarting Roxen.\n"); else report_notice("Shutting down Roxen.\n"); };
8fb5172000-05-27Per Hedbor  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) {
e83c432001-03-11Martin Nilsson  if(++_recurse > 4)
c5e0961999-10-04Per Hedbor  {
3e6f6b2001-03-11Martin Nilsson  catch { report_notice("Exiting roxen (spurious signals received).\n"); stop_all_configurations(); destruct(cache);
e83c432001-03-11Martin Nilsson #ifdef THREADS
3e6f6b2001-03-11Martin Nilsson  stop_handler_threads();
e83c432001-03-11Martin Nilsson #endif /* THREADS */
3e6f6b2001-03-11Martin Nilsson  };
e83c432001-03-11Martin Nilsson  exit(-1); // Restart. } catch(stop_all_configurations()); destruct(cache);
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.
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() { while(!(w_ptr - r_ptr)) r_cond::wait(); 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; }
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(); } }
6037992000-12-13Per Hedbor // This is easier than when there is no threads. // See the discussion below. :-) function async_sig_start( function f, int really )
4c12d72000-12-10Per Hedbor { 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; while(1)
4f4bc11998-02-04Per Hedbor  { if(q=catch {
45cae31998-03-06Henrik Grubbström (Grubba)  do {
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++; if (Thread.Condition cond = hold_wakeup_cond) cond->wait(); 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));
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])); };
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(); 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 //! responding. Any thread that haven't arrived to the hold state //! since @[hold_handler_threads] is considered nonresponding. { 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; THREAD_WERR("Releasing " + was_on_hold + " threads on hold"); cond->broadcast(); if (threads_to_create > 0) { array(object) new_threads = ({}); for (threads_to_create = 0; threads_on_hold < numthreads; number_of_threads++, threads_to_create++) 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; 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 ) { if( async_called ) { 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; } async_called=1; call_out( really_call, 0, args ); } }; // 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; }
49284b2000-02-17Per Hedbor #endif /* THREADS */
81f8af1999-12-20Martin Nilsson 
76ae182001-02-23Martin Stjernholm #ifdef THREADS static Queue bg_queue = Queue(); 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 { while (array task = bg_queue->tryread()) {
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(); }
76ae182001-02-23Martin Stjernholm #if 0
3e6f6b2001-03-11Martin Nilsson  report_debug ("background run %O (%{%O, %})\n", task[0], task[1] / 1);
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]. { #ifdef THREADS if (!hold_wakeup_cond) // stop_handler_threads is running; ignore more work. return; void enqueue() { bg_queue->write (({func, args})); if (bg_queue->size() == 1) handle (bg_process_queue); }; if (delay) call_out (enqueue, delay); else enqueue(); #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); } //! @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; } }
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( ); }
4c04e52000-07-25Marcus Comstedt class InternalRequestID //! ID for internal requests that are not linked to any real request. { inherit RequestID;
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 );
4c04e52000-07-25Marcus Comstedt  misc = ([]); 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 {
761baa1999-12-08Per Hedbor  inherit Stdio.Port: port;
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
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
c5e0961999-10-04Per Hedbor  string ip;
a6e4a12000-07-09Per Hedbor  //! The IP-number (0 for ANY) this port is bound to
8fb5172000-05-27Per Hedbor  int refs;
a6e4a12000-07-09Per Hedbor  //! The number of references to this port
c5e0961999-10-04Per Hedbor  program requesthandler;
a6e4a12000-07-09Per Hedbor  //! The per-connection request handling class
ddefa61999-10-04Marcus Comstedt  array(string) sorted_urls = ({});
a6e4a12000-07-09Per Hedbor  //! Sorted by length, longest first
ddefa61999-10-04Marcus Comstedt  mapping(string:mapping) urls = ([]);
a6e4a12000-07-09Per Hedbor  //! .. url -> ([ "conf":.., ... ])
c5e0961999-10-04Per Hedbor 
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  { urls[name] = data; 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++;
ddefa61999-10-04Marcus Comstedt  urls[name] = data;
24625e2000-08-28Per Hedbor  sorted_urls = Array.sort_array(indices(urls), lambda(string a, string b) { return sizeof(a)<sizeof(b); });
c5e0961999-10-04Per Hedbor  }
ddefa61999-10-04Marcus Comstedt  void unref(string name)
a6e4a12000-07-09Per Hedbor  //! 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 
ddefa61999-10-04Marcus Comstedt  m_delete(urls, name);
5773f62000-08-27Martin Stjernholm  if (!path && sizeof (Array.uniq (values (urls)->path)) == 1) path = values (urls)[0]->path;
ddefa61999-10-04Marcus Comstedt  sorted_urls -= ({name});
c5e0961999-10-04Per Hedbor  if( !--refs ) destruct( ); // Close the port. }
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 
347c722000-08-31Per Hedbor #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)
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;
4c87772000-08-11Per Hedbor  if( sizeof( urls ) == 1 ) {
347c722000-08-31Per Hedbor  if(!mu) mu=urls[sorted_urls[0]]; INIT( mu );
4c87772000-08-11Per Hedbor  return c; } 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] );
1d7d6d2000-02-16Per Hedbor  return c;
ddefa61999-10-04Marcus Comstedt  } }
8fb5172000-05-27Per Hedbor  if( no_default ) return 0; // 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)
8fb5172000-05-27Per Hedbor  && (i = sp_fcfu( url, id, 1 )))
7120b82000-03-27Per Hedbor  return i;
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 ); 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(); 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; 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
c5e0961999-10-04Per Hedbor  ::create();
81f8af1999-12-20Martin Nilsson  if(!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) 
d0a4f82001-04-18Per Hedbor  foreach( query_option("ssl_cert_file")/",", string cert_file )
df36c61999-10-08Henrik Grubbström (Grubba)  {
d0a4f82001-04-18Per Hedbor  if( catch{ f = lopen(cert_file, "r")->read(); } ) { report_error(LOC_M(8,"SSL3: Reading cert-file failed!")+"\n"); destruct(); 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  {
d0a4f82001-04-18Per Hedbor  report_error(LOC_M(9, "SSL3: Reading key-file failed!")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); 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)  destruct(); 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"); destruct(); 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"); destruct(); 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(); object tbs = Tools.X509.decode_certificate (cert); if (!tbs) { report_error(LOC_M(13,"SSL3: Certificate not valid (DER).")+"\n"); destruct(); return; } if (tbs->public_key->rsa->public_key_equal (rsa)) key_matches++; else continue;
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"); destruct(); 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"); destruct(); 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 */ ctx->dh_params = SSL.cipher.dh_parameters(); ctx->dhe_dss_mode(); // FIXME: Add cert <-> private key check. } 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)  destruct(); return; }
d0a4f82001-04-18Per Hedbor  ctx->certificates = ({ cert }) + ctx->certificates; 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)  destruct(); 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();
287d202001-05-16Per Hedbor  report_debug("Protocol handlers ... ");
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 #if !constant(HTTPLoop.prog) if( s == "fhttp" ) continue; #endif
287d202001-05-16Per Hedbor  report_debug( 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
287d202001-05-16Per Hedbor  report_debug( 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  }; }
287d202001-05-16Per Hedbor  report_debug(" [%.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 = ([ ]); mapping(string:object) urls = ([]); 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 }
81f8af1999-12-20Martin Nilsson void unregister_url( string url )
c5e0961999-10-04Per Hedbor {
03aa492000-08-23Per Hedbor  string ourl = url;
8fb5172000-05-27Per Hedbor  url = lower_case( url );
bc5c2a2000-08-23Per Hedbor  string host, path, protocol; int port; if (!sizeof (url - " " - "\t")) return; url = replace( url, "/ANY", "/*" ); url = replace( url, "/any", "/*" ); sscanf( url, "%[^:]://%[^/]%s", protocol, host, path );
728d832000-11-27Per Hedbor  if (!host || !stringp(host)) return;
0276292000-11-27Per Hedbor  if( !protocols[ protocol ] ) return;
bc5c2a2000-08-23Per Hedbor  sscanf(host, "%[^:]:%d", host, port); if( !port ) { port = protocols[ protocol ]->default_port; url = protocol+"://"+host+":"+port+path; }
e7e6031999-11-05Per Hedbor  report_debug("Unregister "+url+"\n");
0aee222000-08-15Martin Stjernholm 
c5e0961999-10-04Per Hedbor  if( urls[ url ] && urls[ url ]->port ) {
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 
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 });
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 });
81f8af1999-12-20Martin Nilsson 
c5e0961999-10-04Per Hedbor  urls[ url ] = ([ "conf":conf, "path":path ]);
03aa492000-08-23Per Hedbor  urls[ ourl ] = ([ "conf":conf, "path":path ]);
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) {
49cd282000-08-11Andreas Lange  report_warning(LOC_M(22, "Binding the port on IP %s "
23414a2000-07-21Andreas Lange  "failed\n for URL %s!\n"),
434bac2000-07-14Andreas Lange  url, required_host);
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 ) { if( !mod->error_log ) mod->error_log = ([]);
c60cae2000-02-02Johan Sundström  mod->error_log[log_index] += ({ log_time });
e99fd41999-11-17Per Hedbor  } if( conf ) { if( !conf->error_log ) conf->error_log = ([]);
c60cae2000-02-02Johan Sundström  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(); md5->update(query("server_salt") + (unique_id_counter++) + time(1)); 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)
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);
6ca8f61998-10-13Per Hedbor  signal(signum("SIGALRM"), lambda( int n ) {
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 });
8552d92001-01-13Martin Nilsson  alarm (60*query("abs_timeout")+10);
edc9af1998-07-11David Hedbor }
dbfe9d2000-04-13Per Hedbor #endif
edc9af1998-07-11David Hedbor 
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 {
850c282001-01-10Per Hedbor  Sql.Sql db;
2ff8461999-09-02Per Hedbor  string name; string dir; function draw_function; mapping meta_cache = ([]);
0f8b2f1999-03-27Henrik Grubbström (Grubba) 
6f76f12000-06-01Martin Nilsson  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 ) { return meta_cache[i] = what; }
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  }
2ff8461999-09-02Per Hedbor  static void draw( string name, RequestID id ) {
482ac32001-03-16Per Hedbor  mixed args = Array.map( Array.map( name/"$", argcache->lookup, id->client ), frommapp);
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 
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; void sort_guides() { 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; } case "auto": [ x0, y0, x1, y1 ] = reply->find_autocrop(); x1 -= x0; y1 -= y0; }
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  void do_scale_and_crop( 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 );
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  { 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  }
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;
29fac12001-01-04Martin Nilsson  meta_cache_insert( id, meta ); string meta_data = encode_value( meta );
850c282001-01-10Per Hedbor  db->query( "INSERT INTO "+name+"_data VALUES (%s,%s,%s)", id,meta_data,data);
6e05b22001-01-01Martin Nilsson  db->query( "INSERT INTO "+name+" VALUES ('"+id+"', "+strlen(data)+ ", UNIX_TIMESTAMP(), UNIX_TIMESTAMP())" );
69a8691999-05-18Per Hedbor  } static mapping restore_meta( string id ) { if( meta_cache[ id ] ) return meta_cache[ id ];
6e05b22001-01-01Martin Nilsson 
850c282001-01-10Per Hedbor  array(mapping(string:string)) q = db->query("SELECT meta FROM "+ name+"_data WHERE id='"+ id+"'");
55fc492000-12-30Per Hedbor  if(!sizeof(q))
69a8691999-05-18Per Hedbor  return 0;
9fcbed2001-01-20Per Hedbor  db->query("UPDATE "+name+" SET atime=UNIX_TIMESTAMP() WHERE id='"+id+"'" );
29fac12001-01-04Martin Nilsson  string s = q[0]->meta;
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" );
6e05b22001-01-01Martin Nilsson  db->query( "DELETE FROM "+name+" WHERE id='"+id+"'" ); db->query( "DELETE FROM "+name+"_data WHERE id='"+id+"'" );
18e19c2000-06-12Martin Stjernholm  return 0; } return meta_cache_insert( id, m );
69a8691999-05-18Per Hedbor  }
93dd0e2000-07-27Johan Sundström  void flush(int|void age) //! Flush the cache. If an age (an integer as returned by
b5e3862000-12-31Martin Nilsson  //! <pi>time()</pi>) is provided, only images with their latest access before //! that time are flushed.
4f2d5f2000-03-13Per Hedbor  {
1f58d02000-01-02Martin Nilsson  report_debug("Flushing "+name+" image cache.\n");
55fc492000-12-30Per Hedbor  if( !age ) {
6e05b22001-01-01Martin Nilsson  db->query( "DELETE FROM "+name );
55fc492000-12-30Per Hedbor  db->query( "DELETE FROM "+name+"_data" );
b5e3862000-12-31Martin Nilsson  return;
55fc492000-12-30Per Hedbor  }
b5e3862000-12-31Martin Nilsson  array(string) ids = db->query( "SELECT id FROM "+name+" WHERE atime < "+age)->id; int q; while(q<sizeof(ids)) { string list = ids[q..q+100] * "','"; q+=100;
6e05b22001-01-01Martin Nilsson  db->query( "DELETE FROM "+name+ " WHERE id in ('"+list+"')" );
b5e3862000-12-31Martin Nilsson  db->query( "DELETE FROM "+name+"_data WHERE id in ('"+list+"')" );
55fc492000-12-30Per Hedbor  }
b5e3862000-12-31Martin Nilsson 
49e9ad2000-12-30Per Hedbor  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.
b5e3862000-12-31Martin Nilsson  db->query( "OPTIMIZE TABLE "+name+"_data" ); db->query( "OPTIMIZE TABLE "+name );
49e9ad2000-12-30Per Hedbor  };
29fac12001-01-04Martin Nilsson  meta_cache = ([]);
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 
6e05b22001-01-01Martin Nilsson  q=db->query("SHOW TABLE STATUS"); 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) { q=db->query("select SUM(1) as num from "+name+" where atime < "+age); aged = (int)q[0]->num; }
b5e3862000-12-31Martin Nilsson  return ({ imgs, size, aged });
1f58d02000-01-02Martin Nilsson  }
9fcbed2001-01-20Per Hedbor  static mapping rst_cache = ([ ]);
69a8691999-05-18Per Hedbor  static mapping restore( string id ) {
9fcbed2001-01-20Per Hedbor  if( rst_cache[ id ] ) return rst_cache[ id ] + ([]);
29fac12001-01-04Martin Nilsson 
9fcbed2001-01-20Per Hedbor  array q = db->query( "SELECT data FROM "+name+"_data WHERE id=%s",id); if( sizeof(q) )
55fc492000-12-30Per Hedbor  {
9fcbed2001-01-20Per Hedbor  string f = q[0]->data; // Caches. Thus, it's faster than doing decode_value each time here. mapping m = restore_meta( id ); if( !m ) return 0; m = Roxen.http_string_answer( f, m->type||("image/gif") ); if( strlen( f ) > 6000 ) return m; rst_cache[ id ] = m; if( sizeof( rst_cache ) > 100 ) rst_cache = ([ "id":m ]); return rst_cache[ id ] + ([]);
29fac12001-01-04Martin Nilsson  }
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 //! `data' instructions. //! //! A non-zero `nodraw' parameter means an image not already in the //! cache will not be rendered on the fly, but instead return zero.
69a8691999-05-18Per Hedbor  { string na = store( args, id ); mixed res; if(!( res = restore( na )) ) { if(nodraw) return 0; draw( na, id ); res = restore( na ); } if( res->file ) return res->file->read(); return res->data; }
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 )
93dd0e2000-07-27Johan Sundström  //! Returns a <ref>result mapping</ref> like one generated by //! <ref>Roxen.http_file_answer()</ref> but for the image file //! rendered from the `data' instructions. //! //! Like <ref>metadata</ref>, a non-zero `nodraw' parameter means an //! image not already in the cache will not be rendered on the fly, //! but instead return zero (for request not handled).
69a8691999-05-18Per Hedbor  { string na = store( data,id ); mixed res; if(!( res = restore( na )) ) { if(nodraw) return 0; draw( na, id ); res = restore( na ); } 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 //! the data given (as sent to <ref>store()</ref>). If a non-zero //! `nodraw' parameter is given and the image was not in the cache, //! it will not be rendered on the fly to get the correct data.
2ff8461999-09-02Per Hedbor  { string na = store( data,id ); if(!restore_meta( na )) { if(nodraw) return 0; draw( na, id ); return restore_meta( na ); } return restore_meta( na ); } 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 //! will be called like <pi>callback( @data, id )</pi>.
2ff8461999-09-02Per Hedbor  { string ci;
f04b922000-09-13Jonas Wallden  if( mappingp( data ) ) { if (!data->format) { // Make implicit format choice explicit #if constant(Image.GIF) && constant(Image.GIF.encode) data->format = "gif"; #else data->format = "png"; #endif }
2ff8461999-09-02Per Hedbor  ci = argcache->store( data );
f04b922000-09-13Jonas Wallden  } else if( arrayp( data ) ) { if (!data[0]->format) { // Make implicit format choice explicit #if constant(Image.GIF) && constant(Image.GIF.encode) data[0]->format = "gif"; #else data[0]->format = "png"; #endif }
93dd0e2000-07-27Johan Sundström  ci = map( map( data, tomapp ), argcache->store )*"$";
f04b922000-09-13Jonas Wallden  } else
2ff8461999-09-02Per Hedbor  ci = data; 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() {
e83c432001-03-11Martin Nilsson  if(catch(db->query("select id from "+name+" where id=''")))
55fc492000-12-30Per Hedbor  {
6e05b22001-01-01Martin Nilsson  db->query("CREATE TABLE "+name+" (" "id CHAR(64) NOT NULL PRIMARY KEY, " "size INT UNSIGNED NOT NULL DEFAULT 0, " "ctime INT UNSIGNED NOT NULL DEFAULT 0, " "atime INT UNSIGNED NOT NULL DEFAULT 0)"); db->query("CREATE TABLE "+name+"_data (" "id CHAR(64) NOT NULL PRIMARY KEY, "
29fac12001-01-04Martin Nilsson  "meta MEDIUMBLOB NOT NULL DEFAULT '',"
6e05b22001-01-01Martin Nilsson  "data MEDIUMBLOB NOT NULL DEFAULT '')");
55fc492000-12-30Per Hedbor  } }
03156d2001-01-29Per Hedbor  static void init_db( ) { meta_cache = ([]); db = master()->resolv( "DBManager.get" )( "local" ); if( !db )
97a3612001-05-18Martin Nilsson  report_fatal("No 'local' database!\n");
03156d2001-01-29Per Hedbor  catch(setup_tables()); }
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(); // Support that the 'shared' database moves. master()->resolv( "DBManager.add_dblist_changed_callback" )( init_db );
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 {
850c282001-01-10Per Hedbor  static Sql.Sql db;
a662d52000-12-31Per Hedbor  static 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;
de4a0c2001-03-16Per Hedbor #ifdef DB_SHARING
a662d52000-12-31Per Hedbor  class DBLock { static void create() { if(!lq) {
fe6aca2001-01-01Martin Nilsson  lq = "select GET_LOCK('"+name+"', 4)"; ulq = "select RELEASE_LOCK('"+name+"')";
a662d52000-12-31Per Hedbor  } db->query( lq ); } static void destroy() { db->query( ulq ); } } # define LOCK() DBLock __ = DBLock()
de4a0c2001-03-16Per Hedbor #else #ifdef THREADS Thread.Mutex _mt = Thread.Mutex(); # define LOCK() mixed __ = _mt->lock() #else # define LOCK() #endif #endif
2ff8461999-09-02Per Hedbor  static mapping (string:mixed) cache = ([ ]);
b870ac1999-09-03Henrik Grubbström (Grubba)  static void setup_table()
2ff8461999-09-02Per Hedbor  {
e83c432001-03-11Martin Nilsson  if(catch(db->query("SELECT id FROM "+name+" WHERE id=0")))
6e05b22001-01-01Martin Nilsson  db->query("CREATE TABLE "+name+" (" "id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, "
9fcbed2001-01-20Per Hedbor  "hash INT NOT NULL DEFAULT 0, "
6e05b22001-01-01Martin Nilsson  "atime INT UNSIGNED NOT NULL DEFAULT 0, "
9fcbed2001-01-20Per Hedbor  "contents BLOB NOT NULL DEFAULT '', " "INDEX hind (hash))");
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 = ([]);
2c0b612001-01-29Per Hedbor  db = master()->resolv( "DBManager.get" )( "shared" );
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 ); }
0e00082001-03-05Per Hedbor  static string read_args( int id )
2ff8461999-09-02Per Hedbor  {
6e05b22001-01-01Martin Nilsson  array res = db->query("SELECT contents FROM "+name+" WHERE id="+id);
a662d52000-12-31Per Hedbor  if( sizeof(res) )
2ff8461999-09-02Per Hedbor  {
6e05b22001-01-01Martin Nilsson  db->query("UPDATE "+name+" SET atime='"+time(1)+"' WHERE id="+id);
a662d52000-12-31Per Hedbor  return res[0]->contents;
2ff8461999-09-02Per Hedbor  } return 0; }
0e00082001-03-05Per Hedbor  static int create_key( string long_key )
2ff8461999-09-02Per Hedbor  {
de4a0c2001-03-16Per Hedbor  string hl = Crypto.md5()->update( long_key )->digest();
9fcbed2001-01-20Per Hedbor  array data = db->query("SELECT id,contents FROM "+name+" WHERE hash=%d",
de4a0c2001-03-16Per Hedbor  hash(long_key));
a662d52000-12-31Per Hedbor  foreach( data, mapping m ) if( m->contents == long_key ) return m->id;
2ff8461999-09-02Per Hedbor 
9fcbed2001-01-20Per Hedbor  db->query( "INSERT INTO "+name+" (contents,hash,atime) VALUES " "(%s,%d,UNIX_TIMESTAMP())",
de4a0c2001-03-16Per Hedbor  long_key, hash(long_key) );
3e2e092001-03-06Per Hedbor  int id = (int)db->master_sql->insert_id();
b7914f2001-03-06Per Hedbor  if( !plugins ) get_plugins();
3e2e092001-03-06Per Hedbor  (plugins->create_key-({0}))( id, hl, long_key ); return id;
2ff8461999-09-02Per Hedbor  }
387fa62001-03-05Per Hedbor  static int low_key_exists( string key ) { return sizeof( db->query( "SELECT id FROM "+name+" WHERE id="+(int)key)); } static string secret; static void ensure_secret() { if( !secret ) secret = query( "argcache_secret" ); }
0e00082001-03-05Per Hedbor  static string encode_id( int a, int b )
387fa62001-03-05Per Hedbor  { ensure_secret(); object crypto = Crypto.arcfour(); crypto->set_encrypt_key( secret ); string res = crypto->crypt( a+"×"+b ); res = Gmp.mpz( res, 256 )->digits( 36 ); return res; }
3e2e092001-03-06Per Hedbor  static array plugins; static void get_plugins() { plugins = ({}); foreach( ({ "../local/arg_cache_plugins", "arg_cache_plugins" }), string d) if( file_stat( d ) ) foreach( get_dir( d ), string f ) plugins += ({ (object)(d+"/"+f) }); } static array plugin_decode_id( string id ) { if( !plugins ) get_plugins(); mixed r; foreach( (plugins->decode_id-({0})), function(string:array(int)) f ) if( r = f( id ) ) return r; return 0; }
2ff8461999-09-02Per Hedbor 
387fa62001-03-05Per Hedbor  static array decode_id( string a ) { ensure_secret(); object crypto = Crypto.arcfour(); crypto->set_encrypt_key( secret ); a = Gmp.mpz( a, 36 )->digits( 256 ); a = crypto->crypt( a ); int i, j; if( sscanf( a, "%d×%d", i, j ) != 2 )
3e2e092001-03-06Per Hedbor  return plugin_decode_id( a );
0e00082001-03-05Per Hedbor  return ({ i, j });
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);
387fa62001-03-05Per Hedbor  string id = encode_id( low_store( a ), low_store( b ) );
10bb982001-03-16Per Hedbor  if( !cache[ id ] ) cache[ id ] = args+([]);
387fa62001-03-05Per Hedbor  return id; }
2ff8461999-09-02Per Hedbor 
0e00082001-03-05Per Hedbor  static int low_store( array a )
387fa62001-03-05Per Hedbor  {
10bb982001-03-16Per Hedbor  string data = encode_value_canonic( a );
de4a0c2001-03-16Per Hedbor  string hv = Crypto.md5()->update( data )->digest();
0e00082001-03-05Per Hedbor  if( mixed q = cache[ hv ] ) return q;
ddb5a72001-02-05Per Hedbor  LOCK();
0e00082001-03-05Per Hedbor #ifdef THREADS if( mixed q = cache[ hv ] ) return q; #endif
8693b22000-01-08Martin Stjernholm  if( sizeof( cache ) >= CACHE_SIZE ) { array i = indices(cache); while( sizeof(cache) > CACHE_SIZE-CLEAN_SIZE ) { string idx=i[random(sizeof(i))];
0e00082001-03-05Per Hedbor  m_delete( cache, cache[idx] ); m_delete( cache, idx );
8693b22000-01-08Martin Stjernholm  } }
0e00082001-03-05Per Hedbor  int id = create_key( data );
3e2e092001-03-06Per Hedbor  if( !plugins ) get_plugins();
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 ); if( !i ) {
3e2e092001-03-06Per Hedbor  mixed res; if( !plugins ) get_plugins(); foreach( (plugins->lookup-({0})), function f ) if( res = f( id ) ) return res;
387fa62001-03-05Per Hedbor  return 0; } array a = low_lookup( i[0] ); array b = low_lookup( i[1] ); if( a && b ) return (cache[id] = mkmapping( a, b ))+([]); }
0e00082001-03-05Per Hedbor  static 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 ) { mixed res; if( !plugins ) get_plugins(); foreach( (plugins->low_lookup-({0})), function f ) if( res = f( id ) ) return res; 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  {
3e2e092001-03-06Per Hedbor  if(!plugins) get_plugins(); (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 ); }
0e00082001-03-05Per Hedbor  db->query( "DELETE FROM "+name+" WHERE id="+id );
2ff8461999-09-02Per Hedbor  }
69a8691999-05-18Per Hedbor  }
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
4ee4942000-07-21Andreas Lange  __REG_PROJ("roxen_start", "translations/%L/roxen_start.xml"); __REG_PROJ("roxen_config", "translations/%L/roxen_config.xml");
23414a2000-07-21Andreas Lange  __REG_PROJ("roxen_message", "translations/%L/roxen_message.xml");
c12fcc2001-01-01Martin Nilsson  __REG_PROJ("admin_tasks", "translations/%L/admin_tasks.xml"); 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 
970e242000-09-12Per Hedbor  // This is currently needed to resolve the circular references in // RXML.pmod correctly. :P
dbfe9d2000-04-13Per Hedbor  master()->resolv ("RXML.refs"); master()->resolv ("RXML.PXml"); master()->resolv ("RXML.PEnt");
74e3902000-12-30Per Hedbor  foreach(({ "module.pmod","PEnt.pike", "PExpr.pike","PXml.pike", "refs.pmod","utils.pmod" }), string q ) dump( "etc/modules/RXML.pmod/"+ q );
8514832000-11-27Per Hedbor  dump( "etc/modules/RXML.pmod/module.pmod" ); // Already loaded. No delayed dump possible.
a40c152000-02-16Per Hedbor  dump( "etc/roxen_master.pike" );
27008b2000-03-20Martin Stjernholm  dump( "etc/modules/Roxen.pmod" );
970e242000-09-12Per Hedbor  dump( "base_server/config_userdb.pike" );
cde8d62000-02-16Per Hedbor  dump( "base_server/disk_cache.pike" );
2537c32000-02-14Per Hedbor  dump( "base_server/roxen.pike" );
6f72d42000-02-08Per Hedbor  dump( "base_server/basic_defvar.pike" );
2ff8461999-09-02Per Hedbor  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");
69a8691999-05-18Per Hedbor 
95b69e1999-09-05Per Hedbor #ifndef __NT__
640cc41999-11-29Per Hedbor  if(!getuid())
95b69e1999-09-05Per Hedbor  add_constant("Privs", Privs);
640cc41999-11-29Per Hedbor  else
95b69e1999-09-05Per Hedbor #endif /* !__NT__ */
eb6ce92000-01-03Martin Stjernholm  add_constant("Privs", class { void create(string reason, int|string|void uid, int|string|void gid) {} });
69a8691999-05-18Per Hedbor 
855c391999-11-29Per Hedbor 
2ff8461999-09-02Per Hedbor  // for module encoding stuff
d3b98e1999-11-28Per Hedbor 
6fa4d12001-02-23Per Hedbor  add_constant( "CFUserDBModule",config_userdb_module );
95b69e1999-09-05Per Hedbor  add_constant( "ArgCache", ArgCache );
27008b2000-03-20Martin Stjernholm  //add_constant( "roxen.load_image", load_image );
2ff8461999-09-02Per Hedbor 
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 
6051aa2001-02-23Per Hedbor  DDUMP( "etc/modules/DBManager.pmod");
6fa4d12001-02-23Per Hedbor  DDUMP( "base_server/roxenlib.pike"); DDUMP( "base_server/html.pike"); DDUMP( "etc/modules/Dims.pmod"); DDUMP( "config_interface/boxes/Box.pmod" );
855c391999-11-29Per Hedbor  add_constant( "RoxenModule", RoxenModule);
67c7491999-12-27Martin Stjernholm  add_constant( "ModuleInfo", ModuleInfo );
855c391999-11-29Per Hedbor 
95b69e1999-09-05Per Hedbor  add_constant( "load", load); add_constant( "Roxen.set_locale", set_locale );
55a8662000-11-20Per Hedbor  add_constant( "Roxen.get_locale", get_locale );
2537c32000-02-14Per Hedbor  add_constant( "roxen.locale", locale );
27008b2000-03-20Martin Stjernholm  //add_constant( "roxen.ImageCache", ImageCache );
2537c32000-02-14Per Hedbor 
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(); }
2ff8461999-09-02Per Hedbor int set_u_and_gid()
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 
8552d92001-01-13Martin Nilsson  u=query("User");
2ff8461999-09-02Per Hedbor  sscanf(u, "%s:%s", u, g); if(strlen(u)) { if(getuid()) {
49cd282000-08-11Andreas Lange  report_error(LOC_M(24, "It is only possible to change uid and gid "
c8ee712000-09-09Andreas Lange  "if the server is running as root.")+"\n");
2ff8461999-09-02Per Hedbor  } else { 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;
2ff8461999-09-02Per Hedbor  catch { mutex_key = euid_egid_lock->lock(); }; object threads_disabled = _disable_threads(); #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
c8ee712000-09-09Andreas Lange  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; } if (u) report_notice(CALL_M("setting_uid_gid_permanently", "eng")
1c783c2000-07-15Andreas Lange  (uid, gid, u, g));
2ff8461999-09-02Per Hedbor #else
c8ee712000-09-09Andreas Lange  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
c8ee712000-09-09Andreas Lange  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; } if (u) report_notice(CALL_M("setting_uid_gid", "eng")(uid, gid, u, g));
2ff8461999-09-02Per Hedbor #else
49cd282000-08-11Andreas Lange  report_warning(LOC_M(32, "Setting effective uid not supported on "
c8ee712000-09-09Andreas Lange  "this system.")+"\n");
2ff8461999-09-02Per Hedbor  u = g = 0; #endif
b84a161999-06-07Martin 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)); 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( "shared", conf, 2 ); 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. 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
9a8a152000-09-25Per Hedbor  Configuration cf = _configuration( name );
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"); shutdown();
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))) {
3ffe061999-05-16David Hedbor  file=Stdio.File();
c526921999-05-18Per Hedbor  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 ); }
a075f42000-11-21Per Hedbor array(Image.Layer) load_layers(string f, RequestID id, mapping|void opt)
d681842000-02-08Per Hedbor { string data; Stdio.File file; if(id->misc->_load_image_called < 5) { // We were recursing very badly with the demo module here... id->misc->_load_image_called++; if(!(data=id->conf->try_get_file(f, id))) { file=Stdio.File(); if(!file->open(f,"r") || !(data=file->read()))
8ace002000-09-19Martin Nilsson #ifdef THREADS
d681842000-02-08Per Hedbor  catch { data = Protocols.HTTP.get_url_nice( f )[1]; };
8ace002000-09-19Martin Nilsson #endif
d681842000-02-08Per Hedbor  if( !data ) return 0; } } id->misc->_load_image_called = 0; if(!data) return 0;
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() { array(mixed) all_backtraces; #if constant(all_threads) all_backtraces = all_threads()->backtrace(); #else /* !constant(all_threads) */ all_backtraces = ({ backtrace() }); #endif /* constant(all_threads) */
81f8af1999-12-20Martin Nilsson  report_debug("Describing all threads:\n");
1c9d081999-07-19Henrik Grubbström (Grubba)  int i; for(i=0; i < sizeof(all_backtraces); i++) {
81f8af1999-12-20Martin Nilsson  report_debug("Thread %d:\n" "%s\n", i+1, describe_backtrace(all_backtraces[i]));
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(); report_debug( "Initiating argument cache ... ");
a662d52000-12-31Per Hedbor  if( mixed e = catch( argcache = ArgCache("arguments") ) )
1d7d6d2000-02-16Per Hedbor  {
d77f9a2000-03-28Per Hedbor  report_fatal( "Failed to initialize the global argument cache:\n"
1d7d6d2000-02-16Per Hedbor  + (describe_backtrace( e )/"\n")[0]+"\n");
d77f9a2000-03-28Per Hedbor  exit(1);
1d7d6d2000-02-16Per Hedbor  } add_constant( "roxen.argcache", argcache ); report_debug("Done [%.2fms]\n", (gethrtime()-t)/1000.0); }
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
9eb8742001-05-16Per Hedbor // void show_avg_prof() // { // foreach(configurations, Configuration c ) // c->debug_write_prof( ); // call_out(show_avg_prof, 10 ); // }
4717052001-05-07Per Hedbor 
f109262001-01-31Per Hedbor array argv;
7339a02000-02-10Per Hedbor int main(int argc, array tmp)
b1fca01996-11-12Per Hedbor {
f109262001-01-31Per Hedbor  argv = tmp;
1e5cc42001-03-17Martin Stjernholm  tmp = 0;
9eb8742001-05-16Per Hedbor // #ifdef AVERAGE_PROFILING // call_out(show_avg_prof, 10 ); // #endif
4717052001-05-07Per Hedbor 
74e3902000-12-30Per Hedbor  slowpipe = ((program)"base_server/slowpipe"); fastpipe = ((program)"base_server/fastpipe"); dump( "base_server/slowpipe.pike" ); dump( "base_server/fastpipe.pike" );
8514832000-11-27Per Hedbor  dump( "base_server/throttler.pike" );
a577791999-11-24Per Hedbor 
b0a9ea2001-04-18Martin Stjernholm  if (!has_value (compat_levels, __roxen_version__)) report_debug ("Warning: The current version %s does not exist in " "roxen.compat_levels.\n", __roxen_version__);
0d0e952000-11-13Per Hedbor  add_constant( "Protocol", Protocol );
9c19002001-02-27Per Hedbor #ifdef TIMERS call_out( show_timers, 30 ); #endif
0d0e952000-11-13Per Hedbor #if constant(SSL.sslfile) add_constant( "SSLProtocol", SSLProtocol ); #endif
8514832000-11-27Per Hedbor  dump( "etc/modules/Variable.pmod/module.pmod" ); dump( "etc/modules/Variable.pmod/Language.pike" ); DDUMP( "base_server/state.pike" ); DDUMP( "base_server/highlight_pike.pike" ); DDUMP( "base_server/wizard.pike" ); DDUMP( "base_server/proxyauth.pike" ); DDUMP( "base_server/module.pike" ); DDUMP( "base_server/throttler.pike" );
753a831999-08-30Per Hedbor 
b1fca01996-11-12Per Hedbor  mark_fd(0, "Stdin"); mark_fd(1, "Stdout"); mark_fd(2, "Stderr");
51643e1997-08-21Per Hedbor  configuration_dir =
6ca8f61998-10-13Per Hedbor  Getopt.find_option(argv, "d",({"config-dir","configuration-directory" }),
51643e1997-08-21Per Hedbor  ({ "ROXEN_CONFIGDIR", "CONFIGURATIONS" }), "../configurations");
b1fca01996-11-12Per Hedbor 
a92b951997-08-05Martin Stjernholm  if(configuration_dir[-1] != '/')
b1fca01996-11-12Per Hedbor  configuration_dir += "/";
08eba62000-07-09Per Hedbor  restore_global_variables(); // restore settings...
14179b1997-01-29Per Hedbor  // Dangerous...
d6df352000-11-14Martin Nilsson  mixed tmp_root; if(tmp_root = Getopt.find_option(argv, "r", "root")) fix_root(tmp_root);
b1fca01996-11-12Per Hedbor  argv -= ({ 0 });
51643e1997-08-21Per Hedbor  argc = sizeof(argv);
b1fca01996-11-12Per Hedbor 
c6fd2e2000-09-03Per Hedbor  add_constant( "roxen.fonts", (fonts = ((program)"base_server/fonts.pike")()) );
8514832000-11-27Per Hedbor  DDUMP( "languages/abstract.pike" );
48884e2000-07-31Martin Nilsson  initiate_languages(query("locale"));
8514832000-11-27Per Hedbor 
48884e2000-07-31Martin Nilsson  set_locale();
67f60e2000-07-09Martin Nilsson 
b1fca01996-11-12Per Hedbor #if efun(syslog) init_logger(); #endif init_garber();
d649ad2000-12-30Per Hedbor 
b1fca01996-11-12Per Hedbor  initiate_supports();
1d7d6d2000-02-16Per Hedbor  initiate_argcache();
970e242000-09-12Per Hedbor  init_configuserdb();
e83c432001-03-11Martin Nilsson  cache.init_session_cache();
d649ad2000-12-30Per Hedbor 
0d0e952000-11-13Per Hedbor  protocols = build_protocols_mapping();
0764862001-03-16Per Hedbor  int t = gethrtime(); werror("Searching for pike-modules directories ... "); foreach( find_all_pike_module_directories( ), string d ) master()->add_module_path( d ); werror(" Done [%dms]\n", (gethrtime()-t)/1000 );
b84a161999-06-07Martin Stjernholm  enable_configurations();
95b69e1999-09-05Per Hedbor  set_u_and_gid(); // Running with the right [e]uid:[e]gid from this point on.
b84a161999-06-07Martin Stjernholm 
a3a0ec2001-03-17Martin Stjernholm  create_pid_file(Getopt.find_option(argv, "p", "pid-file"));
b84a161999-06-07Martin Stjernholm 
c30d322000-11-11Martin Nilsson #ifdef RUN_SELF_TEST enable_configurations_modules(); #else if( Getopt.find_option( argv, 0, "no-delayed-load" ))
1766be2000-02-16Per Hedbor  enable_configurations_modules();
11f1922000-02-16Per Hedbor  else
d093992000-09-25Per Hedbor  foreach( configurations, Configuration c )
11f1922000-02-16Per Hedbor  if( c->query( "no_delayed_load" ) ) c->enable_all_modules();
c30d322000-11-11Martin Nilsson #endif // RUN_SELF_TEST
81f8af1999-12-20Martin Nilsson 
48fa361997-04-05Per Hedbor #ifdef THREADS start_handler_threads();
34d3fa1999-04-22Per Hedbor  backend_thread = this_thread();
6664122001-02-23Per Hedbor  name_thread( backend_thread, "Backend" );
34fbbc1998-02-05Henrik Grubbström (Grubba) #endif /* THREADS */
990cbb1997-08-12David Hedbor 
3835ca1998-01-16Henrik Grubbström (Grubba)  // Signals which cause a restart (exitcode != 0)
15635b1999-10-10Per Hedbor  foreach( ({ "SIGINT", "SIGTERM" }), string sig)
6037992000-12-13Per Hedbor  catch( signal(signum(sig), async_sig_start(exit_when_done,0)) );
15635b1999-10-10Per Hedbor 
6037992000-12-13Per Hedbor  catch(signal(signum("SIGHUP"),async_sig_start(reload_all_configurations,1)));
15635b1999-10-10Per Hedbor 
1c9d081999-07-19Henrik Grubbström (Grubba)  // Signals which cause Roxen to dump the thread state
8ff2aa2000-12-13Per Hedbor  foreach( ({ "SIGQUIT", "SIGUSR1", "SIGUSR2", "SIGTRAP" }), string sig) catch( signal(signum(sig),async_sig_start(describe_all_threads,-1)));
0f28da1997-08-13Per Hedbor 
08152b1998-04-24Per Hedbor  start_time=time(); // Used by the "uptime" info later on.
4cf7832000-02-16Per Hedbor 
dbfe9d2000-04-13Per Hedbor 
8552d92001-01-13Martin Nilsson  if (query("suicide_engage")) call_out (restart,60*60*24*max(1,query("suicide_timeout")));
dbfe9d2000-04-13Per Hedbor #ifndef __NT__
4cf7832000-02-16Per Hedbor  restart_if_stuck( 0 );
f603962000-12-13Per Hedbor #endif #ifdef __RUN_TRACE trace(1);
dbfe9d2000-04-13Per Hedbor #endif
b1fca01996-11-12Per Hedbor  return -1; }
7a61de1998-03-26Per Hedbor 
a6ef1f2000-03-28Johan Sundström // Called from the administration interface.
ee8b201998-07-13David Hedbor string check_variable(string name, mixed value)
edc9af1998-07-11David Hedbor { switch(name) {
dbfe9d2000-04-13Per Hedbor #ifndef __NT__
ee8b201998-07-13David Hedbor  case "abs_engage": if (value) restart_if_stuck(1);
81f8af1999-12-20Martin Nilsson  else
ee8b201998-07-13David Hedbor  remove_call_out(restart_if_stuck); break;
dbfe9d2000-04-13Per Hedbor #endif
ee8b201998-07-13David Hedbor  case "suicide_engage":
81f8af1999-12-20Martin Nilsson  if (value)
8552d92001-01-13Martin Nilsson  call_out(restart,60*60*24*max(1,query("suicide_timeout")));
ee8b201998-07-13David Hedbor  else remove_call_out(restart); break;
edc9af1998-07-11David Hedbor  } }
9fd61c1999-02-16Per Hedbor  int is_ip(string s) {
1f58d02000-01-02Martin Nilsson  return (sscanf(s,"%*d.%*d.%*d.%*d")==4 && s[-1]>47 && s[-1]<58);
9fd61c1999-02-16Per Hedbor }
8b64e82000-01-03Martin Nilsson 
a6e4a12000-07-09Per Hedbor static string _sprintf( ) { return "roxen"; }
f3ca762000-08-19Per Hedbor  // Support for logging in configurations and modules. class LogFormat { static string host_ip_to_int(string s) { int a, b, c, d; sscanf(s, "%d.%d.%d.%d", a, b, c, d); return sprintf("%c%c%c%c",a, b, c, d); } static string extract_user(string from) { array tmp; if (!from || sizeof(tmp = from/":")<2) return "-"; return tmp[0]; // username only, no password } void log( function do_write, RequestID id, mapping file ); static void do_async_write( string host, string data, string ip, function c ) {
3e02742000-08-23Per Hedbor  if( c ) c( replace( data, "\4711", (host||ip) ) );
f3ca762000-08-19Per Hedbor  } }
f683652000-08-19Per Hedbor static mapping(string:function) compiled_formats = ([ ]);
f3ca762000-08-19Per Hedbor  constant formats = ({
ad092a2000-08-21Per Hedbor  ({ "ip_number", "%s", "(string)request_id->remoteaddr",0 }),
f3ca762000-08-19Per Hedbor  ({ "bin-ip_number","%s", "host_ip_to_int(request_id->remoteaddr)",0 }), ({ "cern_date", "%s", "Roxen.cern_http_date( time( 1 ) )",0 }), ({ "bin-date", "%4c", "time(1)",0 }),
ad092a2000-08-21Per Hedbor  ({ "method", "%s", "(string)request_id->method",0 }), ({ "resource", "%s", "(string)(request_id->raw_url||request_id->not_query)", 0 }), ({ "full_resource","%s", "(string)(request_id->raw_url||request_id->not_query)",0 }), ({ "protocol", "%s", "(string)request_id->prot",0 }), ({ "response", "%d", "(int)(file->error || 200)",0 }), ({ "bin-response","%2c", "(int)(file->error || 200)",0 }), ({ "length", "%d", "(int)file->len",0 }), ({ "bin-length", "%4c", "(int)file->len",0 }),
f034ab2001-04-17Per Hedbor  ({ "vhost", "%s", "(request_id->misc->host||\"-\")", 0 }),
f3ca762000-08-19Per Hedbor  ({ "referer", "%s",
370e542000-12-20Anders Johansson  "sizeof(request_id->referer||({}))?request_id->referer[0]:\"-\"", 0 }),
f3ca762000-08-19Per Hedbor  ({ "user_agent", "%s",
42c2092000-11-16Per Hedbor  "request_id->client?request_id->client*\"%20\":\"-\"", 0 }),
b613482001-01-31Per Hedbor  ({ "user_id", "%s", "(request_id->cookies&&request_id->cookies->RoxenUserID)||\"0\"",0 }),
f3ca762000-08-19Per Hedbor  ({ "user", "%s", "extract_user( request_id->realauth )",0 }),
77c7e92000-11-02Peter J. Holzer  ({ "request-time","%1.2f", "time(request_id->time )",0 }),
f3ca762000-08-19Per Hedbor  ({ "host", "\4711", 0, 1 }), // unlikely to occur normally });
f683652000-08-19Per Hedbor void run_log_format( string fmt, function c, RequestID id, mapping file ) { (compiled_formats[ fmt ] || compile_log_format( fmt ))(c,id,file); } function compile_log_format( string fmt )
f3ca762000-08-19Per Hedbor {
e461842000-08-19Per Hedbor  if( compiled_formats[ fmt ] ) return compiled_formats[ fmt ];
f3ca762000-08-19Per Hedbor  array parts = fmt/"$"; string format = parts[0]; array args = ({}); int do_it_async = 0; int add_nl = 1; string sr( string s ) { return s[1..strlen(s)-2]; }; // sr(replace(sprintf("%O", X), "%", "%%")) #define DO_ES(X) replace(X, ({"\\n", "\\r", "\\t", }), ({ "\n", "\r", "\t" }) ) foreach( parts[1..], string part ) { int c, processed; foreach( formats, array q ) if( part[..strlen(q[0])-1] == q[0]) { format += q[1] + DO_ES(part[ strlen(q[0]) .. ]); if( q[2] ) args += ({ q[2] }); if( q[3] ) do_it_async = 1; processed=1; break; } if( processed ) continue; if( sscanf( part, "char(%d)%s", c, part ) ) format += sprintf( "%"+(c<0?"-":"")+"c", abs( c ) )+DO_ES(part); else if( sscanf( part, "wchar(%d)%s", c, part ) ) format += sprintf( "%"+(c<0?"-":"")+"2c", abs( c ) )+DO_ES(part); else if( sscanf( part, "int(%d)%s", c, part ) ) format += sprintf( "%"+(c<0?"-":"")+"4c", abs( c ) )+DO_ES(part); else if( part[0] == '^' ) { format += DO_ES(part[1..]); add_nl = 0; } else format += "$"+part; } if( add_nl ) format += "\n"; add_constant( "___LogFormat", LogFormat ); string code = sprintf( #" inherit ___LogFormat;
3e3bab2001-01-19Per Hedbor  void log( function callback, RequestID request_id, mapping file )
f3ca762000-08-19Per Hedbor  {
3e02742000-08-23Per Hedbor  if(!callback) return;
f3ca762000-08-19Per Hedbor  string data = sprintf( %O %{, %s%} ); ", format, args ); if( do_it_async ) { code += #" roxen.ip_to_host(request_id->remoteaddr,do_async_write, data, request_id->remoteaddr, callback ); } "; } else code += #" callback( data ); } ";
f683652000-08-19Per Hedbor  return compiled_formats[ fmt ] = compile_string( code )()->log;
f3ca762000-08-19Per Hedbor }
bc0fa02001-03-08Per Hedbor array security_checks = ({ "ip=%s:%s",2,({ lambda( string a, string b ){ int net = Roxen.ip_to_int( a ); int mask = Roxen.ip_to_int( b ); net &= mask; return ({ net, sprintf("%c",mask)[0] }); }, " if( (Roxen.ip_to_int( id->remoteaddr ) & %[1]d) == %[0]d ) ", }), "ip=%s/%d",2,({ lambda( string a, int b ){ int net = Roxen.ip_to_int( a ); int mask = ((~0<<(32-b))&0xffffffff); net &= mask; return ({ net, sprintf("%c",mask)[0] }); }, " if( (Roxen.ip_to_int( id->remoteaddr ) & %[1]d) == %[0]d ) ", }), "ip=%s",1,({
f91a1a2001-03-08Per Hedbor  " if( sizeof(filter(%[0]O/\",\",lambda(string q){\n" " return glob(q,id->remoteaddr);\n" " })) )"
bc0fa02001-03-08Per Hedbor  }),
f91a1a2001-03-08Per Hedbor  "user=%s",1,({ 1,
bc0fa02001-03-08Per Hedbor  lambda( string x ) { return ({sprintf("(< %{%O, %}>)", x/"," )}); }, " if( (user || (user = authmethod->authenticate( id, userdb_module )))\n" " && ((%[0]s->any) || (%[0]s[user->name()])) ) ", (< " User user" >), }),
f91a1a2001-03-08Per Hedbor  "group=%s",1,({ 1,
bc0fa02001-03-08Per Hedbor  lambda( string x ) { return ({sprintf("(< %{%O, %}>)", x/"," )}); }, " if( (user || (user = authmethod->authenticate( id, userdb_module )))\n" " && ((%[0]s->any) || sizeof(mkmultiset(user->groups())&%[0]s)))", (<" User user" >), }), "dns=%s",1,({
f91a1a2001-03-08Per Hedbor  " if(!dns && \n" " ((dns=roxen.quick_ip_to_host(id->remoteaddr))!=id->remoteaddr))\n" " if( (id->misc->delayed+=0.1) < 1.0 )\n" " return http_try_again_later( 0.1 );\n" " if( sizeof(filter(%[0]O/\",\",lambda(string q){return glob(q,dns);})) )", (< " string dns" >),
bc0fa02001-03-08Per Hedbor  }), "time=%d:%d-%d:%d",4,({
f91a1a2001-03-08Per Hedbor  (< " mapping l = localtime(time(1))" >), (< " int th = l->hour, tm = l->min" >), " if( ((th >= %[0]d) && (tm >= %[1]d)) &&\n" " ((th <= %[2]d) && (tm <= %[3]d)) )", }),
c0db6e2001-04-24Per Hedbor  "referer=%s", 1, ({ (< " string referer = sizeof(request_id->referer||({}))?" "request_id->referer[0]:\"\"; " >), " if( sizeof(filter(%[0]O/\",\",lambda(string q){\n" " return glob(q,referer);\n" " })) )" }),
f91a1a2001-03-08Per Hedbor  "day=%s",1,({ lambda( string q ) { multiset res = (<>); foreach( q/",", string w ) if( (int)w ) res[((int)w % 7)] = 1; else res[ (["monday":1,"thuesday":2,"wednesday":3,"thursday":4,"friday":5, "saturday":6,"sunday":0,"mon":1, "thu":2, "wed":3, "thu":4, "fri":5, "sat":6, "sun":0, ])[ lower_case(w) ] ] = 1; return ({sprintf("(< %{%O, %}>)", (array)res)}); }, (< " mapping l = localtime(time(1))" >), " if( %[0]s[l->wday] )"
bc0fa02001-03-08Per Hedbor  }), });
f91a1a2001-03-08Per Hedbor #define DENY 0 #define ALLOW 1
bc0fa02001-03-08Per Hedbor  function(RequestID:mapping|int) compile_security_pattern( string pattern, RoxenModule m )
3e6f6b2001-03-11Martin Nilsson //! Parse a security pattern and return a function that when called //! will do the checks required by the format. //! //! The syntax is: //! //! userdb userdatabase module //! authmethod authentication module //! realm realm name //! //! Below, CMD is one of 'allow' and 'deny' //! //! CMD ip=ip/bits[,ip/bits] [return] //! CMD ip=ip:mask[,ip:mask] [return] //! CMD ip=pattern [return] //! //! CMD user=name[,name,...] [return] //! CMD group=name[,name,...] [return] //! //! CMD dns=pattern [return]
c0db6e2001-04-24Per Hedbor //! //! CMD day=pattern [return]
3e6f6b2001-03-11Martin Nilsson //! //! CMD time=<start>-<stop> [return] //! times in HH:mm format //!
c0db6e2001-04-24Per Hedbor //! CMD referer=pattern [return] //! Check the referer header. //!
3e6f6b2001-03-11Martin Nilsson //! pattern is a glob pattern. //! //! return means that reaching this command results in immediate //! return, only useful for 'allow'. //! //! 'deny' always implies a return, no futher testing is done if a //! 'deny' match.
bc0fa02001-03-08Per Hedbor { string code = "";
f91a1a2001-03-08Per Hedbor  array variables = ({ " object userdb_module", " object authmethod = id->conf", " string realm = \"User\"", " int|mapping fail" });
bc0fa02001-03-08Per Hedbor  int shorted, patterns, cmd; foreach( pattern / "\n", string line ) { line = String.trim_all_whites( line ); if( !strlen(line) || line[0] == '#' ) continue; sscanf( line, "%[^#]#", line ); if( sscanf( line, "allow %s", line ) ) cmd = ALLOW; else if( sscanf( line, "deny %s", line ) ) cmd = DENY; else if( sscanf( line, "userdb %s", line ) ) { line = String.trim_all_whites( line ); if( line == "config_userdb" ) code += " userdb_module = roxen.config_userdb_module;\n"; else if( line == "all" ) code += " userdb_module = 0;\n"; else if( !m->my_configuration()->find_user_database( line ) )
89d1d82001-05-16Martin Nilsson  m->report_notice( LOC_M( 58,"Syntax error in security patterns: "
bc0fa02001-03-08Per Hedbor  "Cannot find the user database '"+ line+"'\n" )); else code += sprintf(" userdb_module = id->conf->find_user_database( %O );\n", line); continue; } else if( sscanf( line, "authmethod %s", line ) ) { line = String.trim_all_whites( line ); if( line == "all" ) code += " authmethod = id->conf;\n"; else if( !m->my_configuration()->find_auth_module( line ) )
89d1d82001-05-16Martin Nilsson  m->report_notice( LOC_M( 59,"Syntax error in security patterns: "
bc0fa02001-03-08Per Hedbor  "Cannot find the auth method '"+ line+"'\n" )); else code += sprintf(" authmethod = id->conf->find_auth_module( %O );\n", line); continue; } else if( sscanf( line, "realm %s", line ) ) { line = String.trim_all_whites( line ); code += sprintf( " realm = %O;\n", line ); } else
89d1d82001-05-16Martin Nilsson  m->report_notice( LOC_M( 60,"Syntax error in security patterns: "
bc0fa02001-03-08Per Hedbor  "Expected 'allow' or 'deny'\n" )); shorted = sscanf( line, "%s return", line ); for( int i = 0; i<sizeof( security_checks ); i+=3 ) { string check = security_checks[i]; array args; if( sizeof( args = array_sscanf( line, check ) ) == security_checks[i+1] ) { patterns++; int thr; // run instructions. foreach( security_checks[ i+2 ], mixed instr ) { if( functionp( instr ) ) args = instr( @args ); else if( multisetp( instr ) )
f91a1a2001-03-08Per Hedbor  { foreach( (array)instr, string v ) if( !has_value( variables, v ) ) variables += ({ v }); }
bc0fa02001-03-08Per Hedbor  else if( intp( instr ) ) thr = instr; else if( stringp( instr ) ) { code += sprintf( instr, @args )+"\n"; if( cmd == DENY ) { if( thr ) code += " return authmethod->authenticate_throw( id, realm );\n"; else code += " return 1;\n"; } else { if( shorted ) { code += " return fail;\n"; code += " else\n"; if( thr ) code += " return authmethod->authenticate_throw( id, realm );\n"; else code += " return fail || 1;\n"; } else { code += " ;\n"; code += " else\n"; if( thr ) code += " fail=authmethod->authenticate_throw( id, realm );\n"; else code += " if( !fail ) fail=1;\n"; } } } } break; } } }
f91a1a2001-03-08Per Hedbor  if( !patterns ) return 0; return compile_string( "int|mapping f( RequestID id )\n" "{\n" +variables *";\n" + ";\n" "" + code + " return fail;\n}\n" )()->f;
bc0fa02001-03-08Per Hedbor }
f3ca762000-08-19Per Hedbor  static string cached_hostname = gethostname();
87dee22000-09-24Henrik Grubbström (Grubba) class LogFile(string fname)
f3ca762000-08-19Per Hedbor { Stdio.File fd; int opened;
87dee22000-09-24Henrik Grubbström (Grubba) 
f3ca762000-08-19Per Hedbor  void do_open() { mixed parent; if (catch { parent = function_object(object_program(this_object())); } || !parent) { // Our parent (aka the configuration) has been destructed. // Time to die. remove_call_out(do_open); remove_call_out(do_close); destruct(); return; } string ff = fname; mapping m = localtime(time(1)); m->year += 1900; // Adjust for years being counted since 1900 m->mon++; // Adjust for months being counted 0-11 if(m->mon < 10) m->mon = "0"+m->mon; if(m->mday < 10) m->mday = "0"+m->mday; if(m->hour < 10) m->hour = "0"+m->hour; ff = replace(fname,({"%d","%m","%y","%h", "%H" }), ({ (string)m->mday, (string)(m->mon), (string)(m->year),(string)m->hour, cached_hostname, })); mkdirhier( ff ); fd = open( ff, "wac" ); if(!fd) { remove_call_out( do_open ); call_out( do_open, 120 ); report_error(LOC_M(37, "Failed to open logfile")+" "+fname+" " #if constant(strerror) "(" + strerror(errno()) + ")" #endif "\n"); return; } opened = 1; remove_call_out( do_open );
e461842000-08-19Per Hedbor  call_out( do_open, 900 );
f3ca762000-08-19Per Hedbor  } void do_close() { destruct( fd ); opened = 0; }
e461842000-08-19Per Hedbor  array(string) write_buf = ({}); static void do_the_write( )
f3ca762000-08-19Per Hedbor  { if( !opened ) do_open(); if( !opened ) return 0;
e461842000-08-19Per Hedbor  fd->write( write_buf ); write_buf = ({});
f3ca762000-08-19Per Hedbor  remove_call_out( do_close ); call_out( do_close, 10.0 );
e461842000-08-19Per Hedbor  } int write( string what ) { if( !sizeof( write_buf ) ) call_out( do_the_write, 1 ); write_buf += ({what}); return strlen(what);
f3ca762000-08-19Per Hedbor  } }