1720541998-10-04Henrik Grubbström (Grubba) /*
7256451999-12-08Per Hedbor  * $Id: roxen.pike,v 1.367 1999/12/08 12:24:08 per Exp $
1720541998-10-04Henrik Grubbström (Grubba)  * * The Roxen Challenger main program. * * Per Hedbor, Henrik Grubbström, Pontus Hagland, David Hedbor and others. */
edc9af1998-07-11David Hedbor  // ABS and suicide systems contributed freely by Francesco Chemolli
7256451999-12-08Per Hedbor constant cvs_version="$Id: roxen.pike,v 1.367 1999/12/08 12:24:08 per Exp $";
34d3fa1999-04-22Per Hedbor  object backend_thread;
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 
9920831999-12-07Martin Stjernholm // #define SSL3_DEBUG
60a9121999-10-10Henrik Grubbström (Grubba) 
0f8b2f1999-03-27Henrik Grubbström (Grubba) /* * Version information */ constant __roxen_version__ = "1.4";
c5e0961999-10-04Per Hedbor constant __roxen_build__ = "0";
0f8b2f1999-03-27Henrik Grubbström (Grubba)  #ifdef __NT__
753a831999-08-30Per Hedbor string real_version= "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__+" NT";
0f8b2f1999-03-27Henrik Grubbström (Grubba) #else
753a831999-08-30Per Hedbor string real_version= "Roxen Challenger/"+__roxen_version__+"."+__roxen_build__;
0f8b2f1999-03-27Henrik Grubbström (Grubba) #endif
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); void deflocaledoc( string locale, string variable, string name, string doc, 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(); mapping query_if_callers(); string info(object conf); string comment(); }
7c92b91998-11-19Per Hedbor class RequestID { 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;
7c92b91998-11-19Per Hedbor  multiset(string) prestate; multiset(string) config; multiset(string) supports; multiset(string) pragma; array(string) client; array(string) referer; Stdio.File my_fd; string prot; string clientprot; string method; 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(); };
5964251999-11-19Per Hedbor mapping old_programs = set_weak_flag( ([]),1 );
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 ); string fname = search( master()->programs, o ); if( !fname ) if( old_programs[ o ] )
b422061999-11-19Per Hedbor  fname = old_programs[ 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 /* * The privilege changer. * * Based on privs.pike,v 1.36. */
0f8b2f1999-03-27Henrik Grubbström (Grubba) int privs_level;
c5e0961999-10-04Per Hedbor static class Privs {
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  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);
c5e0961999-10-04Per Hedbor  else {
0f8b2f1999-03-27Henrik Grubbström (Grubba)  u = getpwnam(uid); if(u) uid = u[2]; }
c5e0961999-10-04Per Hedbor  if(u && !gid) gid = u[3];
0f8b2f1999-03-27Henrik Grubbström (Grubba) 
c5e0961999-10-04Per Hedbor  if(!u) { if (uid && (uid != "root")) { if (intp(uid) && (uid >= 60000)) {
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! */ perror("Privs: WARNING: Assuming nobody-group.\n" "Trying some alternatives...\n"); // Assume we want the nobody group, and try a couple of alternatives foreach(({ 60001, 65534, -2 }), gid2) { perror("%d... ", gid2); if (initgroups(u[0], gid2) >= 0) { if ((err = setegid(new_gid = gid2)) >= 0) { perror("Success!\n"); break; } } } } #endif /* HPUX_KLUDGE */ if (err < 0) { perror("Privs: Failed\n"); throw(({ sprintf("Failed to set EGID to %d\n", gid), backtrace() })); } perror("Privs: WARNING: Set egid to %d instead of %d.\n", 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; #ifdef DEBUG int uid = geteuid(); if (uid != new_uid) { report_warning(sprintf("Privs: UID #%d differs from expected #%d\n" "%s\n", uid, new_uid, describe_backtrace(backtrace()))); } int gid = getegid(); if (gid != new_gid) { report_warning(sprintf("Privs: GID #%d differs from expected #%d\n" "%s\n", gid, new_gid, describe_backtrace(backtrace()))); } #endif /* DEBUG */ 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 */ } #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); }
0f8c871997-06-01Henrik Grubbström (Grubba) #ifdef MODULE_DEBUG
95b69e1999-09-05Per Hedbor #define MD_PERROR(X) roxen_perror X;
0f8c871997-06-01Henrik Grubbström (Grubba) #else #define MD_PERROR(X) #endif /* MODULE_DEBUG */
2ff8461999-09-02Per Hedbor #ifndef THREADS
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 
c31f7b1998-10-10Henrik Grubbström (Grubba) // Locale support
d00fcc1999-09-05Per Hedbor Locale.Roxen.standard default_locale=Locale.Roxen.standard;
fb7ef91998-11-02Per Hedbor object fonts;
c5e0961999-10-04Per Hedbor #if constant( thread_local )
59912f1998-10-15Henrik Grubbström (Grubba) object locale = thread_local(); #else
df022f1998-11-22Per Hedbor 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) 
dca5dc1998-09-12Per Hedbor program Configuration; /*set in create*/
b1fca01996-11-12Per Hedbor 
48fa361997-04-05Per Hedbor array configurations = ({});
b1fca01996-11-12Per Hedbor 
4f4bc11998-02-04Per Hedbor 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) { // Die nicely.
3107101998-09-01Marcus Comstedt #ifdef THREADS
c974c91999-06-27Per Hedbor  catch( stop_handler_threads() );
3107101998-09-01Marcus Comstedt #endif /* THREADS */
a9d8111998-09-01Henrik Grubbström (Grubba)  exit(exit_code); // Now we die... } // Shutdown Roxen // exit_code = 0 True shutdown // exit_code = -1 Restart private static void low_shutdown(int exit_code) {
c5e0961999-10-04Per Hedbor  catch { configurations->stop(); int pid; if (exit_code) { roxen_perror("Restarting Roxen.\n"); } else { roxen_perror("Shutting down Roxen.\n"); // exit(0); } }; call_out(really_low_shutdown, 0.01, 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.
f4e1b71999-10-08Per Hedbor void restart(float|void i) { call_out(low_shutdown, i, -1); } void shutdown(float|void i) { call_out(low_shutdown, i, 0); }
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.
3aaaa71997-06-12Wilhelm Köhler void unthreaded_handle(function f, mixed ... args) { 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
6000961999-04-24Henrik Grubbström (Grubba) // #define THREAD_DEBUG
34fbbc1998-02-05Henrik Grubbström (Grubba) 
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 ));
c5e0961999-10-04Per Hedbor #ifdef THREAD_DEBUG
dca5dc1998-09-12Per Hedbor  roxen_perror(id+" started\n");
c5e0961999-10-04Per Hedbor #endif
4f4bc11998-02-04Per Hedbor  return t; }
1720541998-10-04Henrik Grubbström (Grubba) // Queue of things to handle. // An entry consists of an array(function fp, array args)
3107101998-09-01Marcus Comstedt static object (Thread.Queue) handle_queue = Thread.Queue();
1720541998-10-04Henrik Grubbström (Grubba)  // Number of handler threads that are alive.
3107101998-09-01Marcus Comstedt static int thread_reap_cnt;
14179b1997-01-29Per Hedbor  void handler_thread(int id) {
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 {
6000961999-04-24Henrik Grubbström (Grubba) #ifdef THREAD_DEBUG
75cdcb1999-03-29Henrik Grubbström (Grubba)  werror("Handle thread ["+id+"] waiting for next event\n");
6000961999-04-24Henrik Grubbström (Grubba) #endif /* THREAD_DEBUG */
3107101998-09-01Marcus Comstedt  if((h=handle_queue->read()) && h[0]) {
6000961999-04-24Henrik Grubbström (Grubba) #ifdef THREAD_DEBUG
75cdcb1999-03-29Henrik Grubbström (Grubba)  werror(sprintf("Handle thread [%O] calling %O(@%O)...\n",
6000961999-04-24Henrik Grubbström (Grubba)  id, h[0], h[1..])); #endif /* THREAD_DEBUG */
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. werror("Handle thread ["+id+"] stopped\n"); thread_reap_cnt--; return;
45cae31998-03-06Henrik Grubbström (Grubba)  } } while(1); }) {
6000961999-04-24Henrik Grubbström (Grubba)  report_error(/* LOCALE->uncaught_error(*/describe_backtrace(q)/*)*/);
45cae31998-03-06Henrik Grubbström (Grubba)  if (q = catch {h = 0;}) {
c31f7b1998-10-10Henrik Grubbström (Grubba)  report_error(LOCALE-> uncaught_error(describe_backtrace(q)));
45cae31998-03-06Henrik Grubbström (Grubba)  } }
4f4bc11998-02-04Per Hedbor  }
b1fca01996-11-12Per Hedbor }
3aaaa71997-06-12Wilhelm Köhler void threaded_handle(function f, mixed ... args) { handle_queue->write(({f, args })); }
14179b1997-01-29Per Hedbor int number_of_threads; 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 "+
c5e0961999-10-04Per Hedbor  languages["en"]->number( QUERY(numthreads) )
2ff8461999-09-02Per Hedbor  +" threads to handle requests.\n");
a60c4c1997-07-03Henrik Grubbström (Grubba)  }
14179b1997-01-29Per Hedbor  for(; number_of_threads < QUERY(numthreads); number_of_threads++)
4f4bc11998-02-04Per Hedbor  do_thread_create( "Handle thread ["+number_of_threads+"]", handler_thread, number_of_threads );
14179b1997-01-29Per Hedbor }
06583f1997-09-03Per Hedbor 
3107101998-09-01Marcus Comstedt void stop_handler_threads() {
95b69e1999-09-05Per Hedbor  int timeout=10;
2ff8461999-09-02Per Hedbor  roxen_perror("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++; } while(thread_reap_cnt) { if(--timeout<=0) {
2ff8461999-09-02Per Hedbor  roxen_perror("Giving up waiting on threads!\n");
3107101998-09-01Marcus Comstedt  return; }
34d3fa1999-04-22Per Hedbor  sleep(0.1);
3107101998-09-01Marcus Comstedt  } }
95e2b41997-05-25Wilhelm Köhler #endif /* THREADS */
14179b1997-01-29Per Hedbor 
15635b1999-10-10Per Hedbor class fallback_redirect_request
4820e01999-07-27Henrik Grubbström (Grubba) {
df36c61999-10-08Henrik Grubbström (Grubba)  string in = ""; string out; string default_prefix; int port; object f; void die() { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:fallback_redirect_request::die()\n")); #endif /* SSL3_DEBUG */ #if 0 /* Close the file, DAMMIT */ object dummy = Stdio.File(); if (dummy->open("/dev/null", "rw")) dummy->dup2(f); #endif f->close(); destruct(f); destruct(this_object()); } void write_callback(object id) { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:fallback_redirect_request::write_callback()\n")); #endif /* SSL3_DEBUG */ int written = id->write(out); if (written <= 0) die(); out = out[written..]; if (!strlen(out)) die(); } void read_callback(object id, string s) { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:fallback_redirect_request::read_callback(X, \"%s\")\n", s)); #endif /* SSL3_DEBUG */ in += s; string name; string prefix; if (search(in, "\r\n\r\n") >= 0) { // werror(sprintf("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(Array.map(lines[1..], `/, ":"), array header) { if ( (sizeof(header) >= 2) && (lower_case(header[0]) == "host") )
60a9121999-10-10Henrik Grubbström (Grubba)  prefix = "https://" + header[1] - " ";
df36c61999-10-08Henrik Grubbström (Grubba)  } } 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... */
60a9121999-10-10Henrik Grubbström (Grubba)  string ip = (f->query_address(1)/" ")[0]; prefix = "https://" + ip + ":" + port;
df36c61999-10-08Henrik Grubbström (Grubba)  } 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) { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:fallback_redirect_request(X, \"%s\", \"%s\", %d)\n", s, l||"CONFIG PORT", p)); #endif /* SSL3_DEBUG */ f = socket; default_prefix = l; port = p; f->set_nonblocking(read_callback, 0, die); f->set_id(f); read_callback(f, s); } }
c5e0961999-10-04Per Hedbor class Protocol {
761baa1999-12-08Per Hedbor  inherit Stdio.Port: port;
c5e0961999-10-04Per Hedbor  constant name = "unknown"; constant supports_ipless = 0; constant requesthandlerfile = ""; constant default_port = 4711; int port; int refs; string ip; program requesthandler;
ddefa61999-10-04Marcus Comstedt  array(string) sorted_urls = ({}); mapping(string:mapping) urls = ([]);
c5e0961999-10-04Per Hedbor 
ddefa61999-10-04Marcus Comstedt  void ref(string name, mapping data)
c5e0961999-10-04Per Hedbor  {
ddefa61999-10-04Marcus Comstedt  if(urls[name]) return;
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)
c5e0961999-10-04Per Hedbor  {
ddefa61999-10-04Marcus Comstedt  if(!urls[name]) return; m_delete(urls, name); sorted_urls -= ({name});
c5e0961999-10-04Per Hedbor  if( !--refs ) destruct( ); // Close the port. } void got_connection() { object q = accept( ); if( !q ) ;// .. errno stuff here ..
df36c61999-10-08Henrik Grubbström (Grubba)  else { // FIXME: Add support for ANY => specific IP here.
ddefa61999-10-04Marcus Comstedt  requesthandler( q, this_object() );
df36c61999-10-08Henrik Grubbström (Grubba)  }
ddefa61999-10-04Marcus Comstedt  } object find_configuration_for_url( string url, RequestID id ) {
37c1b31999-10-12Per Hedbor // werror("find configuration for '"+url+"'\n");
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; }
ddefa61999-10-04Marcus Comstedt  return urls[ in ]->conf; } } // Ouch. return values( urls )[0]->conf;
c5e0961999-10-04Per Hedbor  }
10bdc51999-10-08Henrik Grubbström (Grubba)  void set_option_default(string option, mixed value) { mapping all_options = QUERY(port_options); all_options[""][option] = value; // FIXME: Mark as changed? // FIXME: Save? }
df36c61999-10-08Henrik Grubbström (Grubba)  mixed query_option(string option) { // FIXME: Cache? mapping(string:mapping(string:mixed)) all_options = QUERY(port_options); mixed res; mapping val; // Any protocol specific settiongs? if (val = all_options[name]) { mapping val2; // Any ip specific settings? if (val2 = val[ip]) { mapping val3; // Any port specific settings? if (val3 = val2[port]) { if (!zero_type(res = val3[option])) { return res; } } if (!zero_type(res = val2[""][option])) { return res; } } if (!zero_type(res = val[""][option])) { return res; } }
15635b1999-10-10Per Hedbor  if( all_options[""] ) return all_options[""][option]; return 0;
df36c61999-10-08Henrik Grubbström (Grubba)  }
c5e0961999-10-04Per Hedbor  void create( int pn, string i ) {
dd7e661999-10-09Henrik Grubbström (Grubba)  if (query_option("do_not_bind")) { // This is useful if you run two Roxen processes, // that both handle an URL which has two IPs, and // use DNS round-robin. report_warning(sprintf("Binding to %s://%s:%d/ disabled\n", name, ip, port)); destruct(); }
c5e0961999-10-04Per Hedbor  if( !requesthandler ) requesthandler = (program)requesthandlerfile; port = pn; ip = i;
df36c61999-10-08Henrik Grubbström (Grubba) 
c5e0961999-10-04Per Hedbor  ::create();
b6fb051999-11-02Per Hedbor  if(!bind( port, got_connection, ip )) { report_error("Failed to bind %s://%s:%d/ (%s)\n", (string)name, (ip||"*"), (int)port, strerror( errno() ));
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); }
c5e0961999-10-04Per Hedbor  }
b6fb051999-11-02Per Hedbor  string _sprintf( ) { return "Protocol("+name+"://"+ip+":"+port+")"; }
c5e0961999-10-04Per Hedbor }
4820e01999-07-27Henrik Grubbström (Grubba) 
df36c61999-10-08Henrik Grubbström (Grubba) class SSLProtocol { inherit Protocol;
479d8a1999-11-25Henrik Grubbström (Grubba) #if constant(Standards) && constant(Standards.PKCS.RSA) && 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() {
60a9121999-10-10Henrik Grubbström (Grubba)  if (sslfile) { sslfile->close(); }
df36c61999-10-08Henrik Grubbström (Grubba)  } void create(object q, object ctx) { sslfile = SSL.sslfile(q, ctx); } } object accept() { object q = ::accept(); if (q) { return destruct_protected_sslfile(q, ctx); } return 0; } void create(int pn, string i) { ctx = SSL.context(); object privs = Privs("Reading cert file"); string f = Stdio.read_file(query_option("ssl_cert_file") || "demo_certificate.pem"); string f2 = query_option("ssl_key_file") && Stdio.read_file(query_option("ssl_key_file")); if (privs) destruct(privs); if (!f) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: Reading cert-file failed!\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } object msg = Tools.PEM.pem_msg()->init(f); object part = msg->parts["CERTIFICATE"] ||msg->parts["X509 CERTIFICATE"]; string cert; if (!part || !(cert = part->decoded_body())) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: No certificate found.\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } if (query_option("ssl_key_file")) { if (!f2) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: Reading key-file failed!\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } msg = Tools.PEM.pem_msg()->init(f2); } function r = Crypto.randomness.reasonably_random()->read; #ifdef SSL3_DEBUG werror(sprintf("ssl3: key file contains: %O\n", indices(msg->parts))); #endif if (part = msg->parts["RSA PRIVATE KEY"]) { string key; if (!(key = part->decoded_body())) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: Private rsa key not valid (PEM).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } object rsa = Standards.PKCS.RSA.parse_private_key(key); if (!rsa) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: Private rsa key not valid (DER).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } ctx->rsa = rsa; #ifdef SSL3_DEBUG
dd7e661999-10-09Henrik Grubbström (Grubba)  report_debug(sprintf("ssl3: RSA key size: %d bits\n", rsa->rsa_size()));
df36c61999-10-08Henrik Grubbström (Grubba) #endif if (rsa->rsa_size() > 512) { /* Too large for export */ ctx->short_rsa = Crypto.rsa()->generate_key(512, r); // ctx->long_rsa = Crypto.rsa()->generate_key(rsa->rsa_size(), r); } ctx->rsa_mode(); object tbs = Tools.X509.decode_certificate (cert); if (!tbs) {
dd7e661999-10-09Henrik Grubbström (Grubba)  report_error("ssl3: Certificate not valid (DER).\n");
df36c61999-10-08Henrik Grubbström (Grubba)  destruct(); return; } 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; 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; } object dsa = Standards.PKCS.DSA.parse_private_key(key); 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; } #ifdef SSL3_DEBUG
dd7e661999-10-09Henrik Grubbström (Grubba)  report_debug(sprintf("ssl3: Using DSA key.\n"));
df36c61999-10-08Henrik Grubbström (Grubba) #endif dsa->use_random(r); ctx->dsa = dsa; /* Use default DH parameters */ ctx->dh_params = SSL.cipher.dh_parameters(); ctx->dhe_dss_mode(); // FIXME: Add cert <-> private key check. } else {
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) */ void create(int pn, string i) { report_error("No SSL support\n"); 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; // 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; case "multipart/form-data": object messg = MIME.Message(o->data, o->misc); mapping misc = o->misc; foreach(messg->body_parts, object part) { if(part->disp_params->filename) { 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 = (<>); } 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; case "Commonlog": object f = Stdio.File( query("log_file"), "wca" ); l->log_as_commonlog_to_file( f ); destruct(f); break; } cdel--; if(cdel < 1) cdel=1; } else { cdel++; // werror("nolog..\n"); } call_out(do_log, cdel); } void low_adjust_stats(mapping m) {
7256451999-12-08Per Hedbor  #define PCT(X) ((int)(((X)/(float)(m->total+0.1))*100)) werror("\nCache statistics\n"); m->total = m->hits + m->misses + m->stale; werror(" %d elements in cache, size is %1.1fKb max is %1.1fMb\n" " %d cache lookups, %d%% hits, %d%% misses and %d%% stale.\n", m->entries, m->size/1024.0, m->max_size/(1024*1024.0), m->total, PCT(m->hits), PCT(m->misses), PCT(m->stale));
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); low_adjust_stats( l->cache_status() ); } void create( int pn, string i ) { requesthandler = (program)"protocols/fhttp.pike"; if (query_option("do_not_bind")) { // This is useful if you run two Roxen processes, // that both handle an URL which has two IPs, and // use DNS round-robin. report_warning(sprintf("Binding to %s://%s:%d/ disabled\n", name, ip, port)); destruct(); return; } port = pn; ip = i; dolog = (query_option( "log" ) && (query_option( "log" )!="none")); 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; } l = HTTPLoop.Loop( portobj, requesthandler, handle_request, 0, (query_option("ram_cache")||20)*1024*1024, 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; }
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) 
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) { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:http_fallback(X, %O, \"%s\")\n", n, data)); #endif /* SSL3_DEBUG */ // trace(1); #if 0 werror(sprintf("ssl3->http_fallback: alert(%d, %d)\n" "seq_num = %s\n" "data = '%s'", alert->level, alert->description, (string) n, data)); #endif 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, my_fd->config && my_fd->config->query("MyWorldLocation"), port); destruct(my_fd); destruct(this_object()); // my_fd = 0; /* Forget ssl-object */ } } void ssl_accept_callback(object id) { #ifdef SSL3_DEBUG roxen_perror(sprintf("SSL3:ssl_accept_callback(X)\n")); #endif /* SSL3_DEBUG */ 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); } } object accept() { object q = ::accept(); if (q) { http_fallback(q); } return q; } #endif /* constant(SSL.sslfile) */
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;
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; }
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)  "https":HTTPS, "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 } void unregister_url( string 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  } }
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 ) {
07c9921999-11-23Per Hedbor  report_notice("Register "+url+" for "+conf->query_name()+"\n");
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 ); sscanf( host, "%[^:]:%d", host, port ); 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+ ", already registerd by " + 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+ ", cannot find the protocol " + protocol + "!\n" ); return 0; } if( !port ) port = prot->default_port;
dd7e661999-10-09Henrik Grubbström (Grubba)  array(string) required_hosts;
b6fb051999-11-02Per Hedbor  /* if( !prot->supports_ipless )*/
dd7e661999-10-09Henrik Grubbström (Grubba)  required_hosts = find_ips_for( host );
b6fb051999-11-02Per Hedbor  if (!required_hosts)
dd7e661999-10-09Henrik Grubbström (Grubba)  required_hosts = ({ 0 }); // ANY
b6fb051999-11-02Per Hedbor 
c5e0961999-10-04Per Hedbor  mapping m; if( !( m = open_ports[ protocol ] ) ) m = open_ports[ protocol ] = ([]); urls[ url ] = ([ "conf":conf, "path":path ]); sorted_urls += ({ url });
dd7e661999-10-09Henrik Grubbström (Grubba)  int failures;
c5e0961999-10-04Per Hedbor 
dd7e661999-10-09Henrik Grubbström (Grubba)  foreach(required_hosts, string required_host) { if( m[ required_host ] && m[ required_host ][ port ] ) { m[ required_host ][ port ]->ref(url, urls[url]); urls[ url ]->port = prot; continue; /* No need to open a new port */ } if( !m[ required_host ] ) m[ required_host ] = ([ ]); m[ required_host ][ port ] = prot( port, required_host ); 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(); 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;
2856a41999-11-15Per Hedbor // werror(" is not '"+o->name+"'\n" );
9699bf1999-10-11Per Hedbor  }
0666741999-12-07Henrik Grubbström (Grubba)  return 0;
b1fca01996-11-12Per Hedbor }
c5e0961999-10-04Per Hedbor // Create a new configuration from scratch. // 'type' is as in the form. 'none' for a empty configuration. int add_new_configuration(string name, string type) { }
025d221997-06-01Henrik Grubbström (Grubba) 
c5e0961999-10-04Per Hedbor mapping(string:array(int)) error_log=([]);
ec2fe11997-06-09Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor // Write a string to the configuration interface error log and to stderr.
e99fd41999-11-17Per Hedbor void nwrite(string s, int|void perr, int|void type, int|void mod, int|void conf)
b1fca01996-11-12Per Hedbor {
c5e0961999-10-04Per Hedbor  if (!error_log[type+","+s])
9b9f701997-08-12Per Hedbor  error_log[type+","+s] = ({ time() });
c5e0961999-10-04Per Hedbor  else
9b9f701997-08-12Per Hedbor  error_log[type+","+s] += ({ time() });
e99fd41999-11-17Per Hedbor  if( mod ) { if( !mod->error_log ) mod->error_log = ([]); mod->error_log[type+","+s] += ({time()}); } if( conf ) { if( !conf->error_log ) conf->error_log = ([]); conf->error_log[type+","+s] += ({time()}); }
c5e0961999-10-04Per Hedbor  if(type >= 1)
af4c801999-11-19Per Hedbor  werror( 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() {
71a11e1997-08-13Henrik Grubbström (Grubba)  return QUERY(default_ident)?real_version:QUERY(ident);
b1fca01996-11-12Per Hedbor } public void log(mapping file, object request_id) {
14179b1997-01-29Per Hedbor  if(!request_id->conf) return; request_id->conf->log(file, request_id);
b1fca01996-11-12Per Hedbor } // Support for unique user id's 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; } current_user_id_number = (int)current_user_id_file->read(100); current_user_id_file_last_mod = current_user_id_file->stat()[2]; perror("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++; //perror("New unique id: "+current_user_id_number+"\n"); 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();
b1fca01996-11-12Per Hedbor 
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?
b1fca01996-11-12Per Hedbor  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 
c31f7b1998-10-10Henrik Grubbström (Grubba)  return(LOCALE->full_status(real_version, boot_time, start_time-boot_time, 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 }
ee8b201998-07-13David Hedbor static int abs_started;
6ca8f61998-10-13Per Hedbor  void restart_if_stuck (int force) {
ee8b201998-07-13David Hedbor  remove_call_out(restart_if_stuck); if (!(QUERY(abs_engage) || force))
edc9af1998-07-11David Hedbor  return;
6ca8f61998-10-13Per Hedbor  if(!abs_started) {
ee8b201998-07-13David Hedbor  abs_started = 1;
dca5dc1998-09-12Per Hedbor  roxen_perror("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 ) {
b0659e1999-07-20Henrik Grubbström (Grubba)  werror(sprintf("**** %s: ABS engaged!\n" "Trying to dump backlog: \n", ctime(time()) - "\n")); catch { // Catch for paranoia reasons. describe_all_threads(); }; werror(sprintf("**** %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 }
6ca8f61998-10-13Per Hedbor void post_create () {
ee8b201998-07-13David Hedbor  if (QUERY(abs_engage)) call_out (restart_if_stuck,10);
edc9af1998-07-11David Hedbor  if (QUERY(suicide_engage))
ee8b201998-07-13David Hedbor  call_out (restart,60*60*24*QUERY(suicide_timeout));
edc9af1998-07-11David Hedbor }
59912f1998-10-15Henrik Grubbström (Grubba) 
c5e0961999-10-04Per Hedbor // Cache used by the various configuration interface modules etc. // It should be OK to delete this cache at any time. class ConfigIFCache { string dir; void create( string name ) { dir = "config_caches/"+replace(configuration_dir-".", "/", "-") + "/" + name + "/"; mkdirhier( dir+"/foo" ); } mixed set( string name, mixed to ) { Stdio.File f = Stdio.File(); if(!f->open( dir + replace( name, "/", "-" ), "wct" )) { mkdirhier( dir+"/foo" ); if(!f->open( dir + replace( name, "/", "-" ), "wct" )) { report_error("Failed to create configuration interface cache file ("+ dir + replace( name, "/", "-" )+") "+ strerror( errno() )+"\n"); return to; } } f->write( encode_value( to ) ); return to; } mixed get( string name ) { Stdio.File f = Stdio.File(); if(!f->open( dir + replace( name, "/", "-" ), "r" )) return 0; return decode_value( f->read() ); }
6b34861999-11-15Per Hedbor  array list() { return get_dir( dir ); }
c5e0961999-10-04Per Hedbor  void delete( string name ) { rm( dir + replace( name, "/", "-" ) ); } }
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) 
34d3fa1999-04-22Per Hedbor 
2ff8461999-09-02Per Hedbor  static mapping meta_cache_insert( string i, mapping what ) { return meta_cache[i] = what; } 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; 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 
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; 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 
2ff8461999-09-02Per Hedbor  if( format == "jpg" ) format = "jpeg";
b1fca01996-11-12Per Hedbor 
2ff8461999-09-02Per Hedbor  if(mappingp(reply)) { alpha = reply->alpha; reply = reply->img;
93ebdd1999-03-11Martin Stjernholm  }
2ff8461999-09-02Per Hedbor  if( args->gamma ) reply = reply->gamma( (float)args->gamma );
b79be21999-06-11Per Hedbor 
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"] ) {
c3a53d1999-06-25Per Hedbor  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 ) { object 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 { alpha = Image.image( reply->xsize(), reply->ysize(), ov,ov,ov ); } }
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  } }
e7e6031999-11-05Per Hedbor  if( args->maxwidth || args->maxheight || 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 ); } }
e7e6031999-11-05Per Hedbor  if( bgcolor && alpha ) { reply = Image.Image( reply->xsize(), reply->ysize(), bgcolor ) ->paste_mask( reply, alpha ); }
69a8691999-05-18Per Hedbor  if( quant || (format=="gif") ) {
e9d7c51999-11-02Per Hedbor  int ncols = quant||id->misc->defquant||32;
7ed2ef1999-05-25Peter Bortas  if( ncols > 250 )
000c781999-05-25Per Hedbor  ncols = 250; 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(); }
3760171999-06-27Per Hedbor  if(!Image[upper_case( format )] || !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  {
e9d7c51999-11-02Per Hedbor  object bw=Image.Colortable( ({ ({ 0,0,0 }), ({ 255,255,255 }) }) ); 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" );
69a8691999-05-18Per Hedbor  default: data = Image[upper_case( format )]->encode( reply, enc_args ); }
e9d7c51999-11-02Per Hedbor  meta = ([
69a8691999-05-18Per Hedbor  "xsize":reply->xsize(), "ysize":reply->ysize(), "type":"image/"+format, ]);
c526921999-05-18Per Hedbor  }
69a8691999-05-18Per Hedbor  else if( mappingp(reply) ) { 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 );
b503621999-07-27Per Hedbor  Stdio.File f = Stdio.File( ); 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 ) {
b503621999-07-27Per Hedbor  Stdio.File f = Stdio.File( ); 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 ]; f = Stdio.File( ); if( !f->open(dir+id+".i", "r" ) ) return 0; return meta_cache_insert( id, decode_value( f->read() ) ); } static mapping restore( string id ) { string|object(Stdio.File) f; mapping m; if( data_cache[ id ] ) f = data_cache[ id ]; else f = Stdio.File( );
3760171999-06-27Per Hedbor  if(!f->open(dir+id+".d", "r" ))
69a8691999-05-18Per Hedbor  return 0; m = restore_meta( id ); if(!m) return 0; if( stringp( f ) )
2ff8461999-09-02Per Hedbor  return roxenp()->http_string_answer( f, m->type||("image/gif") );
69a8691999-05-18Per Hedbor  return roxenp()->http_file_answer( f, m->type||("image/gif") ); } 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; }
c3a53d1999-06-25Per Hedbor  mapping http_file_answer( string|mapping data, RequestID id, 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 ) { if(!d) d = roxenp()->QUERY(argument_cache_dir); 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; static object db; #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 # define LOCK() #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"); } void create( string _name, string _path, 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" ); object test = Stdio.File(); if (!test->open (path + "/.testfile", "wc")) error ("Can't create files in the argument cache directory " + path + "\n"); else { test->close(); rm (path + "/.testfile"); } } } static string read_args( string id ) { if( is_db ) { mapping res = db->query("select contents from "+name+" where id='"+id+"'"); if( sizeof(res) ) { db->query("update "+name+" set atime='"+ time()+"' where id='"+id+"'"); return res[0]->contents; } return 0; } else { if( file_stat( path+id ) ) return Stdio.read_bytes(path+"/"+id); } return 0; } static string create_key( string long_key ) { if( is_db ) { mapping data = db->query(sprintf("select id,contents from %s where lkey='%s'", 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 " "('%s','%s','%d')", 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]; while( file_stat( path+short_key ) ) { if( Stdio.read_bytes( path+short_key ) == long_key ) return short_key; short_key = _key[..strlen(short_key)]; if( strlen(short_key) >= strlen(_key) ) short_key += "."; // Not very likely... } object f = Stdio.File( path + short_key, "wct" ); f->write( long_key ); return short_key; } } int key_exists( string key ) { LOCK(); if( !is_db ) return !!file_stat( path+key ); 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 ]; string id = create_key( data ); cache[ data ] = ({ 0, 0 }); cache[ data ][ CACHE_VALUE ] = copy_value( args ); cache[ data ][ CACHE_SKEY ] = id; cache[ id ] = data; if( sizeof( cache ) > CACHE_SIZE )
69a8691999-05-18Per Hedbor  {
2ff8461999-09-02Per Hedbor  array i = indices(cache); while( sizeof(cache) > CACHE_SIZE-CLEAN_SIZE ) m_delete( cache, i[random(sizeof(i))] );
69a8691999-05-18Per Hedbor  }
2ff8461999-09-02Per Hedbor  return id;
69a8691999-05-18Per Hedbor  }
2ff8461999-09-02Per Hedbor  mapping lookup( string id, string|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 ); if(!q) error("Key does not exist! (Thinks "+ client +")\n"); 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
2ff8461999-09-02Per Hedbor  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(); cached_decoders[ charset ]->flush(); return data; }
2ff8461999-09-02Per Hedbor void create() { SET_LOCALE(default_locale); // Dump some programs (for speed) 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/fonts.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__ */
640cc41999-11-29Per Hedbor  add_constant("Privs",class{});
69a8691999-05-18Per Hedbor 
855c391999-11-29Per Hedbor 
2ff8461999-09-02Per Hedbor  // for module encoding stuff
d3b98e1999-11-28Per Hedbor  add_constant( "MIME.encode_base64", MIME.encode_base64 ); add_constant( "MIME.decode_base64", MIME.decode_base64 );
95b69e1999-09-05Per Hedbor 
2ff8461999-09-02Per Hedbor  add_constant( "Image", Image ); add_constant( "Image.Image", Image.Image );
95b69e1999-09-05Per Hedbor  add_constant( "Image.Font", Image.Font );
2ff8461999-09-02Per Hedbor  add_constant( "Image.Colortable", Image.Colortable );
95b69e1999-09-05Per Hedbor  add_constant( "Image.Color", Image.Color ); add_constant( "Image.GIF.encode", Image.GIF.encode ); add_constant( "Image.Color.Color", Image.Color.Color ); add_constant( "roxen.argcache", argcache ); add_constant( "ArgCache", ArgCache ); add_constant( "Regexp", Regexp ); add_constant( "Stdio.File", Stdio.File ); add_constant( "Stdio.stdout", Stdio.stdout ); add_constant( "Stdio.stderr", Stdio.stderr ); add_constant( "Stdio.stdin", Stdio.stdin ); add_constant( "Stdio.read_bytes", Stdio.read_bytes ); add_constant( "Stdio.write_file", Stdio.write_file ); add_constant( "Stdio.sendfile", Stdio.sendfile ); add_constant( "Process.create_process", Process.create_process ); add_constant( "roxen.load_image", load_image );
2ff8461999-09-02Per Hedbor #if constant(Thread.Mutex)
95b69e1999-09-05Per Hedbor  add_constant( "Thread.Mutex", Thread.Mutex ); add_constant( "Thread.Queue", Thread.Queue );
2ff8461999-09-02Per Hedbor #endif
95b69e1999-09-05Per Hedbor  add_constant( "roxen", this_object()); 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); add_constant( "ModuleInfo", Module );
95b69e1999-09-05Per Hedbor  add_constant( "load", load); add_constant( "Roxen.set_locale", set_locale ); add_constant( "Roxen.locale", locale ); add_constant( "Locale.Roxen", Locale.Roxen );
d00fcc1999-09-05Per Hedbor  add_constant( "Locale.Roxen.standard", Locale.Roxen.standard ); add_constant( "Locale.Roxen.standard.register_module_doc", Locale.Roxen.standard.register_module_doc );
95b69e1999-09-05Per Hedbor  add_constant( "roxen.ImageCache", ImageCache );
2ff8461999-09-02Per Hedbor  // compatibility
95b69e1999-09-05Per Hedbor  add_constant( "hsv_to_rgb", Colors.hsv_to_rgb ); add_constant( "rgb_to_hsv", Colors.rgb_to_hsv ); add_constant( "parse_color", Colors.parse_color ); add_constant( "color_name", Colors.color_name ); add_constant( "colors", Colors ); add_constant( "roxen.fonts", (fonts = (object)"fonts.pike") );
c821271999-11-11Martin Stjernholm  if ((file_stat("base_server/configuration.pike.o")||(<>))[ST_MTIME] < file_stat("base_server/configuration.pike")[ST_MTIME] || // This since configuration.pike #includes rxml.pike. (file_stat("base_server/configuration.pike.o")||(<>))[ST_MTIME] < file_stat("base_server/rxml.pike")[ST_MTIME]) rm ("base_server/configuration.pike.o");
855c391999-11-29Per Hedbor 
2ff8461999-09-02Per Hedbor  Configuration = (program)"configuration";
2bb9241999-11-24Per Hedbor  dump( "base_server/configuration.pike" );
855c391999-11-29Per Hedbor  add_constant( "Configuration", Configuration );
2ff8461999-09-02Per Hedbor  call_out(post_create,1); //we just want to delay some things a little
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; 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; report_notice(LOCALE->reloading_config_interface()); configs = ([]); setvars(retrieve("Variables", 0)); foreach(list_all_configurations(), string config)
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  array err, st; foreach(configurations, conf)
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  if(lower_case(conf->name) == lower_case(config))
a22f6f1999-05-12Per Hedbor  {
2ff8461999-09-02Per Hedbor  break; } else conf = 0; } 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; if(conf) { // Closing ports... if (conf->server_ports) { // Roxen 1.2.26 or later
479d8a1999-11-25Henrik Grubbström (Grubba)  Array.map(values(conf->server_ports), lambda(object o) { destruct(o); });
2ff8461999-09-02Per Hedbor  } else {
479d8a1999-11-25Henrik Grubbström (Grubba)  Array.map(indices(conf->open_ports), lambda(object o) { destruct(o); });
2ff8461999-09-02Per Hedbor  } conf->stop(); conf->invalidate_cache(); conf->modules = ([]); 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  }
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));
479d8a1999-11-25Henrik Grubbström (Grubba)  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 });
15635b1999-10-10Per Hedbor  report_notice( LOCALE->enabled_server(name) );
2ff8461999-09-02Per Hedbor  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)
15635b1999-10-10Per Hedbor  if(err=catch( enable_configuration(config)->start() )) report_error("Error while loading configuration "+config+":\n"+ describe_backtrace(err)+"\n");
2ff8461999-09-02Per Hedbor }
a22f6f1999-05-12Per Hedbor 
2ff8461999-09-02Per Hedbor void enable_configurations_modules() {
15635b1999-10-10Per Hedbor  mixed err;
2ff8461999-09-02Per Hedbor  foreach(configurations, object config)
15635b1999-10-10Per Hedbor  if(err=catch( config->enable_all_modules() )) report_error("Error while loading modules in configuration "+ config->name+":\n"+describe_backtrace(err)+"\n");
a22f6f1999-05-12Per Hedbor } array(int) invert_color(array color ) { return ({ 255-color[0], 255-color[1], 255-color[2] }); } mapping low_decode_image(string data, void|array tocolor) { Image.image i, a; string format; if(!data) return 0; #if constant(Image.GIF._decode) // Use the low-level decode function to get the alpha channel. catch { array chunks = Image.GIF._decode( data ); // If there is more than one render chunk, the image is probably // an animation. Handling animations is left as an exercise for // the reader. :-) foreach(chunks, mixed chunk) if(arrayp(chunk) && chunk[0] == Image.GIF.RENDER ) [i,a] = chunk[3..4]; format = "GIF"; }; if(!i) catch { i = Image.GIF.decode( data ); format = "GIF"; }; #endif #if constant(Image.JPEG) && constant(Image.JPEG.decode) if(!i) catch { i = Image.JPEG.decode( data ); format = "JPEG"; }; #endif #if constant(Image.XCF) && constant(Image.XCF._decode) if(!i) catch {
c3a53d1999-06-25Per Hedbor  mixed q = Image.XCF._decode( data,(["background":tocolor,]) );
a22f6f1999-05-12Per Hedbor  tocolor=0; format = "XCF Gimp file"; i = q->image; a = q->alpha; }; #endif #if constant(Image.PSD) && constant(Image.PSD._decode) if(!i) catch { mixed q = Image.PSD._decode( data, ([ "background":tocolor, ])); tocolor=0; format = "PSD Photoshop file"; i = q->image; a = q->alpha; }; #endif #if constant(Image.PNG) && constant(Image.PNG._decode) if(!i) catch { mixed q = Image.PNG._decode( data ); format = "PNG"; i = q->image; a = q->alpha; }; #endif #if constant(Image.BMP) && constant(Image.BMP._decode) if(!i) catch { mixed q = Image.BMP._decode( data ); format = "Windows bitmap file"; i = q->image; a = q->alpha; }; #endif #if constant(Image.TGA) && constant(Image.TGA._decode) if(!i) catch { mixed q = Image.TGA._decode( data ); format = "Targa"; i = q->image; a = q->alpha; }; #endif #if constant(Image.PCX) && constant(Image.PCX._decode) if(!i) catch { mixed q = Image.PCX._decode( data ); format = "PCX"; i = q->image; a = q->alpha; }; #endif #if constant(Image.XBM) && constant(Image.XBM._decode) if(!i) catch { mixed q = Image.XBM._decode( data, (["bg":tocolor||({255,255,255}), "fg":invert_color(tocolor||({255,255,255})) ])); format = "XBM"; i = q->image; a = q->alpha; }; #endif #if constant(Image.XPM) && constant(Image.XPM._decode) if(!i) catch { mixed q = Image.XPM._decode( data ); format = "XPM"; i = q->image; a = q->alpha; }; #endif #if constant(Image.TIFF) && constant(Image.TIFF._decode) if(!i) catch { mixed q = Image.TIFF._decode( data ); format = "TIFF"; i = q->image; a = q->alpha; }; #endif #if constant(Image.ILBM) && constant(Image.ILBM._decode) if(!i) catch { mixed q = Image.ILBM._decode( data ); format = "ILBM"; i = q->image; a = q->alpha; }; #endif #if constant(Image.PS) && constant(Image.PS._decode) if(!i) catch { mixed q = Image.PS._decode( data ); format = "Postscript"; i = q->image; a = q->alpha; }; #endif #if constant(Image.XWD) && constant(Image.XWD.decode) if(!i) catch { i = Image.XWD.decode( data ); format = "XWD"; }; #endif #if constant(Image.HRZ) && constant(Image.HRZ._decode) if(!i) catch { mixed q = Image.HRZ._decode( data ); format = "HRZ"; i = q->image; a = q->alpha; }; #endif #if constant(Image.AVS) && constant(Image.AVS._decode) if(!i) catch { mixed q = Image.AVS._decode( data ); format = "AVS X"; i = q->image; a = q->alpha; }; #endif
c5e0961999-10-04Per Hedbor #if constant(Image.PNM)
a22f6f1999-05-12Per Hedbor  if(!i) catch{ i = Image.PNM.decode( data ); format = "PNM"; };
c5e0961999-10-04Per Hedbor #endif
a22f6f1999-05-12Per Hedbor  if(!i) // No image could be decoded at all. return 0; if( tocolor && i && a ) { object o = Image.image( i->xsize(), i->ysize(), @tocolor ); o->paste_mask( i,a ); i = o; } return ([ "format":format, "alpha":a, "img":i, ]); } mapping low_load_image(string f,object id) { string data; object file, img;
69a8691999-05-18Per Hedbor  if(id->misc->_load_image_called < 5) {
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()))
3ffe061999-05-16David Hedbor  return 0; } } id->misc->_load_image_called = 0; if(!data) return 0;
a22f6f1999-05-12Per Hedbor  return low_decode_image( data ); } object load_image(string f,object id) { 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()) { perror("It is impossible to chroot() if the server is not run as root.\n"); return; } if(!chroot(to)) { perror("Roxen: Cannot chroot to "+to+": "); #if efun(real_perror) real_perror(); #endif return; } perror("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; where = replace(where, ({ "$pid", "$uid" }), ({ (string)getpid(), (string)getuid() })); rm(where);
5e89211997-02-13Per Hedbor  if(catch(Stdio.write_file(where, sprintf("%d\n%d", getpid(), getppid()))))
b1fca01996-11-12Per Hedbor  perror("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() { object o; int i;
c31f7b1998-10-10Henrik Grubbström (Grubba)  roxen_perror("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  {
dca5dc1998-09-12Per Hedbor  roxen_perror("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  }
4f4bc11998-02-04Per Hedbor 
c5e0961999-10-04Per Hedbor  roxen_perror("Exiting roxen.\n"); 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() { perror("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 ); SET_LOCALE( Locale.Roxen[ to ] || default_locale ); }
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) */ werror("Describing all threads:\n"); int i; for(i=0; i < sizeof(all_backtraces); i++) { werror(sprintf("Thread %d:\n" "%s\n", i+1, describe_backtrace(all_backtraces[i]))); } }
753a831999-08-30Per Hedbor  void dump( string file ) {
dde8331999-11-15Per Hedbor  if( file[0] != '/' ) file = getcwd() +"/"+ file; program p = master()->programs[ replace(file, "//", "/" ) ];
753a831999-08-30Per Hedbor  array q;
2bb9241999-11-24Per Hedbor 
753a831999-08-30Per Hedbor  if(!p)
2ff8461999-09-02Per Hedbor  { #ifdef DUMP_DEBUG report_debug(file+" not loaded, and thus cannot be dumped.\n"); #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 ) ) ) report_debug("** Cannot encode "+file+": "+describe_backtrace(q)+"\n");
2ff8461999-09-02Per Hedbor #ifdef DUMP_DEBUG
2bb9241999-11-24Per Hedbor  else
95b69e1999-09-05Per Hedbor  report_debug( file+" dumped successfully to "+file+".o\n" );
2ff8461999-09-02Per Hedbor #endif } #ifdef DUMP_DEBUG else report_debug(file+" already dumped (and up to date)\n"); #endif
753a831999-08-30Per Hedbor }
a577791999-11-24Per Hedbor program slowpipe, fastpipe;
95b69e1999-09-05Per Hedbor int main(int argc, array argv)
b1fca01996-11-12Per Hedbor {
2bb9241999-11-24Per Hedbor  mkdir( "precompiled" );
a577791999-11-24Per Hedbor  slowpipe = ((program)"slowpipe"); fastpipe = ((program)"fastpipe");
2bb9241999-11-24Per Hedbor 
753a831999-08-30Per Hedbor  call_out( lambda() {
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" );
95b69e1999-09-05Per Hedbor  dump( "base_server/struct/node.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": default_locale = Locale.Roxen["svenska"]; break; case "jp": default_locale = Locale.Roxen["nihongo"]; break; }
59912f1998-10-15Henrik Grubbström (Grubba)  SET_LOCALE(default_locale);
c245691997-10-25Per Hedbor  initiate_languages();
b1fca01996-11-12Per Hedbor  mixed tmp;
2ff8461999-09-02Per Hedbor 
b1fca01996-11-12Per Hedbor  mark_fd(0, "Stdin"); mark_fd(1, "Stdout"); mark_fd(2, "Stderr");
51643e1997-08-21Per Hedbor  configuration_dir =
6ca8f61998-10-13Per Hedbor  Getopt.find_option(argv, "d",({"config-dir","configuration-directory" }),
51643e1997-08-21Per Hedbor  ({ "ROXEN_CONFIGDIR", "CONFIGURATIONS" }), "../configurations");
b1fca01996-11-12Per Hedbor 
a92b951997-08-05Martin Stjernholm  if(configuration_dir[-1] != '/')
b1fca01996-11-12Per Hedbor  configuration_dir += "/";
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; if(QUERY(locale) != "standard" && (o = Locale.Roxen[QUERY(locale)])) { default_locale = o; SET_LOCALE(default_locale); }
b1fca01996-11-12Per Hedbor #if efun(syslog) init_logger(); #endif init_garber(); initiate_supports();
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));
a22f6f1999-05-12Per Hedbor  roxen_perror("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 ) { report_error( "Failed to initialize the global argument cache:\n" + (describe_backtrace( e )/"\n")[0]+"\n"); werror( describe_backtrace( e ) ); } roxen_perror( "\n" );
acd9e71999-05-12Per Hedbor  enable_configurations_modules();
b1fca01996-11-12Per Hedbor  call_out(update_supports_from_roxen_com, QUERY(next_supports_update)-time());
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.
b1fca01996-11-12Per Hedbor  return -1; }
7a61de1998-03-26Per Hedbor  string diagnose_error(array from) { }
edc9af1998-07-11David Hedbor  // Called from the configuration interface.
ee8b201998-07-13David Hedbor string check_variable(string name, mixed value)
edc9af1998-07-11David Hedbor { switch(name) { case "ConfigurationURL": case "MyWorldLocation": if(strlen(value)<7 || value[-1] != '/' || !(sscanf(value,"%*s://%*s/")==2))
c31f7b1998-10-10Henrik Grubbström (Grubba)  return(LOCALE->url_format());
ee8b201998-07-13David Hedbor  break; case "abs_engage": if (value) restart_if_stuck(1); else remove_call_out(restart_if_stuck); break; case "suicide_engage": if (value) call_out(restart,60*60*24*QUERY(suicide_timeout)); else remove_call_out(restart); break;
b796b51998-11-18Per Hedbor  case "locale": object o; if(value != "standard" && (o = Locale.Roxen[value])) { default_locale = o; SET_LOCALE(default_locale); } break;
edc9af1998-07-11David Hedbor  } }
9fd61c1999-02-16Per Hedbor  mapping config_cache = ([ ]); mapping host_accuracy_cache = ([]); int is_ip(string s) {
753a831999-08-30Per Hedbor  return (replace(s,"0123456789."/"",({""})*11) == "");
9fd61c1999-02-16Per Hedbor }