24c6c12000-02-20Martin Nilsson // The Roxen Webserver main program. // Copyright © 1996 - 2000, Roxen IS. //
4c12d72000-12-10Per Hedbor // Per Hedbor, Henrik Grubbstrm, Pontus Hagland, David Hedbor and others.
edc9af1998-07-11David Hedbor  // ABS and suicide systems contributed freely by Francesco Chemolli
4c12d72000-12-10Per Hedbor constant cvs_version="$Id: roxen.pike,v 1.583 2000/12/10 02:05:33 per Exp $";
34d3fa1999-04-22Per Hedbor 
8fb5172000-05-27Per Hedbor // Used when running threaded to find out which thread is the backend thread, // for debug purposes only.
34d3fa1999-04-22Per Hedbor object backend_thread;
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>
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 
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
bfb4d41999-12-28Martin Nilsson # define SSL3_WERR(X) werror("SSL3: "+X+"\n")
81f8af1999-12-20Martin Nilsson #else
bfb4d41999-12-28Martin Nilsson # define SSL3_WERR(X)
81f8af1999-12-20Martin Nilsson #endif #ifdef THREAD_DEBUG
bfb4d41999-12-28Martin Nilsson # define THREAD_WERR(X) werror("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 }
0f8b2f1999-03-27Henrik Grubbström (Grubba) #ifdef THREADS // 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 werror(sprintf("Privs(%O, %O, %O)\n" "privs_level: %O\n", reason, uid, gid, privs_level)); #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 werror(sprintf("Privs->destroy()\n" "privs_level: %O\n", privs_level)); #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) {
81f8af1999-12-20Martin Nilsson  werror("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) {
81f8af1999-12-20Martin Nilsson  werror("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. */ static object PRIVS(string r, int|string|void u, int|string|void g) { 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)
fb7ef91998-11-02Per Hedbor object 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 
8fb5172000-05-27Per Hedbor // When true, roxen will shut down as soon as possible. local static int die_die_die;
506ede1997-11-11Henrik Grubbström (Grubba) 
1720541998-10-04Henrik Grubbström (Grubba) // Function that actually shuts down Roxen. (see low_shutdown).
a9d8111998-09-01Henrik Grubbström (Grubba) private static void really_low_shutdown(int exit_code) {
8fb5172000-05-27Per Hedbor  // Die nicely. Catch for paranoia reasons
3107101998-09-01Marcus Comstedt #ifdef THREADS
c974c91999-06-27Per Hedbor  catch( stop_handler_threads() );
3107101998-09-01Marcus Comstedt #endif /* THREADS */
8fb5172000-05-27Per Hedbor  exit( exit_code ); // Now we die...
a9d8111998-09-01Henrik Grubbström (Grubba) }
3a4b9a1999-12-27Martin Nilsson 
a9d8111998-09-01Henrik Grubbström (Grubba) // Shutdown Roxen // exit_code = 0 True shutdown // exit_code = -1 Restart private static void low_shutdown(int exit_code) {
81f8af1999-12-20Martin Nilsson  catch
c5e0961999-10-04Per Hedbor  { configurations->stop(); int pid; if (exit_code) {
81f8af1999-12-20Martin Nilsson  report_debug("Restarting Roxen.\n");
c5e0961999-10-04Per Hedbor  } else {
81f8af1999-12-20Martin Nilsson  report_debug("Shutting down Roxen.\n");
c5e0961999-10-04Per Hedbor  // exit(0); } };
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.
81f8af1999-12-20Martin Nilsson void restart(float|void i)
a6e4a12000-07-09Per Hedbor //! Restart roxen, if the start script is running
81f8af1999-12-20Martin Nilsson {
23264c2000-10-17Per Hedbor  werror(describe_backtrace(backtrace()));
81f8af1999-12-20Martin Nilsson  call_out(low_shutdown, i, -1); }
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 
4820e01999-07-27Henrik Grubbström (Grubba) /* * handle() stuff */
2ff8461999-09-02Per Hedbor #ifndef THREADS
1720541998-10-04Henrik Grubbström (Grubba) // handle function used when THREADS is not enabled.
8fb5172000-05-27Per Hedbor local static void unthreaded_handle(function f, mixed ... args)
3aaaa71997-06-12Wilhelm Köhler { f(@args); }
34fbbc1998-02-05Henrik Grubbström (Grubba) function handle = unthreaded_handle;
2ff8461999-09-02Per Hedbor #else
7277a11999-09-02Per Hedbor function handle = threaded_handle;
2ff8461999-09-02Per Hedbor #endif
34fbbc1998-02-05Henrik Grubbström (Grubba) 
1720541998-10-04Henrik Grubbström (Grubba) /* * THREADS code starts here */
34fbbc1998-02-05Henrik Grubbström (Grubba) #ifdef THREADS
4f4bc11998-02-04Per Hedbor object do_thread_create(string id, function f, mixed ... args) { object t = thread_create(f, @args); catch(t->set_name( id ));
bfb4d41999-12-28Martin Nilsson  THREAD_WERR(id+" started");
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; } 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(); } }
4c12d72000-12-10Per Hedbor #if constant(thread_create) function async_sig_start( function f ) { return lambda( mixed ... args ) { thread_create( f, @args ); }; } #else function async_sig_start( function f ) { // call_out is not really useful here, since we probably want to run // the signal handler immediately, not whenever the backend thread // is available. return f; } #endif
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;
a6e4a12000-07-09Per Hedbor //! Number of handler threads that are alive.
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 {
4f4bc11998-02-04Per Hedbor  array (mixed) h, q;
34d3fa1999-04-22Per Hedbor  while(!die_die_die)
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");
3107101998-09-01Marcus Comstedt  if((h=handle_queue->read()) && h[0]) {
bfb4d41999-12-28Martin Nilsson  THREAD_WERR(sprintf("Handle thread [%O] calling %O(@%O)...",
81f8af1999-12-20Martin Nilsson  id, h[0], h[1..]));
67f60e2000-07-09Martin Nilsson  set_locale();
45cae31998-03-06Henrik Grubbström (Grubba)  h[0](@h[1]); h=0;
3107101998-09-01Marcus Comstedt  } else if(!h) { // Roxen is shutting down.
81f8af1999-12-20Martin Nilsson  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)  } } 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;}) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(5, "Uncaught error in handler thread: %s Client" "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 }
8fb5172000-05-27Per Hedbor local static void threaded_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.
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() {
a60c4c1997-07-03Henrik Grubbström (Grubba)  if (QUERY(numthreads) <= 1) {
7b798d2000-07-04Per Hedbor  set( "numthreads", 1 );
c8ee712000-09-09Andreas Lange  report_notice (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",
23414a2000-07-21Andreas Lange  QUERY(numthreads) );
a60c4c1997-07-03Henrik Grubbström (Grubba)  }
f526692000-05-16Henrik Grubbström (Grubba)  array(object) new_threads = ({});
14179b1997-01-29Per Hedbor  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 
3107101998-09-01Marcus Comstedt void stop_handler_threads()
434bac2000-07-14Andreas Lange //! Stop all the handler threads, 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");
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 = ({});
3107101998-09-01Marcus Comstedt  while(thread_reap_cnt) {
f526692000-05-16Henrik Grubbström (Grubba)  sleep(0.1);
3107101998-09-01Marcus Comstedt  if(--timeout<=0) {
81f8af1999-12-20Martin Nilsson  report_debug("Giving up waiting on threads!\n");
3107101998-09-01Marcus Comstedt  return; } } }
49284b2000-02-17Per Hedbor #endif /* THREADS */
81f8af1999-12-20Martin Nilsson 
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"; variables = ([]); 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  { object q = accept( );
8fb5172000-05-27Per Hedbor  if( q )
1550aa2000-08-12Per Hedbor  {
8514832000-11-27Per Hedbor  if( !requesthandler ) { requesthandler = (program)(rrhf); }
1550aa2000-08-12Per Hedbor  object 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 object ctx; class destruct_protected_sslfile { object sslfile; 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)  } void create(object q, object ctx) { sslfile = SSL.sslfile(q, ctx); } } object accept() { object q = ::accept();
2dd46b2000-03-24Per Hedbor  if (q) return destruct_protected_sslfile(q, ctx);
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();
df36c61999-10-08Henrik Grubbström (Grubba)  object privs = Privs("Reading cert file");
6f72d42000-02-08Per Hedbor 
2dd46b2000-03-24Per Hedbor  string f, f2;
df36c61999-10-08Henrik Grubbström (Grubba) 
2dd46b2000-03-24Per Hedbor  if( catch{ f = lopen(query_option("ssl_cert_file"), "r")->read(); } ) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(8,"SSL3: Reading cert-file failed!")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
2dd46b2000-03-24Per Hedbor  if( strlen(query_option("ssl_key_file")) && catch{ f2 = lopen(query_option("ssl_key_file"),"r")->read(); } ) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(9, "SSL3: Reading key-file failed!")+"\n");
2dd46b2000-03-24Per Hedbor  destruct(); return; } if (privs) destruct(privs);
df36c61999-10-08Henrik Grubbström (Grubba) 
2dd46b2000-03-24Per Hedbor  object msg = Tools.PEM.pem_msg()->init( f ); object part = msg->parts["CERTIFICATE"] || msg->parts["X509 CERTIFICATE"];
df36c61999-10-08Henrik Grubbström (Grubba)  string cert;
81f8af1999-12-20Martin Nilsson 
2dd46b2000-03-24Per Hedbor  if (!part || !(cert = part->decoded_body())) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(10, "SSL3: No certificate found.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
81f8af1999-12-20Martin Nilsson 
2dd46b2000-03-24Per Hedbor  if( f2 ) msg = Tools.PEM.pem_msg()->init( f2 );
df36c61999-10-08Henrik Grubbström (Grubba)  function r = Crypto.randomness.reasonably_random()->read;
bfb4d41999-12-28Martin Nilsson  SSL3_WERR(sprintf("key file contains: %O", indices(msg->parts)));
df36c61999-10-08Henrik Grubbström (Grubba)  if (part = msg->parts["RSA PRIVATE KEY"]) { string key;
2dd46b2000-03-24Per Hedbor  if (!(key = part->decoded_body())) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(11,"SSL3: Private rsa key not valid")+" (PEM).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
81f8af1999-12-20Martin Nilsson 
df36c61999-10-08Henrik Grubbström (Grubba)  object rsa = Standards.PKCS.RSA.parse_private_key(key);
2dd46b2000-03-24Per Hedbor  if (!rsa) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(11, "SSL3: Private rsa key not valid")+" (DER).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } ctx->rsa = rsa;
81f8af1999-12-20Martin Nilsson 
bfb4d41999-12-28Martin Nilsson  SSL3_WERR(sprintf("RSA key size: %d bits", rsa->rsa_size()));
81f8af1999-12-20Martin Nilsson 
df36c61999-10-08Henrik Grubbström (Grubba)  if (rsa->rsa_size() > 512) { /* Too large for export */ ctx->short_rsa = Crypto.rsa()->generate_key(512, r);
81f8af1999-12-20Martin Nilsson 
df36c61999-10-08Henrik Grubbström (Grubba)  // ctx->long_rsa = Crypto.rsa()->generate_key(rsa->rsa_size(), r); } ctx->rsa_mode(); object tbs = Tools.X509.decode_certificate (cert);
2dd46b2000-03-24Per Hedbor  if (!tbs) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(13,"SSL3: Certificate not valid (DER).")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
2dd46b2000-03-24Per Hedbor  if (!tbs->public_key->rsa->public_key_equal (rsa)) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(14, "SSL3: Certificate and private key do not " "match.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } } else if (part = msg->parts["DSA PRIVATE KEY"]) { string key;
2dd46b2000-03-24Per Hedbor  if (!(key = part->decoded_body())) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (PEM).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
81f8af1999-12-20Martin Nilsson 
df36c61999-10-08Henrik Grubbström (Grubba)  object dsa = Standards.PKCS.DSA.parse_private_key(key);
2dd46b2000-03-24Per Hedbor  if (!dsa) {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(15,"SSL3: Private dsa key not valid")+" (DER).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; }
bfb4d41999-12-28Martin Nilsson  SSL3_WERR(sprintf("Using DSA key."));
2f74c32000-01-21Per Hedbor 
df36c61999-10-08Henrik Grubbström (Grubba)  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. }
2dd46b2000-03-24Per Hedbor  else {
c8ee712000-09-09Andreas Lange  report_error(LOC_M(17,"SSL3: No private key found.")+"\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } ctx->certificates = ({ cert }); ctx->random = r; #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(); werror("Protocol handlers ... "); #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 werror( 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
0d0e952000-11-13Per Hedbor  werror( s+" " ); 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  }; } werror(" [%.1fms]\n", (gethrtime()-st)/1000.0 ); 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;
bc5c2a2000-08-23Per Hedbor  if( urls[ url ] && urls[ url ]->conf )
c5e0961999-10-04Per Hedbor  { if( urls[ url ]->conf != conf ) {
49cd282000-08-11Andreas Lange  report_error(LOC_M(20, "Cannot register URL %s, "
c8ee712000-09-09Andreas Lange  "already registered by %s!")+"\n",
23414a2000-07-21Andreas Lange  url, urls[ url ]->conf->name);
c5e0961999-10-04Per Hedbor  return 0; }
ddefa61999-10-04Marcus Comstedt  urls[ url ]->port->ref(url, urls[url]);
c5e0961999-10-04Per Hedbor  return 1;
bc5c2a2000-08-23Per Hedbor  } else if( urls[ url ] ) { 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 {
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
71a11e1997-08-13Henrik Grubbström (Grubba)  return QUERY(default_ident)?real_version:QUERY(ident);
daff902000-02-23Martin Nilsson #else multiset choices=(<>); 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 }
81f8af1999-12-20Martin Nilsson // Support for unique user id's
b1fca01996-11-12Per Hedbor private object current_user_id_file; 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 }
f6d62d1997-03-26Per Hedbor 
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++;
81f8af1999-12-20Martin Nilsson  //werror("New unique id: "+current_user_id_number+"\n");
b1fca01996-11-12Per Hedbor  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; }
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); 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 });
ee8b201998-07-13David Hedbor  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 { string name; string dir; function draw_function; mapping data_cache = ([]); // not normally used. mapping meta_cache = ([]);
0f8b2f1999-03-27Henrik Grubbström (Grubba) 
6f76f12000-06-01Martin Nilsson  string documentation(void|string tag_n_args) { Stdio.File doc_file; if(!(doc_file=Stdio.File("base_server/image_cache.xml"))) return ""; string doc=doc_file->read(); if(!tag_n_args) return Parser.HTML()->add_container("ex", "")->feed(doc)->read(); 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 string data_cache_insert( string i, string what ) { return data_cache[i] = what; }
753a831999-08-30Per Hedbor 
2ff8461999-09-02Per Hedbor  static mixed frommapp( mapping what )
753a831999-08-30Per Hedbor  {
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 ) { mixed args = Array.map( Array.map( name/"$", argcache->lookup, id->client ), frommapp); mapping meta; string data;
37ecea2000-02-21Per Hedbor 
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. reply = Image.lay( reply ); if( objectp( reply ) && reply->image ) // layer. { 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;
2ff8461999-09-02Per Hedbor  object 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 
2ff8461999-09-02Per Hedbor  if( dither == "random" ) 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"]) bgcolor = Image.Color( (args["background"]||args["background-color"]) );
e9d7c51999-11-02Per Hedbor 
b79be21999-06-11Per Hedbor  if( args["opaque-value"] ) {
91a1562000-04-11Per 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 ); }
3cbd0d2000-01-31Per Hedbor  int x0, y0, x1, y1; 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"]); 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"]); if( args->crop ) {
c089372000-09-13Per Hedbor  if( sscanf( args->crop, "%d,%d-%d,%d", x0, y0, x1, y1 ) ) { x1 -= x0; y1 -= y0; } else { [ x0, y0, x1, y1 ] = reply->find_autocrop(); x1 -= x0; y1 -= y0; }
3cbd0d2000-01-31Per Hedbor  } if( x0 || x1 || y0 || y1 ) { if( !x1 ) x1 = reply->xsize()-x0; if( !y1 ) y1 = reply->ysize()-y0;
91a1562000-04-11Per Hedbor  reply = reply->copy( x0,y0,x0+x1-1,y0+y1-1 );
3cbd0d2000-01-31Per Hedbor  if( alpha )
91a1562000-04-11Per Hedbor  alpha = alpha->copy( x0,y0,x0+x1-1,y0+y1-1 );
3cbd0d2000-01-31Per Hedbor  }
c526921999-05-18Per Hedbor  if( args->scale ) { int x, y; if( sscanf( args->scale, "%d,%d", x, y ) == 2) { reply = reply->scale( x, y ); if( alpha ) alpha = alpha->scale( x, y ); }
3255b11999-05-19Per Hedbor  else if( (float)args->scale < 3.0)
c526921999-05-18Per Hedbor  {
3255b11999-05-19Per Hedbor  reply = reply->scale( ((float)args->scale) );
c526921999-05-18Per Hedbor  if( alpha )
3255b11999-05-19Per Hedbor  alpha = alpha->scale( ((float)args->scale) );
c526921999-05-18Per Hedbor  } }
81f8af1999-12-20Martin Nilsson  if( args->maxwidth || args->maxheight ||
e7e6031999-11-05Per Hedbor  args["max-width"] || args["max-height"])
c526921999-05-18Per Hedbor  {
e7e6031999-11-05Per Hedbor  int x = (int)args->maxwidth||(int)args["max-width"]; int y = (int)args->maxheight||(int)args["max-height"];
c526921999-05-18Per Hedbor  if( x && reply->xsize() > x ) { reply = reply->scale( x, 0 ); if( alpha ) alpha = alpha->scale( x, 0 ); } if( y && reply->ysize() > y ) { reply = reply->scale( 0, y ); if( alpha ) alpha = alpha->scale( 0, y ); } }
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) { 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 ) error("Image format "+format+" unknown\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(), "type":"image/"+format, ]);
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" "Should be ([ \"meta\": ([metadata]), \"data\":\"data\" ])\n"); } store_meta( name, meta ); store_data( name, data ); } static void store_meta( string id, mapping meta ) { meta_cache_insert( id, meta ); string data = encode_value( meta );
4f2d5f2000-03-13Per Hedbor  Stdio.File f; if(!(f=open(dir+id+".i", "wct" )))
69a8691999-05-18Per Hedbor  { report_error( "Failed to open image cache persistant cache file "+ dir+id+".i: "+strerror( errno() )+ "\n" ); return; } f->write( data ); } static void store_data( string id, string data ) {
1a231e2000-06-04Martin Nilsson  if(!data) return;
4f2d5f2000-03-13Per Hedbor  Stdio.File f; if(!(f = open(dir+id+".d", "wct" )))
69a8691999-05-18Per Hedbor  { data_cache_insert( id, data ); report_error( "Failed to open image cache persistant cache file "+
3760171999-06-27Per Hedbor  dir+id+".d: "+strerror( errno() )+ "\n" );
69a8691999-05-18Per Hedbor  return; } f->write( data ); } static mapping restore_meta( string id ) { Stdio.File f; if( meta_cache[ id ] ) return meta_cache[ id ];
4f2d5f2000-03-13Per Hedbor  if( !(f=open(dir+id+".i", "r" ) ) )
69a8691999-05-18Per Hedbor  return 0;
18e19c2000-06-12Martin Stjernholm  string s = f->read(); mapping m; if (catch (m = decode_value (s))) { rm (dir + id + ".i"); report_error( "Corrupt data in persistent cache file "+ dir+id+".i; removed it.\n" ); 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 //! <pi>time()</pi>) is provided, only images generated earlier than //! that are flushed.
4f2d5f2000-03-13Per Hedbor  {
1f58d02000-01-02Martin Nilsson  report_debug("Flushing "+name+" image cache.\n");
4f2d5f2000-03-13Per Hedbor  foreach(r_get_dir(dir), string f) if(f[-2]=='.' && (f[-1]=='i' || f[-1]=='d') && (!age || age>r_file_stat(dir+f)[2])) r_rm(dir+f);
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 //! images generated earlier than that (see <ref>flush()</ref>). //! (These three integers are returned regardless of whether an age //! parameter was given.) {
1f58d02000-01-02Martin Nilsson  int files=0, size=0, aged=0;
1f4a6c2000-08-28Per Hedbor  Stat stat;
4f2d5f2000-03-13Per Hedbor  foreach(r_get_dir(dir), string f)
1f58d02000-01-02Martin Nilsson  if(f[-2]=='.' && (f[-1]=='i' || f[-1]=='d')) { files++;
4f2d5f2000-03-13Per Hedbor  stat=r_file_stat(dir+f,1);
1f58d02000-01-02Martin Nilsson  if(stat[1]>0) size+=stat[1]; if(age<stat[2]) aged++; } return ({files, size, aged}); }
69a8691999-05-18Per Hedbor  static mapping restore( string id ) {
37ecea2000-02-21Per Hedbor  mixed f;
69a8691999-05-18Per Hedbor  mapping m;
37ecea2000-02-21Per Hedbor 
69a8691999-05-18Per Hedbor  if( data_cache[ id ] ) f = data_cache[ id ];
81f8af1999-12-20Martin Nilsson  else
4f2d5f2000-03-13Per Hedbor  if(!(f = open( dir+id+".d", "r" )))
37ecea2000-02-21Per Hedbor  return 0;
69a8691999-05-18Per Hedbor  m = restore_meta( id );
81f8af1999-12-20Martin Nilsson 
69a8691999-05-18Per Hedbor  if(!m) return 0; if( stringp( f ) )
0fe69d2000-03-19Martin Nilsson  return Roxen.http_string_answer( f, m->type||("image/gif") ); return Roxen.http_file_answer( f, m->type||("image/gif") );
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; } void create( string id, function draw_func, string|void d )
93dd0e2000-07-27Johan Sundström  //! Instantiate an image cache of your own, whose image files will //! be stored in a directory `id' in the argument cache directory, //! typically <tt>../var/cache/</tt>. If you supply the optional //! third parameter, this path will be used instead of the common //! argument cache directory. //! //! 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  {
3590742000-03-08Martin Nilsson  if(!d) d = roxenp()->query("argument_cache_dir");
2ff8461999-09-02Per Hedbor  if( d[-1] != '/' ) d+="/"; d += id+"/"; mkdirhier( d+"foo"); dir = d; name = id; draw_function = draw_func; } } 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 { static string name; static string path; static int is_db;
43665d1999-12-19Martin Nilsson  static Sql.sql db;
2ff8461999-09-02Per Hedbor  #define CACHE_VALUE 0 #define CACHE_SKEY 1 #define CACHE_SIZE 600 #define CLEAN_SIZE 100 #ifdef THREADS static Thread.Mutex mutex = Thread.Mutex(); # define LOCK() object __key = mutex->lock() #else
81f8af1999-12-20Martin Nilsson # define LOCK()
2ff8461999-09-02Per Hedbor #endif static mapping (string:mixed) cache = ([ ]);
b870ac1999-09-03Henrik Grubbström (Grubba)  static void setup_table()
2ff8461999-09-02Per Hedbor  { if(catch(db->query("select id from "+name+" where id=-1"))) if(catch(db->query("create table "+name+" (" "id int auto_increment primary key, " "lkey varchar(80) not null default '', " "contents blob not null default '', " "atime bigint not null default 0)"))) throw("Failed to create table in database\n"); }
81f8af1999-12-20Martin Nilsson  void create( string _name, string _path,
2ff8461999-09-02Per Hedbor  int _is_db )
93dd0e2000-07-27Johan Sundström  //! Instantiates an argument cache of your own. //! //! A value of 0 for the <tt>is_db</tt> parameter will make your //! argument cache store its data in the regular filesystem, in a //! directory <tt>name</tt> created at <tt>path</tt>. //! //! A value of 1 for the <tt>is_db</tt> parameter will make your //! argument cache store its data in a database, <tt>path</tt> being //! an <ref>SQL url</ref>, <tt>name</tt> being the name of the table //! in that database.
2ff8461999-09-02Per Hedbor  { name = _name; path = _path; is_db = _is_db; if(is_db) { db = Sql.sql( path ); if(!db) error("Failed to connect to database for argument cache\n"); setup_table( ); } else { if(path[-1] != '/' && path[-1] != '\\') path += "/"; path += replace(name, "/", "_")+"/"; mkdirhier( path + "/tmp" );
4f2d5f2000-03-13Per Hedbor  Stdio.File test; if (!(test = open (path + "/.testfile", "wc"))) error ("Can't create files in the argument cache directory " + path + #if constant(strerror) " ("+strerror(errno())+ #endif "\n"); // else // { // rm (path + "/.testfile"); // It is better not to remove it, // this way permission problems are detected rather early. // }
2ff8461999-09-02Per Hedbor  } } static string read_args( string id ) { if( is_db ) {
43665d1999-12-19Martin Nilsson  array res = db->query("select contents from "+name+" where id='"+id+"'");
2ff8461999-09-02Per Hedbor  if( sizeof(res) ) {
add34e2000-08-17Per Hedbor  db->query("update "+name+" set atime='"+time(1)+"' where id='"+id+"'");
2ff8461999-09-02Per Hedbor  return res[0]->contents; } return 0; } else {
4f2d5f2000-03-13Per Hedbor  Stdio.File f; if( search( id, "/" )<0 && (f = open(path+"/"+id, "r"))) return f->read();
2ff8461999-09-02Per Hedbor  } return 0; }
a7def02000-06-26Per Hedbor  string tohex( string what ) {
ae46e92000-09-23Per Hedbor #if constant(Gmp.mpz)
a7def02000-06-26Per Hedbor  return sprintf( "%x", Gmp.mpz( what, 256 ) );
ae46e92000-09-23Per Hedbor #else int i = 0; for( int q = 0; q<strlen(what); q++ ) { i<<=8; i |= what[strlen(what)-1-q]; } return sprintf( "%x", i ); #endif
a7def02000-06-26Per Hedbor  }
2ff8461999-09-02Per Hedbor  static string create_key( string long_key ) { if( is_db ) {
43665d1999-12-19Martin Nilsson  array data = db->query(sprintf("select id,contents from %s where lkey='%s'",
2ff8461999-09-02Per Hedbor  name,long_key[..79])); foreach( data, mapping m ) if( m->contents == long_key ) return m->id; db->query( sprintf("insert into %s (contents,lkey,atime) values "
81f8af1999-12-20Martin Nilsson  "('%s','%s','%d')",
2ff8461999-09-02Per Hedbor  name, long_key, long_key[..79], time() )); return create_key( long_key ); } else {
a7def02000-06-26Per Hedbor  string _key=tohex(Crypto.md5()->update(long_key)->digest());
2ff8461999-09-02Per Hedbor  _key = replace(_key-"=","/","="); string short_key = _key[0..1];
4f2d5f2000-03-13Per Hedbor  Stdio.File f; while( f = open( path+short_key, "r" ) )
2ff8461999-09-02Per Hedbor  {
4f2d5f2000-03-13Per Hedbor  if( f->read() == long_key )
2ff8461999-09-02Per Hedbor  return short_key; short_key = _key[..strlen(short_key)]; if( strlen(short_key) >= strlen(_key) ) short_key += "."; // Not very likely... }
4f2d5f2000-03-13Per Hedbor  f = open( path+short_key, "wct" );
2ff8461999-09-02Per Hedbor  f->write( long_key ); return short_key; } } 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  { LOCK();
4f2d5f2000-03-13Per Hedbor  if( !is_db ) return !!open( path+key, "r" );
2ff8461999-09-02Per Hedbor  return !!read_args( key ); } 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  LOCK(); array b = values(args), a = sort(indices(args),b); string data = MIME.encode_base64(encode_value(({a,b})),1); if( cache[ data ] ) return cache[ data ][ CACHE_SKEY ];
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))]; if(arrayp(cache[idx])) { m_delete( cache, cache[idx][CACHE_SKEY] ); m_delete( cache, idx ); } else { m_delete( cache, cache[idx] ); m_delete( cache, idx ); } } }
2ff8461999-09-02Per Hedbor  string id = create_key( data ); cache[ data ] = ({ 0, 0 }); cache[ data ][ CACHE_VALUE ] = copy_value( args ); cache[ data ][ CACHE_SKEY ] = id; cache[ id ] = data; return id;
69a8691999-05-18Per Hedbor  }
56b7fc1999-12-21Per Hedbor  mapping lookup( string id, array|void client )
93dd0e2000-07-27Johan Sundström  //! Recall a mapping stored in the cache. The optional client array //! may be supplied to get an error message stating the browser name //! in the event of the key not being present any more in the cache.
c3a53d1999-06-25Per Hedbor  {
2ff8461999-09-02Per Hedbor  LOCK();
c5e0961999-10-04Per Hedbor  if(cache[id] && cache[ cache[id] ] )
2ff8461999-09-02Per Hedbor  return cache[cache[id]][CACHE_VALUE]; string q = read_args( id );
bfb4d41999-12-28Martin Nilsson  if(!q)
56b7fc1999-12-21Per Hedbor  if( client ) error("Key does not exist! (Thinks "+ (client*"") +")\n"); else error("Requesting unknown key\n");
2ff8461999-09-02Per Hedbor  mixed data = decode_value(MIME.decode_base64( q )); data = mkmapping( data[0],data[1] ); cache[ q ] = ({0,0}); cache[ q ][ CACHE_VALUE ] = data; cache[ q ][ CACHE_SKEY ] = id; cache[ id ] = q; 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  {
2ff8461999-09-02Per Hedbor  LOCK(); if(cache[id]) { m_delete( cache, cache[id] ); m_delete( cache, id ); } if( is_db ) db->query( "delete from "+name+" where id='"+id+"'" );
69a8691999-05-18Per Hedbor  else
4f2d5f2000-03-13Per Hedbor  r_rm( path+id );
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");
c8ee712000-09-09Andreas Lange  __REG_PROJ("admin_tasks", "translations/%L/admin_tasks.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
8514832000-11-27Per Hedbor  foreach(({ "module.pmod","PEnt.pike", "PExpr.pike","PXml.pike", "refs.pmod","utils.pmod" }), string q ) DDUMP( "etc/modules/RXML.pmod/"+ q );
dbfe9d2000-04-13Per Hedbor  master()->resolv ("RXML.refs"); master()->resolv ("RXML.PXml"); master()->resolv ("RXML.PEnt");
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" );
cde8d62000-02-16Per Hedbor  dump( "etc/modules/Dims.pmod" );
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" );
7e596b2000-02-13Per Hedbor  dump( "base_server/roxenlib.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/http.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 
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  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 
a40c152000-02-16Per 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" );
9a8a152000-09-25Per Hedbor  add_constant( "Configuration", _configuration );
7e596b2000-02-13Per Hedbor 
a40c152000-02-16Per Hedbor // 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 
2ff8461999-09-02Per Hedbor  u=QUERY(User); 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 object mutex_key; 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 if (QUERY(permanent_uid)) { #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  } }
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);
acf0fa2000-02-29David Hedbor  // Array.map(values(conf->server_ports), lambda(object o) { destruct(o); });
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) {
bc5c2a2000-08-23Per Hedbor  if (object 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;
81f8af1999-12-20Martin Nilsson  where = replace(where, ({ "$pid", "$uid" }),
b1fca01996-11-12Per Hedbor  ({ (string)getpid(), (string)getuid() }));
4f2d5f2000-03-13Per Hedbor  r_rm(where);
5e89211997-02-13Per Hedbor  if(catch(Stdio.write_file(where, sprintf("%d\n%d", 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 }
95b69e1999-09-05Per Hedbor program pipe;
ccffe71999-03-05Wilhelm Köhler object shuffle(object from, object to, object|void to2, function(:void)|void callback)
14179b1997-01-29Per Hedbor {
beaca01998-02-20Per Hedbor #if efun(spider.shuffle) if(!to2)
5bc1991997-01-29Per Hedbor  {
95b69e1999-09-05Per Hedbor  if(!pipe) pipe = ((program)"smartpipe"); object p = pipe( );
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 // 'smartpipe' does not support multiple outputs. object p = Pipe.pipe(); 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 
4f4bc11998-02-04Per Hedbor 
b1fca01996-11-12Per Hedbor static private int _recurse;
a9d8111998-09-01Henrik Grubbström (Grubba) // FIXME: Ought to use the shutdown code.
b1fca01996-11-12Per Hedbor void exit_when_done() {
81f8af1999-12-20Martin Nilsson  report_debug("Interrupt request received. Exiting,\n");
4f4bc11998-02-04Per Hedbor  die_die_die=1;
c5e0961999-10-04Per Hedbor 
b1fca01996-11-12Per Hedbor  if(++_recurse > 4)
38dca81996-12-10Per Hedbor  {
81f8af1999-12-20Martin Nilsson  report_debug("Exiting roxen (spurious signals received).\n");
c5e0961999-10-04Per Hedbor  configurations->stop();
1f79ba1998-09-01Marcus Comstedt #ifdef THREADS stop_handler_threads(); #endif /* THREADS */
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart.
38dca81996-12-10Per Hedbor  }
81f8af1999-12-20Martin Nilsson  report_debug("Exiting roxen.\n");
c5e0961999-10-04Per Hedbor  configurations->stop();
1f79ba1998-09-01Marcus Comstedt #ifdef THREADS
c5e0961999-10-04Per Hedbor  stop_handler_threads();
1f79ba1998-09-01Marcus Comstedt #endif /* THREADS */
c5e0961999-10-04Per Hedbor  exit(-1); // Restart.
b1fca01996-11-12Per Hedbor } void exit_it() {
81f8af1999-12-20Martin Nilsson  report_debug("Recursive signals.\n");
3835ca1998-01-16Henrik Grubbström (Grubba)  exit(-1); // Restart.
b1fca01996-11-12Per Hedbor }
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)  } }
8514832000-11-27Per Hedbor void dump( string file, program|void p )
753a831999-08-30Per Hedbor {
dde8331999-11-15Per Hedbor  if( file[0] != '/' ) file = getcwd() +"/"+ file;
f526692000-05-16Henrik Grubbström (Grubba) #ifdef __NT__
dbfe9d2000-04-13Per Hedbor  file = normalize_path( file );
f526692000-05-16Henrik Grubbström (Grubba) #endif
8514832000-11-27Per Hedbor  if(!p) p = master()->programs[ replace(file, "//", "/" ) ];
dbfe9d2000-04-13Per Hedbor #ifdef __NT__ if( !p ) { if( sscanf( file, "%*s:/%s", file ) ) { file = "/"+file; p = master()->programs[ replace(file, "//", "/" ) ]; } } #endif
753a831999-08-30Per Hedbor  array q;
8514832000-11-27Per Hedbor #ifdef MUCHU_DUMP_DEBUG # define DUMP_DEBUG #endif
753a831999-08-30Per Hedbor  if(!p)
2ff8461999-09-02Per Hedbor  { #ifdef DUMP_DEBUG
81f8af1999-12-20Martin Nilsson  werror(file+" not loaded, and thus cannot be dumped.\n");
2ff8461999-09-02Per Hedbor #endif
753a831999-08-30Per Hedbor  return;
2ff8461999-09-02Per Hedbor  }
753a831999-08-30Per Hedbor 
2bb9241999-11-24Per Hedbor  string ofile = master()->make_ofilename( replace(file, "//", "/") ); if(!file_stat( ofile ) || (file_stat( ofile )[ ST_MTIME ] < file_stat(file)[ ST_MTIME ]))
2ff8461999-09-02Per Hedbor  {
2bb9241999-11-24Per Hedbor  if(q=catch( master()->dump_program( replace(file, "//", "/"), p ) ) )
1b99022000-04-06Leif Stensson  {
2ff8461999-09-02Per Hedbor #ifdef DUMP_DEBUG
1b99022000-04-06Leif Stensson  report_debug("** Cannot encode "+file+": "+describe_backtrace(q)+"\n"); #else
8514832000-11-27Per Hedbor  array parts = replace(file, "//", "/") / "/"; if (sizeof(parts) > 3) parts = parts[sizeof(parts)-3..]; report_debug("Notice: Dumping failed for " + parts*"/"+" (not a bug)\n");
646ba32000-04-06Martin Stjernholm #endif
1b99022000-04-06Leif Stensson  }
646ba32000-04-06Martin Stjernholm #ifdef DUMP_DEBUG else werror( file+" dumped successfully to "+ofile+"\n" );
2ff8461999-09-02Per Hedbor #endif }
8514832000-11-27Per Hedbor #ifdef MUCHO_DUMP_DEBUG
2ff8461999-09-02Per Hedbor  else
646ba32000-04-06Martin Stjernholm  werror(file+" already dumped (and up to date)\n");
2ff8461999-09-02Per Hedbor #endif
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 ... "); int id; string cp = QUERY(argument_cache_dir), na = "args"; if( QUERY(argument_cache_in_db) ) { id = 1; cp = QUERY(argument_cache_db_path); na = "argumentcache"; } mixed e; e = catch( argcache = ArgCache(na,cp,id) ); if( e ) {
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  sleep(10); exit(1);
1d7d6d2000-02-16Per Hedbor  } add_constant( "roxen.argcache", argcache ); report_debug("Done [%.2fms]\n", (gethrtime()-t)/1000.0); }
7339a02000-02-10Per Hedbor int main(int argc, array tmp)
b1fca01996-11-12Per Hedbor {
7339a02000-02-10Per Hedbor  array argv = tmp; tmp = 0;
8514832000-11-27Per Hedbor  DDUMP( "base_server/slowpipe.pike" ); DDUMP( "base_server/fastpipe.pike" );
a577791999-11-24Per Hedbor  slowpipe = ((program)"slowpipe"); fastpipe = ((program)"fastpipe");
8514832000-11-27Per Hedbor  dump( "base_server/throttler.pike" );
a577791999-11-24Per Hedbor 
0d0e952000-11-13Per Hedbor  add_constant( "Protocol", Protocol ); #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" ); DDUMP( "base_server/smartpipe.pike" ); DDUMP( "base_server/fastpipe.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(); initiate_supports();
1d7d6d2000-02-16Per Hedbor  initiate_argcache();
970e242000-09-12Per Hedbor  init_configuserdb();
1d7d6d2000-02-16Per Hedbor 
0d0e952000-11-13Per Hedbor  protocols = build_protocols_mapping();
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  create_pid_file(Getopt.find_option(argv, "p", "pid-file", "ROXEN_PID_FILE") || QUERY(pidfile));
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();
4f4bc11998-02-04Per Hedbor  catch( this_thread()->set_name("Backend") );
34d3fa1999-04-22Per Hedbor  backend_thread = this_thread();
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)
4c12d72000-12-10Per Hedbor  catch( signal(signum(sig), async_sig_start(exit_when_done)) );
15635b1999-10-10Per Hedbor 
4c12d72000-12-10Per Hedbor  catch(signal(signum("SIGHUP"), async_sig_start(reload_all_configurations)));
15635b1999-10-10Per Hedbor 
1c9d081999-07-19Henrik Grubbström (Grubba)  // Signals which cause Roxen to dump the thread state
15635b1999-10-10Per Hedbor  foreach( ({ "SIGUSR1", "SIGUSR2", "SIGTRAP" }), string sig)
4c12d72000-12-10Per Hedbor  catch( signal(signum(sig), async_sig_start(describe_all_threads)) );
0f28da1997-08-13Per Hedbor 
4f4bc11998-02-04Per Hedbor #ifdef __RUN_TRACE trace(1); #endif
08152b1998-04-24Per Hedbor  start_time=time(); // Used by the "uptime" info later on.
4cf7832000-02-16Per Hedbor 
dbfe9d2000-04-13Per Hedbor 
4cf7832000-02-16Per Hedbor  if (QUERY(suicide_engage))
23264c2000-10-17Per Hedbor  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 );
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)
23264c2000-10-17Per Hedbor  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 }),
f3ca762000-08-19Per Hedbor  ({ "referer", "%s", "sizeof(request_id->referer||({}))?request_id->referer[0]:\"\"", 0 }), ({ "user_agent", "%s",
42c2092000-11-16Per Hedbor  "request_id->client?request_id->client*\"%20\":\"-\"", 0 }),
f3ca762000-08-19Per Hedbor  ({ "user", "%s", "extract_user( request_id->realauth )",0 }), ({ "user_id", "%s", "request_id->cookies->RoxenUserID||\"0\"",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"; // werror("Format = %O args = %{%O,%} async = %d\n", format, args, do_it_async ); add_constant( "___LogFormat", LogFormat ); string code = sprintf( #" inherit ___LogFormat; void log( function callback, object request_id, mapping file ) {
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 } 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  } }