24c6c12000-02-20Martin Nilsson // The Roxen Webserver main program. // Copyright © 1996 - 2000, Roxen IS. // // Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others.
edc9af1998-07-11David Hedbor  // ABS and suicide systems contributed freely by Francesco Chemolli
18e19c2000-06-12Martin Stjernholm constant cvs_version="$Id: roxen.pike,v 1.491 2000/06/12 16:50:27 mast 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"; inherit "language";
2ff8461999-09-02Per Hedbor inherit "supports";
7c92b91998-11-19Per Hedbor 
81f8af1999-12-20Martin Nilsson 
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) 
855c391999-11-29Per Hedbor 
7c92b91998-11-19Per Hedbor // Prototypes for other parts of roxen.
855c391999-11-29Per Hedbor  class RoxenModule { constant is_module=1; constant module_type = 0; constant module_unique = 1; string|mapping(string:string) module_name; string|mapping(string:string) module_doc; array(int|string|mapping) register_module(); string file_name_and_stuff(); void start(void|int num, void|object conf); void defvar(string var, mixed value, string name, int type, string|void doc_str, mixed|void misc, int|function|void not_in_config); void definvisvar(string name, int value, int type, array|void misc);
81f8af1999-12-20Martin Nilsson  void deflocaledoc( string locale, string variable, string name, string doc,
855c391999-11-29Per Hedbor  mapping|void translate ); int killvar(string var); string check_variable( string s, mixed value ); mixed query(string|void var, int|void ok); void set(string var, mixed value); int setvars( mapping (string:mixed) vars ); string query_internal_location(); string query_location(); string query_provides(); array query_seclevels(); array(int) stat_file(string f, RequestID id); array(String) find_dir(string f, RequestID id); mapping(string:array(mixed)) find_dir_stat(string f, RequestID id); string real_file(string f, RequestID id); void save(); mapping api_functions(); mapping query_tag_callers(); mapping query_container_callers();
81f8af1999-12-20Martin Nilsson 
855c391999-11-29Per Hedbor  string info(object conf); string comment(); }
81f8af1999-12-20Martin Nilsson class RequestID
7c92b91998-11-19Per Hedbor { object conf; // Really Configuration, but that's sort of recursive. int time; string raw_url; int do_not_disconnect; mapping (string:string) variables; mapping (string:mixed) misc; mapping (string:string) cookies; mapping (string:string) request_headers;
df4fbf1999-10-10Francesco Chemolli  mapping (string:mixed) throttle;
e2900e2000-03-07Martin Nilsson  mapping (string:string) client_var;
7c92b91998-11-19Per Hedbor  multiset(string) prestate; multiset(string) config; multiset(string) supports; multiset(string) pragma; array(string) client; array(string) referer;
2ec1182000-02-04Per Hedbor  Stdio.File my_fd;
7c92b91998-11-19Per Hedbor  string prot; string clientprot; string method;
81f8af1999-12-20Martin Nilsson 
7c92b91998-11-19Per Hedbor  string realfile; string virtfile; string rest_query; string raw; string query; string not_query; string extra_extension; string data; string leftovers; array (int|string) auth; string rawauth; string realauth; string since;
e682c11998-11-22Per Hedbor  string remoteaddr; string host; void create(object|void master_request_id);
7c92b91998-11-19Per Hedbor  void send(string|object what, int|void len); string scan_for_query( string in ); void end(string|void s, int|void keepit); void ready_to_receive(); void send_result(mapping|void result); RequestID clone_me();
84fb682000-02-03Per Hedbor  Stdio.File connection( ); object configuration(); // really Configuration
7c92b91998-11-19Per 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; #define LOGP (variables && variables->audit && GLOBVAR(audit)) #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) report_notice(sprintf("Change to %s(%d):%d privs wanted (%s), from %s", (string)u[0], (int)uid, (int)gid, (string)reason, (string)dbt(backtrace()[-2]))); #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) {
07c9921999-11-23Per Hedbor  report_warning(sprintf("Privs: WARNING: Failed to set the effective group id to %d!\n"
0f8b2f1999-03-27Henrik Grubbström (Grubba)  "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)); 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) {
d3a71c1999-04-20Martin Stjernholm  report_notice(sprintf("Change back to uid#%d gid#%d, from %s\n", saved_uid, saved_gid, dbt(bt[-2])));
0f8b2f1999-03-27Henrik Grubbström (Grubba)  } else {
d3a71c1999-04-20Martin Stjernholm  report_notice(sprintf("Change back to uid#%d gid#%d, 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); }
2ff8461999-09-02Per Hedbor #ifndef THREADS
8fb5172000-05-27Per Hedbor // Emultades a thread_local() object.
b796b51998-11-18Per Hedbor class container { mixed value; mixed set(mixed to) { return value=to; } mixed get() { return value; } }
2ff8461999-09-02Per Hedbor #endif
b796b51998-11-18Per Hedbor 
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  // Locale support. Work in progress. The API is not fixed RoxenLocale.standard default_locale=RoxenLocale.standard;
c5e0961999-10-04Per Hedbor #if constant( thread_local )
59912f1998-10-15Henrik Grubbström (Grubba) object locale = thread_local(); #else
81f8af1999-12-20Martin Nilsson object locale = container();
59912f1998-10-15Henrik Grubbström (Grubba) #endif /* THREADS */
c5e0961999-10-04Per Hedbor 
59912f1998-10-15Henrik Grubbström (Grubba) #define LOCALE LOW_LOCALE->base_server
c31f7b1998-10-10Henrik Grubbström (Grubba) 
8fb5172000-05-27Per Hedbor // For prototype reasons.
dca5dc1998-09-12Per Hedbor program Configuration; /*set in create*/
b1fca01996-11-12Per Hedbor 
8fb5172000-05-27Per Hedbor // No way to write array(Configuration) here, since the program // is not loaded yet.
48fa361997-04-05Per Hedbor array 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)
8fb5172000-05-27Per Hedbor //. Restart roxen, if the start script is running
81f8af1999-12-20Martin Nilsson { call_out(low_shutdown, i, -1); }
8fb5172000-05-27Per Hedbor 
81f8af1999-12-20Martin Nilsson void shutdown(float|void i)
8fb5172000-05-27Per 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
8fb5172000-05-27Per 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(); } }
8fb5172000-05-27Per Hedbor local static Queue handle_queue = Queue(); //. 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; //. Number of handler threads that are alive.
14179b1997-01-29Per Hedbor 
8fb5172000-05-27Per Hedbor local static void handler_thread(int id) //. 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..]));
b796b51998-11-18Per Hedbor  SET_LOCALE(default_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 { report_error(/* LOCALE->uncaught_error(*/describe_backtrace(q)/*)*/); if (q = catch {h = 0;}) { report_error(LOCALE-> uncaught_error(describe_backtrace(q))); } }) { 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;
8fb5172000-05-27Per Hedbor //. The number of handler threads to run.
f526692000-05-16Henrik Grubbström (Grubba) static array(object) handler_threads = ({});
8fb5172000-05-27Per Hedbor //. The handler threads, the list is kept for debug reasons.
14179b1997-01-29Per Hedbor void start_handler_threads() {
a60c4c1997-07-03Henrik Grubbström (Grubba)  if (QUERY(numthreads) <= 1) { QUERY(numthreads) = 1;
07c9921999-11-23Per Hedbor  report_notice("Starting one thread to handle requests.\n");
a60c4c1997-07-03Henrik Grubbström (Grubba)  } else {
07c9921999-11-23Per Hedbor  report_notice("Starting "+
ceadf12000-02-15Martin Nilsson  language_low("en")->number( QUERY(numthreads) )
2ff8461999-09-02Per Hedbor  +" threads to handle requests.\n");
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()
8fb5172000-05-27Per Hedbor //. Stop all the handler threads, bug 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 )
8fb5172000-05-27Per 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 )
8fb5172000-05-27Per 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( ); }
49284b2000-02-17Per Hedbor 
c5e0961999-10-04Per Hedbor class Protocol
8fb5172000-05-27Per Hedbor //. The basic protocol. //. Implements reference handling, finding Configuration objects //. 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";
c5e0961999-10-04Per Hedbor  constant name = "unknown"; constant supports_ipless = 0;
8fb5172000-05-27Per Hedbor  //. If true, the protocol handles ip-less virtual hosting
c5e0961999-10-04Per Hedbor  constant requesthandlerfile = "";
8fb5172000-05-27Per Hedbor  //. Filename of a by-connection handling class. It is also possible //. to set the 'requesthandler' class member in a overloaded create //. function.
c5e0961999-10-04Per Hedbor  constant default_port = 4711;
8fb5172000-05-27Per Hedbor  //. If no port is specified in the URL, use this one
934b3f2000-02-04Per Hedbor 
c5e0961999-10-04Per Hedbor  int port;
8fb5172000-05-27Per Hedbor  //. The currently bound portnumber
c5e0961999-10-04Per Hedbor  string ip;
8fb5172000-05-27Per Hedbor  //. The IP-number (0 for ANY) this port is bound to int refs; //. The number of references to this port
c5e0961999-10-04Per Hedbor  program requesthandler;
8fb5172000-05-27Per Hedbor  //. The per-connection request handling class
ddefa61999-10-04Marcus Comstedt  array(string) sorted_urls = ({});
8fb5172000-05-27Per Hedbor  //. Sorted by length, longest first
ddefa61999-10-04Marcus Comstedt  mapping(string:mapping) urls = ([]);
8fb5172000-05-27Per Hedbor  //. .. url -> ([ "conf":.., ... ])
c5e0961999-10-04Per Hedbor 
ddefa61999-10-04Marcus Comstedt  void ref(string name, mapping data)
8fb5172000-05-27Per 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 }
38c5072000-02-28Per Hedbor 
c5e0961999-10-04Per Hedbor  refs++;
ddefa61999-10-04Marcus Comstedt  urls[name] = data; 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)
8fb5172000-05-27Per Hedbor  //. Remove a ref for the URL 'name'
c5e0961999-10-04Per Hedbor  {
8fb5172000-05-27Per Hedbor  if(!urls[name]) // only unref once
ddefa61999-10-04Marcus Comstedt  return; m_delete(urls, name); sorted_urls -= ({name});
c5e0961999-10-04Per Hedbor  if( !--refs ) destruct( ); // Close the port. }
8fb5172000-05-27Per Hedbor  static void got_connection()
c5e0961999-10-04Per Hedbor  { object q = accept( );
8fb5172000-05-27Per Hedbor  if( q )
ddefa61999-10-04Marcus Comstedt  requesthandler( q, this_object() ); }
8fb5172000-05-27Per Hedbor  local function sp_fcfu;
7120b82000-03-27Per Hedbor  object find_configuration_for_url( string url, RequestID id, int|void no_default )
8fb5172000-05-27Per 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  {
8fb5172000-05-27Per Hedbor  url = lower_case( url );
1d7d6d2000-02-16Per Hedbor  object c;
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 ) ) { if( urls[in]->path )
9699bf1999-10-11Per Hedbor  {
ddefa61999-10-04Marcus Comstedt  id->not_query = id->not_query[strlen(urls[in]->path)..];
9699bf1999-10-11Per Hedbor  id->misc->site_prefix_path = urls[in]->path; }
1d7d6d2000-02-16Per Hedbor  if(!(c=urls[ in ]->conf)->inited) c->enable_all_modules(); 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. // Is the port in the '*' ports?
2ad7d02000-03-27Per Hedbor  mixed i;
8fb5172000-05-27Per Hedbor  if( !functionp(sp_fcfu) && ( i=open_ports[ name ][ 0 ][ port ] ) ) sp_fcfu = i->find_configuration_for_url; if( sp_fcfu && (sp_fcfu != find_configuration_for_url) && (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 = (< >); foreach( configurations, object c ) 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 foreach( values(urls), mapping c ) if( choices[ c->conf ] ) { id->not_query = id->not_query[strlen(c->path)..]; id->misc->site_prefix_path = c->path; if(c->conf->inited) c->conf->enable_all_modules(); return c->conf; } // if there is no such servers, pick the first default server // available.
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. if(!(c = urls[sorted_urls[0]]->conf)->inited) c->enable_all_modules(); id->misc->defaulted=1; return c;
c5e0961999-10-04Per Hedbor  }
934b3f2000-02-04Per Hedbor  mixed query_option( string x )
8fb5172000-05-27Per 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()
8fb5172000-05-27Per 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()
8fb5172000-05-27Per 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()
8fb5172000-05-27Per 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 ) //. Constructor. Bind to the port 'pn' ip 'i'
c5e0961999-10-04Per Hedbor  {
934b3f2000-02-04Per Hedbor  port = pn; ip = i; restore();
c5e0961999-10-04Per Hedbor  if( !requesthandler ) requesthandler = (program)requesthandlerfile;
df36c61999-10-08Henrik Grubbström (Grubba) 
c5e0961999-10-04Per Hedbor  ::create();
81f8af1999-12-20Martin Nilsson  if(!bind( port, got_connection, ip ))
b6fb051999-11-02Per Hedbor  {
81f8af1999-12-20Martin Nilsson  report_error("Failed to bind %s://%s:%d/ (%s)\n", (string)name,
b6fb051999-11-02Per Hedbor  (ip||"*"), (int)port, strerror( errno() ));
df36c61999-10-08Henrik Grubbström (Grubba)  }
c5e0961999-10-04Per Hedbor  }
81f8af1999-12-20Martin Nilsson 
8fb5172000-05-27Per Hedbor  static string _sprintf( )
b6fb051999-11-02Per Hedbor  {
8fb5172000-05-27Per Hedbor  return "Protocol("+name+"://"+ip+":"+port+")";
b6fb051999-11-02Per Hedbor  }
c5e0961999-10-04Per Hedbor }
4820e01999-07-27Henrik Grubbström (Grubba) 
df36c61999-10-08Henrik Grubbström (Grubba) class SSLProtocol
8fb5172000-05-27Per Hedbor //. Base protocol for SSL ports. Exactly like Port, but uses SSL.
df36c61999-10-08Henrik Grubbström (Grubba) { inherit Protocol;
fd45882000-03-23Henrik Grubbström (Grubba) #if constant(Crypto) && constant(Crypto.rsa) && constant(Standards) && constant(Standards.PKCS.RSA) && constant(SSL) && constant(SSL.sslfile)
dd7e661999-10-09Henrik Grubbström (Grubba) 
df36c61999-10-08Henrik Grubbström (Grubba)  // 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(); } ) {
81f8af1999-12-20Martin Nilsson  report_error("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(); } ) { report_error("SSL3: Reading key-file failed!\n"); 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())) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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())) {
81f8af1999-12-20Martin Nilsson  report_error("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) {
81f8af1999-12-20Martin Nilsson  report_error("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) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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)) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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())) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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 {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("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); }
dd7e661999-10-09Henrik Grubbström (Grubba) #else /* !constant(SSL.sslfile) */
2dd46b2000-03-24Per Hedbor  void create(int pn, string i) { report_error("No SSL support available\n");
dd7e661999-10-09Henrik Grubbström (Grubba)  destruct(); }
df36c61999-10-08Henrik Grubbström (Grubba) #endif /* constant(SSL.sslfile) */
b6fb051999-11-02Per Hedbor  string _sprintf( ) { return "SSLProtocol("+name+"://"+ip+":"+port+")"; }
dd7e661999-10-09Henrik Grubbström (Grubba) }
df36c61999-10-08Henrik Grubbström (Grubba) 
761baa1999-12-08Per Hedbor #if constant(HTTPLoop.prog) class FHTTP { inherit Protocol; // inherit Stdio.Port : port; constant supports_ipless=1; constant name = "fhttp"; constant default_port = 80; int dolog; int requests, received, sent; HTTPLoop.Loop l; Stdio.Port portobj; mapping flatten_headers( mapping from ) { mapping res = ([]); foreach(indices(from), string f) res[f] = from[f]*", "; return res; } void setup_fake(object o) { mapping vars = ([]); o->extra_extension = ""; o->misc = flatten_headers(o->headers); o->cmf = 100*1024; o->cmp = 100*1024;
81f8af1999-12-20Martin Nilsson 
761baa1999-12-08Per Hedbor  // werror("%O\n", o->variables); if(o->method == "POST" && strlen(o->data)) { mapping variabels = ([]); switch((o->misc["content-type"]/";")[0]) { default: // Normal form data, handled in the C part. break;
81f8af1999-12-20Martin Nilsson 
761baa1999-12-08Per Hedbor  case "multipart/form-data": object messg = MIME.Message(o->data, o->misc); mapping misc = o->misc;
81f8af1999-12-20Martin Nilsson  foreach(messg->body_parts, object part)
761baa1999-12-08Per Hedbor  {
81f8af1999-12-20Martin Nilsson  if(part->disp_params->filename)
761baa1999-12-08Per Hedbor  { vars[part->disp_params->name]=part->getdata(); vars[part->disp_params->name+".filename"]= part->disp_params->filename; if(!misc->files) misc->files = ({ part->disp_params->name }); else misc->files += ({ part->disp_params->name }); } else { vars[part->disp_params->name]=part->getdata(); } } break; } o->variables = vars|o->variables; } string contents; if(contents = o->misc["cookie"]) { string c; mapping cookies = ([]); multiset config = (<>); o->misc->cookies = contents; foreach(((contents/";") - ({""})), c) { string name, value; while(sizeof(c) && c[0]==' ') c=c[1..]; if(sscanf(c, "%s=%s", name, value) == 2) { value=http_decode_string(value); name=http_decode_string(name); cookies[ name ]=value; if(name == "RoxenConfig" && strlen(value)) config = aggregate_multiset(@(value/"," + ({ }))); } } o->cookies = cookies; o->config = config; } else { o->cookies = ([]); o->config = (<>); }
81f8af1999-12-20Martin Nilsson 
761baa1999-12-08Per Hedbor  if(contents = o->misc->accept) o->misc->accept = contents/","; if(contents = o->misc["accept-charset"]) o->misc["accept-charset"] = ({ contents/"," }); if(contents = o->misc["accept-language"]) o->misc["accept-language"] = ({ contents/"," }); if(contents = o->misc["session-id"]) o->misc["session-id"] = ({ contents/"," }); } void handle_request(object o) { setup_fake( o ); // Equivalent to parse_got in http.pike handle( o->handle_request, this_object() ); } int cdel=10; void do_log() { if(l->logp()) { // werror("log..\n"); switch(query("log")) { case "None": l->log_as_array(); break;
934b3f2000-02-04Per Hedbor  case "CommonLog":
761baa1999-12-08Per Hedbor  object f = Stdio.File( query("log_file"), "wca" ); l->log_as_commonlog_to_file( f ); destruct(f); break;
934b3f2000-02-04Per Hedbor  default: report_notice( "It is not yet possible to log using the "+ query("log")+" method. Sorry. Out of time"); break;
761baa1999-12-08Per Hedbor  } cdel--; if(cdel < 1) cdel=1; } else { cdel++; // werror("nolog..\n"); } call_out(do_log, cdel); }
6796aa1999-12-11Per Hedbor  string status( )
761baa1999-12-08Per Hedbor  {
6796aa1999-12-11Per Hedbor  mapping m = l->cache_status(); string res; low_adjust_stats( m );
7256451999-12-08Per Hedbor #define PCT(X) ((int)(((X)/(float)(m->total+0.1))*100))
e3bbe11999-12-15Per Hedbor  res = ("\nCache statistics\n<pre>\n");
7256451999-12-08Per Hedbor  m->total = m->hits + m->misses + m->stale;
e3bbe11999-12-15Per Hedbor  res += sprintf(" %d elements in cache, size is %1.1fMb max is %1.1fMb\n"
6796aa1999-12-11Per Hedbor  " %d cache lookups, %d%% hits, %d%% misses and %d%% stale.\n", m->entries, m->size/(1024.0*1024.0), m->max_size/(1024*1024.0), m->total, PCT(m->hits), PCT(m->misses), PCT(m->stale));
e3bbe11999-12-15Per Hedbor  return res+"\n</pre>\n";
6796aa1999-12-11Per Hedbor  }
7256451999-12-08Per Hedbor 
6796aa1999-12-11Per Hedbor  void low_adjust_stats(mapping m) {
761baa1999-12-08Per Hedbor  array q = values( urls )->conf; if( sizeof( q ) ) /* This is not exactly correct if sizeof(q)>1 */ { q[0]->requests += m->num_request; q[0]->received += m->received_bytes; q[0]->sent += m->sent_bytes; } requests += m->num_requests; received += m->received_bytes; sent += m->sent_bytes; } void adjust_stats() { call_out(adjust_stats, 2);
ebdf942000-02-04Per Hedbor // werror( status() ); low_adjust_stats( l->cache_status() );
761baa1999-12-08Per Hedbor  } void create( int pn, string i ) { requesthandler = (program)"protocols/fhttp.pike"; port = pn; ip = i;
6f72d42000-02-08Per Hedbor  set_up_fhttp_variables( this_object() );
934b3f2000-02-04Per Hedbor  restore();
6f72d42000-02-08Per Hedbor 
934b3f2000-02-04Per Hedbor  dolog = (query_option( "log" ) && (query_option( "log" )!="None"));
761baa1999-12-08Per Hedbor  portobj = Stdio.Port(); /* No way to use ::create easily */ if( !portobj->bind( port, 0, ip ) ) { report_error("Failed to bind %s://%s:%d/ (%s)\n", name,ip||"*",(int)port, strerror(errno())); destruct(portobj); destruct(); return; }
81f8af1999-12-20Martin Nilsson  l = HTTPLoop.Loop( portobj, requesthandler,
761baa1999-12-08Per Hedbor  handle_request, 0,
0c989e2000-02-04Per Hedbor  ((int)query_option("ram_cache")||20)*1024*1024,
761baa1999-12-08Per Hedbor  dolog, (query_option("read_timeout")||120) ); call_out(adjust_stats, 10); if(dolog) call_out(do_log, 5); } } #endif
c5e0961999-10-04Per Hedbor class HTTP { inherit Protocol; constant supports_ipless = 1; constant name = "http"; constant requesthandlerfile = "protocols/http.pike"; constant default_port = 80;
934b3f2000-02-04Per Hedbor  void create( mixed ... args ) { set_up_http_variables( this_object() ); ::create( @args ); }
c5e0961999-10-04Per Hedbor }
b1fca01996-11-12Per Hedbor 
ac76fc1999-10-07Henrik Grubbström (Grubba) class HTTPS {
df36c61999-10-08Henrik Grubbström (Grubba)  inherit SSLProtocol;
ac76fc1999-10-07Henrik Grubbström (Grubba)  constant supports_ipless = 0; constant name = "https";
df36c61999-10-08Henrik Grubbström (Grubba)  constant requesthandlerfile = "protocols/http.pike";
ac76fc1999-10-07Henrik Grubbström (Grubba)  constant default_port = 443;
df36c61999-10-08Henrik Grubbström (Grubba) 
1d7d6d2000-02-16Per Hedbor  class fallback_redirect_request { string in = ""; string out; string default_prefix; int port; Stdio.File f; void die() { SSL3_WERR(sprintf("fallback_redirect_request::die()")); f->close(); destruct(f); destruct(this_object()); } void write_callback(object id) { SSL3_WERR(sprintf("fallback_redirect_request::write_callback()")); int written = id->write(out); if (written <= 0) die(); out = out[written..]; if (!strlen(out)) die(); } void read_callback(object id, string s) { SSL3_WERR(sprintf("fallback_redirect_request::read_callback(X, \"%s\")\n", s)); in += s; string name; string prefix; if (search(in, "\r\n\r\n") >= 0) { // werror("request = '%s'\n", in); array(string) lines = in / "\r\n"; array(string) req = replace(lines[0], "\t", " ") / " "; if (sizeof(req) < 2) { out = "HTTP/1.0 400 Bad Request\r\n\r\n"; } else { if (sizeof(req) == 2) { name = req[1]; } else { name = req[1..sizeof(req)-2] * " "; foreach(map(lines[1..], `/, ":"), array header) { if ( (sizeof(header) >= 2) && (lower_case(header[0]) == "host") ) prefix = "https://" + header[1] - " "; } } if (prefix) { if (prefix[-1] == '/') prefix = prefix[..strlen(prefix)-2]; prefix = prefix + ":" + port; } else { /* default_prefix (aka MyWorldLocation) already contains the * portnumber. */ if (!(prefix = default_prefix)) { /* This case is most unlikely to occur, * but better safe than sorry... */ string ip = (f->query_address(1)/" ")[0]; prefix = "https://" + ip + ":" + port; } else if (prefix[..4] == "http:") { /* Broken MyWorldLocation -- fix. */ prefix = "https:" + prefix[5..]; } } out = sprintf("HTTP/1.0 301 Redirect to secure server\r\n" "Location: %s%s\r\n\r\n", prefix, name); } f->set_read_callback(0); f->set_write_callback(write_callback); } } void create(object socket, string s, string l, int p) { SSL3_WERR(sprintf("fallback_redirect_request(X, \"%s\", \"%s\", %d)", s, l||"CONFIG PORT", p)); f = socket; default_prefix = l; port = p; f->set_nonblocking(read_callback, 0, die); f->set_id(f); read_callback(f, s); } }
dd7e661999-10-09Henrik Grubbström (Grubba) #if constant(SSL.sslfile)
df36c61999-10-08Henrik Grubbström (Grubba)  class http_fallback { object my_fd; void ssl_alert_callback(object alert, object|int n, string data) {
bfb4d41999-12-28Martin Nilsson  SSL3_WERR(sprintf("http_fallback(X, %O, \"%s\")", n, data));
df36c61999-10-08Henrik Grubbström (Grubba)  // trace(1); if ( (my_fd->current_write_state->seq_num == 0) && search(lower_case(data), "http")) { object raw_fd = my_fd->socket;
60a9121999-10-10Henrik Grubbström (Grubba)  my_fd->socket = 0;
df36c61999-10-08Henrik Grubbström (Grubba)  /* Redirect to a https-url */ // my_fd->set_close_callback(0); // my_fd->leave_me_alone = 1; fallback_redirect_request(raw_fd, data,
81f8af1999-12-20Martin Nilsson  my_fd->config &&
df36c61999-10-08Henrik Grubbström (Grubba)  my_fd->config->query("MyWorldLocation"), port); destruct(my_fd); destruct(this_object()); // my_fd = 0; /* Forget ssl-object */ } } void ssl_accept_callback(object id) {
bfb4d41999-12-28Martin Nilsson  SSL3_WERR(sprintf("ssl_accept_callback(X)"));
df36c61999-10-08Henrik Grubbström (Grubba)  id->set_alert_callback(0); /* Forget about http_fallback */ my_fd = 0; /* Not needed any more */ } void create(object fd) { my_fd = fd; fd->set_alert_callback(ssl_alert_callback); fd->set_accept_callback(ssl_accept_callback); } }
81f8af1999-12-20Martin Nilsson 
df36c61999-10-08Henrik Grubbström (Grubba)  object accept() { object q = ::accept(); if (q) { http_fallback(q); } return q; } #endif /* constant(SSL.sslfile) */
934b3f2000-02-04Per Hedbor  void create( mixed ... args ) { set_up_http_variables( this_object() ); ::create( @args ); }
dd7e661999-10-09Henrik Grubbström (Grubba) }
df36c61999-10-08Henrik Grubbström (Grubba) 
ac76fc1999-10-07Henrik Grubbström (Grubba) class FTP { inherit Protocol; constant supports_ipless = 0; constant name = "ftp"; constant requesthandlerfile = "protocols/ftp.pike"; constant default_port = 21;
df36c61999-10-08Henrik Grubbström (Grubba)  // Some statistics int sessions; int ftp_users; int ftp_users_now;
934b3f2000-02-04Per Hedbor  void create( mixed ... args ) { set_up_ftp_variables( this_object() ); ::create( @args ); }
ac76fc1999-10-07Henrik Grubbström (Grubba) }
df36c61999-10-08Henrik Grubbström (Grubba) class FTPS { inherit SSLProtocol; constant supports_ipless = 0; constant name = "ftps"; constant requesthandlerfile = "protocols/ftp.pike"; constant default_port = 21; /*** ???? ***/ // Some statistics int sessions; int ftp_users; int ftp_users_now;
934b3f2000-02-04Per Hedbor  void create( mixed ... args ) { set_up_ftp_variables( this_object() ); ::create( @args ); }
df36c61999-10-08Henrik Grubbström (Grubba) }
ac76fc1999-10-07Henrik Grubbström (Grubba) class GOPHER { inherit Protocol; constant supports_ipless = 0; constant name = "gopher"; constant requesthandlerfile = "protocols/gopher.pike"; constant default_port = 70; } class TETRIS { inherit Protocol; constant supports_ipless = 0; constant name = "tetris"; constant requesthandlerfile = "protocols/tetris.pike"; constant default_port = 2050; }
479d8a1999-11-25Henrik Grubbström (Grubba) class SMTP { inherit Protocol; constant supports_ipless = 1; constant name = "smtp"; constant requesthandlerfile = "protocols/smtp.pike"; constant default_port = Protocols.Ports.tcp.smtp; } class POP3 { inherit Protocol; constant supports_ipless = 0; constant name = "pop3"; constant requesthandlerfile = "protocols/pop3.pike"; constant default_port = Protocols.Ports.tcp.pop3; } class IMAP { inherit Protocol; constant supports_ipless = 0; constant name = "imap"; constant requesthandlerfile = "protocols/imap.pike"; constant default_port = Protocols.Ports.tcp.imap2; }
c5e0961999-10-04Per Hedbor mapping protocols = ([
761baa1999-12-08Per Hedbor #if constant(HTTPLoop.prog) "fhttp":FHTTP, #else "fhttp":HTTP, #endif
c5e0961999-10-04Per Hedbor  "http":HTTP,
ac76fc1999-10-07Henrik Grubbström (Grubba)  "ftp":FTP,
df36c61999-10-08Henrik Grubbström (Grubba) 
3eebb92000-02-12Martin Stjernholm  "https":HTTPS,
df36c61999-10-08Henrik Grubbström (Grubba)  "ftps":FTPS,
ac76fc1999-10-07Henrik Grubbström (Grubba)  "gopher":GOPHER, "tetris":TETRIS,
479d8a1999-11-25Henrik Grubbström (Grubba)  "smtp":SMTP, "pop3":POP3, "imap":IMAP,
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] ) ) report_error( "I cannot possibly bind to "+what+ ", that host is unknown. " "Substituting with ANY\n"); 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 {
8fb5172000-05-27Per Hedbor  url = lower_case( url );
e7e6031999-11-05Per Hedbor  report_debug("Unregister "+url+"\n");
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 ); sort_urls();
b1fca01996-11-12Per Hedbor  } }
934b3f2000-02-04Per Hedbor array all_ports( ) { return Array.uniq( values( urls )->port ); } 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) }
c5e0961999-10-04Per Hedbor int register_url( string url, object conf ) {
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)) { report_error("Bad URL `" + url + "' for server `" + conf->query_name() + "'\n"); return 1; } 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; if( urls[ url ] ) { if( urls[ url ]->conf != conf ) { report_error( "Cannot register URL "+url+
dfb2302000-02-20Martin Stjernholm  ", already registered by " +
c5e0961999-10-04Per Hedbor  urls[ url ]->conf->name + "!\n" ); return 0; }
ddefa61999-10-04Marcus Comstedt  urls[ url ]->port->ref(url, urls[url]);
c5e0961999-10-04Per Hedbor  return 1; }
dd7e661999-10-09Henrik Grubbström (Grubba)  Protocol prot;
c5e0961999-10-04Per Hedbor  if( !( prot = protocols[ protocol ] ) ) { report_error( "Cannot register URL "+url+
81f8af1999-12-20Martin Nilsson  ", cannot find the protocol " +
c5e0961999-10-04Per Hedbor  protocol + "!\n" ); 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 ]); 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];
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; }
dd7e661999-10-09Henrik Grubbström (Grubba)  if( !( m[ required_host ][ port ] ) ) { m_delete( m[ required_host ], port ); failures++; if (required_host) { report_warning("Binding the port on IP " + required_host +
b6fb051999-11-02Per Hedbor  " failed\n for URL " + url + "!\n");
dd7e661999-10-09Henrik Grubbström (Grubba)  } continue; } urls[ url ]->port = m[ required_host ][ port ]; m[ required_host ][ port ]->ref(url, urls[url]); } if (failures == sizeof(required_hosts)) {
c5e0961999-10-04Per Hedbor  m_delete( urls, url );
b6fb051999-11-02Per Hedbor  report_error( "Cannot register URL "+url+"!\n" );
dd7e661999-10-09Henrik Grubbström (Grubba)  sort_urls();
c5e0961999-10-04Per Hedbor  return 0; } sort_urls();
1d7d6d2000-02-16Per Hedbor  report_notice("Registered "+url+" for "+conf->query_name()+"\n");
c5e0961999-10-04Per Hedbor  return 1; } object find_configuration( string name )
b1fca01996-11-12Per Hedbor {
9699bf1999-10-11Per Hedbor  name = replace( lower_case( replace(name,"-"," ") )-" ", "/", "-" );
c5e0961999-10-04Per Hedbor  foreach( configurations, object 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 {
c60cae2000-02-02Johan Sundström  int log_time = time();
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
c31f7b1998-10-10Henrik Grubbström (Grubba)  mark_fd(current_user_id_file->query_fd(), LOCALE->unique_uid_logfile());
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(); return current_user_id_number+time(); } 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; } public string full_status() { int tmp; string res=""; array foo = ({0.0, 0.0, 0.0, 0.0, 0}); if(!sizeof(configurations))
c31f7b1998-10-10Henrik Grubbström (Grubba)  return LOCALE->no_servers_enabled();
81f8af1999-12-20Martin Nilsson 
5281751997-11-26Henrik Grubbström (Grubba)  foreach(configurations, object conf)
b1fca01996-11-12Per Hedbor  {
88e1cb1996-12-07David Hedbor  if(!conf->sent ||!conf->received ||!conf->hsent) continue;
48315a1999-11-23Per Hedbor  foo[0] += conf->sent/(1024.0*1024.0)/(float)(time(1)-start_time+1); foo[1] += conf->sent/(1024.0*1024.0); foo[2] += conf->hsent/(1024.0*1024.0); foo[3] += conf->received/(1024.0*1024.0);
5281751997-11-26Henrik Grubbström (Grubba)  foo[4] += conf->requests;
b1fca01996-11-12Per Hedbor  }
5281751997-11-26Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  for(tmp = 1; tmp < 4; tmp ++) {
c31f7b1998-10-10Henrik Grubbström (Grubba)  // FIXME: LOCALE?
81f8af1999-12-20Martin Nilsson  if(foo[tmp] < 1024.0)
27b0e11996-11-26Per Hedbor  foo[tmp] = sprintf("%.2f MB", foo[tmp]);
b1fca01996-11-12Per Hedbor  else
27b0e11996-11-26Per Hedbor  foo[tmp] = sprintf("%.2f GB", foo[tmp]/1024.0);
b1fca01996-11-12Per Hedbor  }
60ecef1998-06-13Henrik Grubbström (Grubba)  int uptime = time()-start_time; int days = uptime/(24*60*60); int hrs = uptime/(60*60); int min = uptime/60 - hrs*60; hrs -= days*24;
053e9a1998-08-20Per Hedbor  tmp=(int)((foo[4]*600.0)/(uptime+1));
b1fca01996-11-12Per Hedbor 
20b07e2000-02-02Per Hedbor  return(LOCALE->full_status(real_version, start_time,
c31f7b1998-10-10Henrik Grubbström (Grubba)  days, hrs, min, uptime%60, foo[1], foo[0] * 8192.0, foo[2], foo[4], (float)tmp/(float)10, foo[3]));
b1fca01996-11-12Per Hedbor }
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 
a6ef1f2000-03-28Johan Sundström // Settings used by the various administration interface modules etc.
c5e0961999-10-04Per Hedbor class ConfigIFCache { string dir;
41b9142000-04-04Per Hedbor  int settings; void create( string name, int|void _settings )
c5e0961999-10-04Per Hedbor  {
41b9142000-04-04Per Hedbor  if( settings = _settings )
4f2d5f2000-03-13Per Hedbor  dir = configuration_dir + "_configinterface/" + name + "/"; else dir = "../var/"+roxen_version()+"/config_caches/" + name + "/";
52c0fa2000-03-01Per Hedbor  mkdirhier( dir );
c5e0961999-10-04Per Hedbor  } mixed set( string name, mixed to ) {
4f2d5f2000-03-13Per Hedbor  Stdio.File f; if(!(f=open( dir + replace( name, "/", "-" ), "wct" )))
c5e0961999-10-04Per Hedbor  { mkdirhier( dir+"/foo" );
a8ee432000-03-31Martin Nilsson  if(!(f=open( dir + replace( name, "/", "-" ), "wct" )))
c5e0961999-10-04Per Hedbor  {
a6ef1f2000-03-28Johan Sundström  report_error("Failed to create administration interface cache file ("+
c5e0961999-10-04Per Hedbor  dir + replace( name, "/", "-" )+") "+ strerror( errno() )+"\n"); return to; } }
41b9142000-04-04Per Hedbor  if( settings ) f->write(
ca594e2000-03-01Per Hedbor #"<?XML version=\"1.0\" encoding=\"UTF-8\"?> " + string_to_utf8(encode_mixed( to, this_object() ) ));
41b9142000-04-04Per Hedbor  else f->write( encode_value( to ) );
c5e0961999-10-04Per Hedbor  return to; }
81f8af1999-12-20Martin Nilsson 
c5e0961999-10-04Per Hedbor  mixed get( string name ) {
4f2d5f2000-03-13Per Hedbor  Stdio.File f;
52c0fa2000-03-01Per Hedbor  mapping q = ([]);
4f2d5f2000-03-13Per Hedbor  f=open( dir + replace( name, "/", "-" ), "r" ); if(!f) return 0;
41b9142000-04-04Per Hedbor  if( settings ) decode_variable( 0, ([ "name":"res" ]), utf8_to_string(f->read()), q ); else { catch{ return decode_value( f->read() ); }; return 0; }
52c0fa2000-03-01Per Hedbor  return q->res;
c5e0961999-10-04Per Hedbor  }
6b34861999-11-15Per Hedbor  array list() {
4f2d5f2000-03-13Per Hedbor  return r_get_dir( dir );
6b34861999-11-15Per Hedbor  }
c5e0961999-10-04Per Hedbor  void delete( string name ) {
4f2d5f2000-03-13Per Hedbor  r_rm( dir + replace( name, "/", "-" ) );
c5e0961999-10-04Per Hedbor  } }
753a831999-08-30Per Hedbor 
2ff8461999-09-02Per Hedbor class ImageCache { 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  {
2ff8461999-09-02Per Hedbor  if( what[""] ) return what[""]; 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 ) {
91a1562000-04-11Per Hedbor  Image.Image i = Image.Image( reply->xsize(), reply->ysize(), ov,ov,ov );
e9d7c51999-11-02Per Hedbor  i = i->paste_alpha( alpha, ov );
b79be21999-06-11Per Hedbor  alpha = i; } 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 ) { sscanf( args->crop, "%d,%d-%d,%d", x0, y0, x1, y1 ); x1 -= x0; y1 -= y0; } 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 )
000c781999-05-25Per Hedbor  if( ct[ dither ] ) ct[ dither ]();
69a8691999-05-18Per Hedbor  else ct->ordered(); }
81f8af1999-12-20Martin Nilsson  if(!Image[upper_case( format )]
3760171999-06-27Per Hedbor  || !Image[upper_case( format )]->encode )
69a8691999-05-18Per Hedbor  error("Image format "+format+" unknown\n"); mapping enc_args = ([]); if( ct ) enc_args->colortable = ct; if( alpha ) enc_args->alpha = alpha;
c526921999-05-18Per Hedbor  foreach( glob( "*-*", indices(args)), string n ) if(sscanf(n, "%*[^-]-%s", string opt ) == 2) enc_args[opt] = (int)args[n];
69a8691999-05-18Per Hedbor  switch(format) { case "gif":
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 ); break;
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" );
e9d7c51999-11-02Per Hedbor  if( !enc_args->alpha ) m_delete( enc_args, "alpha" );
81f8af1999-12-20Martin Nilsson 
69a8691999-05-18Per Hedbor  default: data = Image[upper_case( format )]->encode( reply, enc_args ); }
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  }
4f2d5f2000-03-13Per Hedbor  void flush(int|void age) {
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  } array status(int|void age) { int files=0, size=0, aged=0; array 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  } string data( string|mapping args, RequestID id, int|void nodraw ) { 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; }
81f8af1999-12-20Martin Nilsson  mapping http_file_answer( string|mapping data, RequestID id,
c3a53d1999-06-25Per Hedbor  int|void nodraw )
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; }
2ff8461999-09-02Per Hedbor  mapping metadata( string|mapping data, RequestID id, int|void nodraw ) { 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 ) { string ci; if( mappingp( data ) ) ci = argcache->store( data ); else if( arrayp( data ) ) ci = Array.map( Array.map( data, tomapp ), argcache->store )*"$"; else ci = data; return ci; } void set_draw_function( function to ) { draw_function = to; } void create( string id, function draw_func, string|void d ) {
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 { 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 ) { 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) ) {
4f2d5f2000-03-13Per Hedbor  db->query("update "+name+" set atime='"+time()+"' 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; } 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 { string _key=MIME.encode_base64(Crypto.md5()->update(long_key)->digest(),1); _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 ) { 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 )
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 )
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 )
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() {
2537c32000-02-14Per Hedbor  SET_LOCALE(default_locale);
cde8d62000-02-16Per Hedbor 
2ff8461999-09-02Per Hedbor  // Dump some programs (for speed)
dbfe9d2000-04-13Per Hedbor  master()->resolv ("RXML.refs"); master()->resolv ("RXML.PXml"); master()->resolv ("RXML.PEnt");
a40c152000-02-16Per Hedbor  dump( "etc/roxen_master.pike" );
cde8d62000-02-16Per Hedbor  dump( "etc/modules/Dims.pmod" );
dbfe9d2000-04-13Per Hedbor // dump( "etc/modules/RXML.pmod/module.pmod" ); foreach(({ "module.pmod","PEnt.pike", "PExpr.pike","PXml.pike", "refs.pmod","utils.pmod" }), string q )
27008b2000-03-20Martin Stjernholm  dump( "etc/modules/RXML.pmod/"+ q ); dump( "etc/modules/Roxen.pmod" ); // This is currently needed to resolve the circular references in // RXML.pmod correctly. :P
cde8d62000-02-16Per Hedbor  dump( "base_server/disk_cache.pike" );
2537c32000-02-14Per Hedbor  foreach( glob("*.pmod",get_dir( "etc/modules/RoxenLocale.pmod/")), string q )
4cf7832000-02-16Per Hedbor  if( q != "Modules.pmod" ) dump( "etc/modules/RoxenLocale.pmod/"+ q );
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 
95b69e1999-09-05Per Hedbor  add_constant( "RequestID", RequestID);
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 );
2537c32000-02-14Per Hedbor  add_constant( "roxen.locale", locale );
27008b2000-03-20Martin Stjernholm  //add_constant( "roxen.ImageCache", ImageCache );
2537c32000-02-14Per Hedbor 
2ff8461999-09-02Per Hedbor  // compatibility
a40c152000-02-16Per Hedbor // int s = gethrtime();
cde8d62000-02-16Per Hedbor  add_constant( "roxen.fonts", (fonts = ((program)"base_server/fonts.pike")()) );
a40c152000-02-16Per Hedbor // report_debug( "[fonts: %.2fms] ", (gethrtime()-s)/1000.0);
cde8d62000-02-16Per Hedbor  dump( "base_server/fonts.pike" );
855c391999-11-29Per Hedbor 
a40c152000-02-16Per Hedbor // int s = gethrtime();
7e596b2000-02-13Per Hedbor  Configuration = (program)"configuration"; dump( "base_server/configuration.pike" ); dump( "base_server/rxmlhelp.pike" ); add_constant( "Configuration", Configuration );
a40c152000-02-16Per Hedbor // report_debug( "[Configuration: %.2fms] ", (gethrtime()-s)/1000.0);
69a8691999-05-18Per Hedbor }
2ff8461999-09-02Per Hedbor // 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).
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor int set_u_and_gid() { #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()) { report_error ("It is only possible to change uid and gid if the server " "is running as root.\n"); } 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); if (getgid() != gid) report_error ("Failed to set gid.\n"), g = 0; # else report_warning ("Setting gid not supported on this system.\n"); g = 0; # endif } setuid(uid); if (getuid() != uid) report_error ("Failed to set uid.\n"), u = 0; if (u) report_notice(LOCALE->setting_uid_gid_permanently (uid, gid, u, g)); #else report_warning ("Setting uid not supported on this system.\n"); u = g = 0; #endif }
b84a161999-06-07Martin Stjernholm  else {
2ff8461999-09-02Per Hedbor #if constant(seteuid) if (g) { # if constant(setegid) setegid(gid); if (getegid() != gid) report_error ("Failed to set effective gid.\n"), g = 0; # else report_warning ("Setting effective gid not supported on this system.\n"); g = 0; # endif } seteuid(uid); if (geteuid() != uid) report_error ("Failed to set effective uid.\n"), u = 0; if (u) report_notice(LOCALE->setting_uid_gid (uid, gid, u, g)); #else report_warning ("Setting effective uid not supported on this system.\n"); 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() { object conf; array (object) new_confs = ({}); mapping config_cache = ([]); int modified; configs = ([]); setvars(retrieve("Variables", 0)); foreach(list_all_configurations(), string config)
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  array err, 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); }) { report_error(LOCALE-> error_enabling_configuration(config, describe_backtrace(err))); continue;
a22f6f1999-05-12Per Hedbor  } }
2ff8461999-09-02Per Hedbor  if(err = catch { conf->start(); conf->enable_all_modules(); }) { report_error(LOCALE-> error_enabling_configuration(config, describe_backtrace(err))); 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; report_notice(LOCALE->disabling_configuration(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; config_stat_cache = config_cache;
a22f6f1999-05-12Per Hedbor  }
2ff8461999-09-02Per Hedbor }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor object enable_configuration(string name) {
15635b1999-10-10Per Hedbor  object cf = Configuration( name );
2ff8461999-09-02Per Hedbor  configurations += ({ cf }); return cf; }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor // Enable all configurations void enable_configurations() { array err; configurations = ({});
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);
15635b1999-10-10Per Hedbor  if(err=catch( enable_configuration(config)->start() ))
1d7d6d2000-02-16Per Hedbor  report_error("\nError while loading configuration "+config+":\n"+
15635b1999-10-10Per Hedbor  describe_backtrace(err)+"\n");
2dd46b2000-03-24Per Hedbor  report_debug("Enabled %s in %.1fms\n", config, (gethrtime()-t)/1000.0 );
1d7d6d2000-02-16Per Hedbor  }
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;
2ff8461999-09-02Per Hedbor  foreach(configurations, object config)
4cf7832000-02-16Per Hedbor  if(mixed err=catch( config->enable_all_modules() ))
15635b1999-10-10Per Hedbor  report_error("Error while loading modules in configuration "+ 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; // object ob; // mixed e = // catch // { // if( ob = Image.ANY.decode( data ) ) // return ([ "img":ob, "image":ob ]); // }; return 0;
a22f6f1999-05-12Per Hedbor }
d681842000-02-08Per Hedbor array(Image.Layer) decode_layers(string data, void|mixed tocolor) { return Image.decode_layers( data, tocolor ); }
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()))
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  }; 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 ); }
d681842000-02-08Per Hedbor array(Image.Layer) load_layers(string f, RequestID id) { 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())) catch { data = Protocols.HTTP.get_url_nice( f )[1]; }; if( !data ) return 0; } } id->misc->_load_image_called = 0; if(!data) return 0; return decode_layers( data ); }
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 }
7c92b91998-11-19Per Hedbor void set_locale( string to ) { if( to == "standard" ) SET_LOCALE( default_locale );
2537c32000-02-14Per Hedbor  SET_LOCALE( RoxenLocale[ to ] || default_locale );
7c92b91998-11-19Per Hedbor }
a22f6f1999-05-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)  } }
753a831999-08-30Per Hedbor  void dump( string file ) {
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
dde8331999-11-15Per Hedbor  program 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;
2bb9241999-11-24Per Hedbor 
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 array parts = replace(file, "//", "/") / "/"; if (sizeof(parts) > 3) parts = parts[sizeof(parts)-3..];
646ba32000-04-06Martin Stjernholm  report_debug("Dumping failed for " + parts*"/" + "\n"); #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 } #ifdef DUMP_DEBUG 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;
a577791999-11-24Per Hedbor  slowpipe = ((program)"slowpipe"); fastpipe = ((program)"fastpipe");
753a831999-08-30Per Hedbor  call_out( lambda() {
2537c32000-02-14Per Hedbor  foreach(glob("*.pmod",get_dir( "etc/modules/RoxenLocale.pmod/")), string q ) dump( "etc/modules/RoxenLocale.pmod/"+ q );
4cf7832000-02-16Per Hedbor  (program)"module";
95b69e1999-09-05Per Hedbor  dump( "protocols/http.pike"); dump( "protocols/ftp.pike"); dump( "protocols/https.pike");
753a831999-08-30Per Hedbor  dump( "base_server/state.pike" );
2ff8461999-09-02Per Hedbor  dump( "base_server/highlight_pike.pike");
753a831999-08-30Per Hedbor  dump( "base_server/wizard.pike" ); dump( "base_server/proxyauth.pike" ); dump( "base_server/html.pike" ); dump( "base_server/module.pike" );
95b69e1999-09-05Per Hedbor  dump( "base_server/throttler.pike" ); dump( "base_server/smartpipe.pike" ); dump( "base_server/slowpipe.pike" ); dump( "base_server/fastpipe.pike" );
753a831999-08-30Per Hedbor  }, 9); switch(getenv("LANG")) { case "sv":
2537c32000-02-14Per Hedbor  default_locale = RoxenLocale["svenska"];
753a831999-08-30Per Hedbor  break; case "jp":
2537c32000-02-14Per Hedbor  default_locale = RoxenLocale["nihongo"];
753a831999-08-30Per Hedbor  break;
e6f83d2000-03-06Peter Bortas  case "de": default_locale = RoxenLocale["deutsch"]; break;
753a831999-08-30Per Hedbor  }
59912f1998-10-15Henrik Grubbström (Grubba)  SET_LOCALE(default_locale);
c245691997-10-25Per Hedbor  initiate_languages();
cde8d62000-02-16Per Hedbor  dump( "languages/abstract.pike" );
b1fca01996-11-12Per Hedbor  mixed tmp;
81f8af1999-12-20Martin Nilsson 
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 += "/";
14179b1997-01-29Per Hedbor  // Dangerous...
6ca8f61998-10-13Per Hedbor  if(tmp = Getopt.find_option(argv, "r", "root")) fix_root(tmp);
b1fca01996-11-12Per Hedbor  argv -= ({ 0 });
51643e1997-08-21Per Hedbor  argc = sizeof(argv);
b1fca01996-11-12Per Hedbor  define_global_variables(argc, argv);
95b69e1999-09-05Per Hedbor 
b796b51998-11-18Per Hedbor  object o;
2537c32000-02-14Per Hedbor  if(QUERY(locale) != "standard" && (o = RoxenLocale[QUERY(locale)]))
b796b51998-11-18Per Hedbor  { default_locale = o; SET_LOCALE(default_locale); }
b1fca01996-11-12Per Hedbor #if efun(syslog) init_logger(); #endif init_garber(); initiate_supports();
1d7d6d2000-02-16Per Hedbor  initiate_argcache();
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));
1766be2000-02-16Per Hedbor  if( Getopt.find_option( argv, 0, "no-delayed-load" ) ) enable_configurations_modules();
11f1922000-02-16Per Hedbor  else foreach( configurations, object c ) if( c->query( "no_delayed_load" ) ) c->enable_all_modules();
81f8af1999-12-20Martin Nilsson 
b1fca01996-11-12Per Hedbor  call_out(update_supports_from_roxen_com, QUERY(next_supports_update)-time());
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) catch( signal(signum(sig), exit_when_done) ); catch( signal(signum("SIGHUP"), reload_all_configurations) );
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) catch( signal(signum(sig), 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)) call_out (restart,60*60*24*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)
ee8b201998-07-13David Hedbor  call_out(restart,60*60*24*QUERY(suicide_timeout)); else remove_call_out(restart); break;
b796b51998-11-18Per Hedbor  case "locale": object o;
280e2b2000-03-04Henrik Grubbström (Grubba)  if(o = RoxenLocale[value])
b796b51998-11-18Per Hedbor  { default_locale = o; SET_LOCALE(default_locale);
280e2b2000-03-04Henrik Grubbström (Grubba)  } else { return sprintf("No such locale: %O\n", value);
b796b51998-11-18Per Hedbor  } break;
edc9af1998-07-11David Hedbor  } }
9fd61c1999-02-16Per Hedbor  mapping config_cache = ([ ]); mapping host_accuracy_cache = ([]); 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 
a6ceb52000-04-03Martin Stjernholm array(RoxenModule) configuration_auth=({});
b5595b2000-01-07Martin Nilsson mapping configuration_perm=([]);
2f74c32000-01-21Per Hedbor 
3b03962000-04-25Martin Stjernholm void fix_configuration_auth() {
f526692000-05-16Henrik Grubbström (Grubba)  foreach (configurations, object c) if (!c->inited && c->retrieve("EnabledModules", c)["config_userdb#0"]) c->enable_all_modules(); configuration_auth -= ({0});
a6ceb52000-04-03Martin Stjernholm }
2f74c32000-01-21Per Hedbor void add_permission(string name, mapping desc) {
f526692000-05-16Henrik Grubbström (Grubba)  fix_configuration_auth();
2f74c32000-01-21Per Hedbor  configuration_perm[ name ]=desc; configuration_auth->add_permission( name, desc );
c616962000-01-18Martin Nilsson }
2f74c32000-01-21Per Hedbor  void remove_configuration_auth(RoxenModule o) {
c616962000-01-18Martin Nilsson  configuration_auth-=({o}); }
2f74c32000-01-21Per Hedbor  void add_configuration_auth(RoxenModule o) {
a1c1d02000-03-30Martin Nilsson  if(!o->auth || !functionp(o->auth)) return;
2f74c32000-01-21Per Hedbor  configuration_auth|=({o}); } string configuration_authenticate(RequestID id, string what) { if(!id->realauth) return 0;
f526692000-05-16Henrik Grubbström (Grubba)  fix_configuration_auth();
1b42792000-03-24Martin Stjernholm 
c616962000-01-18Martin Nilsson  array auth; RoxenModule o;
2f74c32000-01-21Per Hedbor  foreach(configuration_auth, o) {
c616962000-01-18Martin Nilsson  auth=o->auth( ({"",id->realauth}), id); if(auth) break; }
2f74c32000-01-21Per Hedbor  if(!auth) return 0; if(!auth[0]) return 0;
1b42792000-03-24Martin Stjernholm  if( o->find_admin_user( auth[1] )->auth( what ) ) {
c616962000-01-18Martin Nilsson  return auth[1];
1b42792000-03-24Martin Stjernholm  }
c616962000-01-18Martin Nilsson  return 0;
b5595b2000-01-07Martin Nilsson }
2f74c32000-01-21Per Hedbor  array(object) get_config_users( string uname ) {
f526692000-05-16Henrik Grubbström (Grubba)  fix_configuration_auth();
2f74c32000-01-21Per Hedbor  return configuration_auth->find_admin_user( uname ); }
4cf7832000-02-16Per Hedbor array(string|object) list_config_users(string uname, string|void required_auth)
2f74c32000-01-21Per Hedbor {
f526692000-05-16Henrik Grubbström (Grubba)  fix_configuration_auth();
2f74c32000-01-21Per Hedbor  array users = `+( ({}), configuration_auth->list_admin_users( ) ); if( !required_auth ) return users; array res = ({ }); foreach( users, string q ) { foreach( get_config_users( q ), object o ) if( o->auth( required_auth ) ) res += ({ o }); } return res; }