835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer.
f41b982009-05-07Martin Stjernholm // Copyright © 1996 - 2009, Roxen IS.
835c6c2001-06-17Martin Nilsson //
6b67fe2001-08-24Martin Nilsson // @appears Configuration //! A site's main configuration
0917d32013-03-04Anders Johansson constant cvs_version = "$Id$";
b1fca01996-11-12Per Hedbor #include <module.h>
a59d252000-07-04Per Hedbor #include <module_constants.h>
14179b1997-01-29Per Hedbor #include <roxen.h>
c5e0961999-10-04Per Hedbor #include <request_trace.h>
9c19002001-02-27Per Hedbor #include <timers.h>
8afc811998-02-04Per Hedbor 
c7a5f01999-02-16Per Hedbor #define CATCH(P,X) do{mixed e;if(e=catch{X;})report_error("While "+P+"\n"+describe_backtrace(e));}while(0)
18be211998-05-07Henrik Grubbström (Grubba) 
32cd1c2008-10-13Martin Stjernholm // Tell Pike.count_memory this is global. constant pike_cycle_depth = 0;
23414a2000-07-21Andreas Lange // --- Locale defines --- //<locale-token project="roxen_start"> LOC_S </locale-token> //<locale-token project="roxen_config"> LOC_C </locale-token> //<locale-token project="roxen_message"> LOC_M </locale-token> //<locale-token project="roxen_config"> DLOCALE </locale-token> #define LOC_S(X,Y) _STR_LOCALE("roxen_start",X,Y) #define LOC_C(X,Y) _STR_LOCALE("roxen_config",X,Y) #define LOC_M(X,Y) _STR_LOCALE("roxen_message",X,Y) #define DLOCALE(X,Y) _DEF_LOCALE("roxen_config",X,Y)
18be211998-05-07Henrik Grubbström (Grubba) 
199d031999-09-05Francesco Chemolli #ifdef THROTTLING_DEBUG #undef THROTTLING_DEBUG
91d3c32001-03-12Martin Nilsson #define THROTTLING_DEBUG(X) report_debug("Throttling: "+X+"\n")
199d031999-09-05Francesco Chemolli #else #define THROTTLING_DEBUG(X) #endif
10c7e11999-12-28Martin Nilsson #ifdef REQUEST_DEBUG
91d3c32001-03-12Martin Nilsson # define REQUEST_WERR(X) report_debug("CONFIG: "+X+"\n")
10c7e11999-12-28Martin Nilsson #else # define REQUEST_WERR(X) #endif
4717052001-05-07Per Hedbor  #ifdef AVERAGE_PROFILING
7e16102001-11-01Henrik Grubbström (Grubba)  #if !constant(gethrvtime) #define gethrvtime() gethrtime() #endif /* !constant(gethrvtime) */
4717052001-05-07Per Hedbor class ProfStack { array current_stack = ({}); void enter( string k, RequestID id ) { current_stack += ({ ({ k, gethrtime(), gethrvtime() }) }); } void leave( string k, RequestID id ) { int t0 = gethrtime(); int t1 = gethrvtime(); if( !sizeof(current_stack ) ) {
7937df2001-05-16Per Hedbor // report_error("Popping out of profiling stack\n");
4717052001-05-07Per Hedbor  return; } int i = sizeof( current_stack )-1; while( current_stack[ i ][0] != k && i >= 0 ) i--; if(i < 0 ) { return; }
7937df2001-05-16Per Hedbor  void low_leave( int i ) { int tt = t0-current_stack[i][1]; int ttv = t1-current_stack[i][2]; if( i > 0 ) // Do not count child time in parent. { current_stack[i-1][1]+=tt+gethrtime()-t0; current_stack[i-1][2]+=ttv+gethrvtime()-t1; } current_stack = current_stack[..i-1]; add_prof_entry( id, k, tt, ttv ); };
4717052001-05-07Per Hedbor 
7937df2001-05-16Per Hedbor  if( i != sizeof( current_stack )-1 )
4717052001-05-07Per Hedbor  {
7937df2001-05-16Per Hedbor  for( int j = sizeof( current_stack )-1; j>=i; j-- ) low_leave( j ); return;
4717052001-05-07Per Hedbor  }
7937df2001-05-16Per Hedbor  low_leave( i );
4717052001-05-07Per Hedbor  } } class ProfInfo( string url ) { mapping data = ([]); void add( string k, int h, int hrv ) { if( !data[k] ) data[k] = ({ h, hrv, 1 }); else { data[k][0]+=h; data[k][1]+=hrv; data[k][2]++; } } array summarize_table( ) { array table = ({}); int n, t, v; foreach( indices( data ), string k ) table += ({ ({ k, sprintf( "%d", (n=data[k][2]) ), sprintf("%5.2f",(t=data[k][0])/1000000.0), sprintf("%5.2f", (v=data[k][1])/1000000.0), sprintf("%8.2f", t/n/1000.0), sprintf("%8.2f",v/n/1000.0), }) }); sort( (array(float))column(table,2), table ); return reverse(table); } void dump( ) { write( "\n"+url+": \n" );
ab1e022002-03-06Henrik Grubbström (Grubba)  ADT.Table.table t = ADT.Table.table( summarize_table(), ({ "What", "Calls", "Time", "CPU", "t/call(ms)", "cpu/call(ms)" }));
4717052001-05-07Per Hedbor  write( ADT.Table.ASCII.encode( t )+"\n" ); } } mapping profiling_info = ([]); void debug_write_prof( ) { foreach( sort( indices( profiling_info ) ), string p ) profiling_info[p]->dump(); } void add_prof_entry( RequestID id, string k, int hr, int hrv ) {
e8dffa2001-05-14Per Hedbor  string l = id->not_query;
7937df2001-05-16Per Hedbor // if( has_prefix( k, "find_internal" ) ) l = dirname(l); if( has_prefix( l, query_internal_location() ) ) l = dirname( l ); // enough, really. if( !profiling_info[l] ) profiling_info[l] = ProfInfo(l);
e8dffa2001-05-14Per Hedbor  profiling_info[l]->add( k, hr, hrv );
4717052001-05-07Per Hedbor } void avg_prof_enter( string name, string type, RequestID id ) { if( !id->misc->prof_stack ) id->misc->prof_stack = ProfStack(); id->misc->prof_stack->enter( name+":"+type,id ); } void avg_prof_leave( string name, string type, RequestID id ) { if( !id->misc->prof_stack ) id->misc->prof_stack = ProfStack(); id->misc->prof_stack->leave( name+":"+type,id ); } #endif
b1fca01996-11-12Per Hedbor /* A configuration.. */
9a8a152000-09-25Per Hedbor inherit Configuration; inherit "basic_defvar";
dfe0362000-03-19Martin Nilsson 
fc40392008-08-15Martin Stjernholm protected mapping(RequestID:mapping) current_connections =
b9c3872002-03-27Per Hedbor  set_weak_flag( ([ ]), 1 ); void connection_add( RequestID id, mapping data ) //! Add a connection. The data mapping can contain things such as //! currently sent bytes. //! //! See protocols/http.pike and slowpipe.pike for more information. //! //! You are not in any way forced to use this method from your //! protocol module. The information is only used for debug purposes //! in the configuration interface. //! //! You have to keep a reference to the mapping on your own, none is //! kept by the configuration object. { current_connections[id] = data; } mapping connection_drop( RequestID id ) //! Remove a connection from the list of currently active connections. //! Returns the mapping previously added with connection_add, if any. {
2d35f72007-02-01Arjan van Staalduijnen  return m_delete( current_connections, id );
b9c3872002-03-27Per Hedbor } mapping(RequestID:mapping) connection_get( ) //! Return all currently active connections. { return current_connections; }
3557f52001-06-30Martin Stjernholm // It's nice to have the name when the rest of __INIT executes. string name = roxen->bootstrap_info->get();
26088b2012-02-22Henrik Grubbström (Grubba) //! The hierarchal cache used for the HTTP protocol cache.
d093992000-09-25Per Hedbor class DataCache {
fc40392008-08-15Martin Stjernholm  protected typedef array(string|mapping(string:mixed))|string| function(string, RequestID:string|int) EntryType;
932f132006-12-05Henrik Grubbström (Grubba)  mapping(string:EntryType) cache = ([]);
d093992000-09-25Per Hedbor  int current_size; int max_size; int max_file_size; int hits, misses; void flush() {
c3c8c02005-11-24Henrik Grubbström (Grubba) #ifndef RAM_CACHE_NO_RELOAD_FLUSH
d093992000-09-25Per Hedbor  current_size = 0; cache = ([]);
c3c8c02005-11-24Henrik Grubbström (Grubba) #endif
d093992000-09-25Per Hedbor  }
c3a6b92009-02-24Martin Stjernholm  // Heuristic to calculate the entry size. Besides the data itself, // we add the size of the key. Even though it's a shared string we
fdeb862012-10-01Marcus Wellhardh  // can pretty much assume it has no other permanent refs. 1024 is a
c3a6b92009-02-24Martin Stjernholm  // constant penalty that accounts for the keypair in the mapping and
fdeb862012-10-01Marcus Wellhardh  // that leaf entries are stored in arrays and the metadata mapping. #define CALC_ENTRY_SIZE(key, data) (sizeof (data) + sizeof (key) + 1024) #define CALC_VARY_CB_SIZE(key) (sizeof (key) + 128)
c3a6b92009-02-24Martin Stjernholm 
932f132006-12-05Henrik Grubbström (Grubba)  // Expire a single entry.
fc40392008-08-15Martin Stjernholm  protected void really_low_expire_entry(string key)
932f132006-12-05Henrik Grubbström (Grubba)  { EntryType e = m_delete(cache, key); if (arrayp(e)) {
c3a6b92009-02-24Martin Stjernholm  current_size -= CALC_ENTRY_SIZE (key, e[0]);
a5c23c2007-09-10Henrik Grubbström (Grubba)  if (e[1]->co_handle) { remove_call_out(e[1]->co_handle); }
b9635f2011-01-21Martin Jonsson  if (CacheKey cachekey = e[1]->key) { destruct (cachekey); }
fdeb862012-10-01Marcus Wellhardh  } else if (!zero_type(e)) { current_size -= CALC_VARY_CB_SIZE(key);
932f132006-12-05Henrik Grubbström (Grubba)  } } // NOTE: Avoid using this function if possible! O(n)
fc40392008-08-15Martin Stjernholm  protected int low_expire_entry(string key_prefix)
d093992000-09-25Per Hedbor  {
7646672006-11-14Henrik Grubbström (Grubba)  if (!key_prefix) return 0;
4bd5602006-11-14Henrik Grubbström (Grubba)  if (arrayp(cache[key_prefix])) { // Leaf node. No need to loop.
932f132006-12-05Henrik Grubbström (Grubba)  really_low_expire_entry(key_prefix);
4bd5602006-11-14Henrik Grubbström (Grubba)  return 1; } // Inner node. Find all its children.
7646672006-11-14Henrik Grubbström (Grubba)  int res = 0;
932f132006-12-05Henrik Grubbström (Grubba)  foreach(indices(cache); int ind; string key) {
98cb8c2006-10-13Martin Stjernholm  if (!key) continue; if (has_prefix(key, key_prefix)) {
932f132006-12-05Henrik Grubbström (Grubba)  really_low_expire_entry(key);
7646672006-11-14Henrik Grubbström (Grubba)  res++;
1455922005-12-05Henrik Grubbström (Grubba)  }
98cb8c2006-10-13Martin Stjernholm  }
7646672006-11-14Henrik Grubbström (Grubba)  return res;
1455922005-12-05Henrik Grubbström (Grubba)  } void expire_entry(string key_prefix, RequestID|void id) { if (!id) {
932f132006-12-05Henrik Grubbström (Grubba)  low_expire_entry(key_prefix);
d093992000-09-25Per Hedbor  return; }
1455922005-12-05Henrik Grubbström (Grubba)  string url = key_prefix;
98cb8c2006-10-13Martin Stjernholm  sscanf(url, "%[^\0]", url);
1455922005-12-05Henrik Grubbström (Grubba)  while(1) {
932f132006-12-05Henrik Grubbström (Grubba)  EntryType val;
1455922005-12-05Henrik Grubbström (Grubba)  if (arrayp(val = cache[key_prefix])) {
c3a6b92009-02-24Martin Stjernholm  current_size -= CALC_ENTRY_SIZE (key_prefix, val[0]);
1455922005-12-05Henrik Grubbström (Grubba)  m_delete(cache, key_prefix); return;
fdeb862012-10-01Marcus Wellhardh  } else if (!zero_type(val)) { current_size -= CALC_VARY_CB_SIZE(key_prefix);
1455922005-12-05Henrik Grubbström (Grubba)  } if (!val) { return; }
88249d2006-06-30Arjan van Staalduijnen  string|array(string) key_frag;
1455922005-12-05Henrik Grubbström (Grubba)  if (stringp(val)) {
d7dbb22006-06-21Arjan van Staalduijnen  key_frag = id->request_headers[val];
1455922005-12-05Henrik Grubbström (Grubba)  } else { key_frag = val(url, id); }
98cb8c2006-10-13Martin Stjernholm  if (key_frag) // Avoid spoofing if key_frag happens to contain "\0\0". key_frag = replace (key_frag, "\0", "\0\1"); else key_frag = ""; key_prefix += "\0\0" + key_frag;
1455922005-12-05Henrik Grubbström (Grubba)  }
d093992000-09-25Per Hedbor  }
932f132006-12-05Henrik Grubbström (Grubba)  //! Clear ~1/10th of the cache.
fdeb862012-10-01Marcus Wellhardh  void clear_some_cache()
d093992000-09-25Per Hedbor  {
c3a6b92009-02-24Martin Stjernholm  // FIXME: Use an iterator to avoid indices() here.
1455922005-12-05Henrik Grubbström (Grubba)  array(string) q = indices(cache); if(!sizeof(q))
d093992000-09-25Per Hedbor  {
1455922005-12-05Henrik Grubbström (Grubba)  current_size=0; return;
d093992000-09-25Per Hedbor  }
932f132006-12-05Henrik Grubbström (Grubba)  // The following code should be ~O(n * log(n)). sort(q);
fdeb862012-10-01Marcus Wellhardh  for(int i = 0; i < sizeof(q)/10 + 1; i++) {
932f132006-12-05Henrik Grubbström (Grubba)  int r = random(sizeof(q)); string key_prefix = q[r = random(sizeof(q))];
78fe042006-12-05Henrik Grubbström (Grubba)  if (!key_prefix) continue;
932f132006-12-05Henrik Grubbström (Grubba)  for(;r < sizeof(q); r++,i++) {
78fe042006-12-05Henrik Grubbström (Grubba)  if (!q[r]) continue;
932f132006-12-05Henrik Grubbström (Grubba)  if (!has_prefix(q[r], key_prefix)) break; really_low_expire_entry(q[r]); q[r] = 0; }
4bd5602006-11-14Henrik Grubbström (Grubba)  }
d093992000-09-25Per Hedbor  }
1455922005-12-05Henrik Grubbström (Grubba)  void set(string url, string data, mapping meta, int expire, RequestID id)
d093992000-09-25Per Hedbor  {
c3a6b92009-02-24Martin Stjernholm  int entry_size = CALC_ENTRY_SIZE (url, data); if( entry_size > max_file_size ) {
de26832008-03-17Henrik Grubbström (Grubba)  // NOTE: There's a possibility of a stale entry remaining in the // cache until it expires, rather than being replaced here.
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Result of size %d is too large " "to store in the protocol cache (limit %d)",
c3a6b92009-02-24Martin Stjernholm  entry_size, max_file_size);
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE (""); return; }
7723f02005-11-28Henrik Grubbström (Grubba) 
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Storing result of size %d in the protocol cache " "using key %O (expire in %ds)",
c3a6b92009-02-24Martin Stjernholm  entry_size, url, expire);
1455922005-12-05Henrik Grubbström (Grubba)  string key = url; foreach(id->misc->vary_cb_order || ({}),
d64d902007-01-03Henrik Grubbström (Grubba)  string|function(string, RequestID: string|int) vary_cb) {
1455922005-12-05Henrik Grubbström (Grubba)  array(string|mapping(string:mixed))|string|
d64d902007-01-03Henrik Grubbström (Grubba)  function(string, RequestID:string|int) old = cache[key];
1455922005-12-05Henrik Grubbström (Grubba)  if (old && (old != vary_cb)) {
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Registering vary cb %O - conflicts with " "existing entry %s, old entry expired", vary_cb, (arrayp (old) ? "of size " + sizeof (old[0]) : sprintf ("%O", old)));
932f132006-12-05Henrik Grubbström (Grubba)  low_expire_entry(key);
fdeb862012-10-01Marcus Wellhardh  old = UNDEFINED; // Ensure that current size is updated below.
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("");
1455922005-12-05Henrik Grubbström (Grubba)  } cache[key] = vary_cb;
fdeb862012-10-01Marcus Wellhardh  if(!old) { current_size += CALC_VARY_CB_SIZE(key); }
1455922005-12-05Henrik Grubbström (Grubba) 
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Registering vary cb %O", vary_cb);
d64d902007-01-03Henrik Grubbström (Grubba)  string key_frag;
1455922005-12-05Henrik Grubbström (Grubba)  if (stringp(vary_cb)) {
d64d902007-01-03Henrik Grubbström (Grubba)  string|array(string) header = id->request_headers[vary_cb]; if (arrayp(header)) key_frag = header * ","; else key_frag = header;
1455922005-12-05Henrik Grubbström (Grubba)  } else {
d64d902007-01-03Henrik Grubbström (Grubba)  int|string frag = vary_cb(url, id); if (intp(frag) && frag) { key_frag = frag->digits(256); } else { key_frag = frag; }
1455922005-12-05Henrik Grubbström (Grubba)  }
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("Vary cb resolved to key fragment %O", key_frag || "");
98cb8c2006-10-13Martin Stjernholm  if (key_frag) // Avoid spoofing if key_frag happens to contain "\0\0". key_frag = replace (key_frag, "\0", "\0\1"); else key_frag = ""; key += "\0\0" + key_frag;
fdeb862012-10-01Marcus Wellhardh  entry_size += 2 + sizeof(key_frag);
1455922005-12-05Henrik Grubbström (Grubba)  } array(string|mapping(string:mixed))|string| function(string, RequestID:string) old = cache[key]; if (old) {
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("Entry conflicts with existing entry %s, " "old entry expired", (arrayp (old) ? "of size " + sizeof (old[0]) : sprintf ("%O", old)));
932f132006-12-05Henrik Grubbström (Grubba)  low_expire_entry(key);
7723f02005-11-28Henrik Grubbström (Grubba)  }
4337712008-01-09Martin Stjernholm  else SIMPLE_TRACE_LEAVE ("");
1455922005-12-05Henrik Grubbström (Grubba) 
c3a6b92009-02-24Martin Stjernholm  current_size += entry_size;
1455922005-12-05Henrik Grubbström (Grubba)  cache[key] = ({ data, meta }); // Only the actual cache entry is expired.
98cb8c2006-10-13Martin Stjernholm  // FIXME: This could lead to lots and lots of call outs.. :P
a5c23c2007-09-10Henrik Grubbström (Grubba)  meta->co_handle = call_out(really_low_expire_entry, expire, key);
d093992000-09-25Per Hedbor  int n; while( (current_size > max_size) && (n++<10)) clear_some_cache(); }
1455922005-12-05Henrik Grubbström (Grubba)  array(string|mapping(string:mixed)) get(string url, RequestID id)
d093992000-09-25Per Hedbor  {
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Looking up entry for %O in the protocol cache", url);
1455922005-12-05Henrik Grubbström (Grubba)  array(string|mapping(string:mixed))|string|
d64d902007-01-03Henrik Grubbström (Grubba)  function(string, RequestID:string|int) res;
1455922005-12-05Henrik Grubbström (Grubba)  string key = url; while(1) {
4acd992006-04-20Henrik Grubbström (Grubba)  id->misc->protcache_cost++;
1455922005-12-05Henrik Grubbström (Grubba)  if (arrayp(res = cache[key])) { hits++;
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("Found entry of size %d", sizeof (res[0]));
1455922005-12-05Henrik Grubbström (Grubba)  return [array(string|mapping(string:mixed))]res; } if (!res) { misses++;
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("Found no entry");
1455922005-12-05Henrik Grubbström (Grubba)  return UNDEFINED; }
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_ENTER (this, "Found vary cb %O", res);
d64d902007-01-03Henrik Grubbström (Grubba)  string key_frag;
1455922005-12-05Henrik Grubbström (Grubba)  if (stringp(res)) {
d64d902007-01-03Henrik Grubbström (Grubba)  string|array(string) header = id->request_headers[res]; if (arrayp(header)) key_frag = header * ","; else key_frag = header;
1455922005-12-05Henrik Grubbström (Grubba)  } else {
d64d902007-01-03Henrik Grubbström (Grubba)  int|string frag = res(url, id); if (intp(frag) && frag) { key_frag = frag->digits(256); } else { key_frag = frag; }
1455922005-12-05Henrik Grubbström (Grubba)  }
4337712008-01-09Martin Stjernholm  SIMPLE_TRACE_LEAVE ("Vary cb resolved to key fragment %O", key_frag || "");
98cb8c2006-10-13Martin Stjernholm  if (key_frag) // Avoid spoofing if key_frag happens to contain "\0\0". key_frag = replace (key_frag, "\0", "\0\1"); else key_frag = ""; key += "\0\0" + key_frag;
1455922005-12-05Henrik Grubbström (Grubba)  };
d093992000-09-25Per Hedbor  } void init_from_variables( ) { max_size = query( "data_cache_size" ) * 1024; max_file_size = query( "data_cache_file_max_size" ) * 1024; if( max_size < max_file_size ) max_size += max_file_size; int n; while( (current_size > max_size) && (n++<10)) clear_some_cache(); }
fc40392008-08-15Martin Stjernholm  protected void create()
d093992000-09-25Per Hedbor  { init_from_variables(); } }
3b17831998-11-22Per Hedbor #include "rxml.pike";
9a8a152000-09-25Per Hedbor constant store = roxen.store; constant retrieve = roxen.retrieve; constant remove = roxen.remove;
14179b1997-01-29Per Hedbor 
65c6d22000-03-10Martin Nilsson int config_id;
9a8a152000-09-25Per Hedbor int get_config_id() {
65c6d22000-03-10Martin Nilsson  if(config_id) return config_id; for(int i=sizeof(roxen->configurations); i;) if(roxen->configurations[--i]->name==name) return config_id=i; }
c7a5f01999-02-16Per Hedbor string get_doc_for( string region, string variable ) {
e351dd1999-11-29Per Hedbor  RoxenModule module;
c7a5f01999-02-16Per Hedbor  if(variable[0] == '_') return 0; if((int)reverse(region)) return 0; if(module = find_module( region )) { if(module->variables[variable])
b2c49b2000-07-09Per Hedbor  return module->variables[variable]->name()+ "\n"+module->variables[ variable ]->doc();
c7a5f01999-02-16Per Hedbor  } if(variables[ variable ])
b2c49b2000-07-09Per Hedbor  return variables[variable]->name()+ "\n"+variables[ variable ]->doc();
c7a5f01999-02-16Per Hedbor }
14179b1997-01-29Per Hedbor 
e351dd1999-11-29Per Hedbor string query_internal_location(RoxenModule|void mod)
5839c31999-01-22Marcus Comstedt {
9ac0332002-11-05Anders Johansson  return internal_location+(mod?replace(otomod[mod]||"", "#", "!")+"/":"");
5839c31999-01-22Marcus Comstedt }
b1fca01996-11-12Per Hedbor  string query_name() {
8552d92001-01-13Martin Nilsson  if(strlen(query("name"))) return query("name");
b1fca01996-11-12Per Hedbor  return name; } string comment() {
8552d92001-01-13Martin Nilsson  return query("comment");
b1fca01996-11-12Per Hedbor }
b6675b2002-10-28Martin Stjernholm private float cached_compat_level; float compat_level() { if (cached_compat_level == 0.0) cached_compat_level = (float) query ("compat_level"); return cached_compat_level; }
b1fca01996-11-12Per Hedbor /* A 'pri' is one of the ten priority objects. Each one holds a list * of modules for that priority. They are all merged into one list for * performance reasons later on. */
e351dd1999-11-29Per Hedbor array (Priority) allocate_pris()
b1fca01996-11-12Per Hedbor {
c5e0961999-10-04Per Hedbor  return allocate(10, Priority)();
b1fca01996-11-12Per Hedbor }
a5c23c2007-09-10Henrik Grubbström (Grubba) array(int) query_oid() {
7adb102007-11-05Henrik Grubbström (Grubba)  return SNMP.RIS_OID_WEBSERVER + ({ 2 });
a5c23c2007-09-10Henrik Grubbström (Grubba) }
747c742007-09-14Henrik Grubbström (Grubba) //! @returns //! Returns an array with two elements: //! @array
0b1eae2009-04-03Henrik Grubbström (Grubba) //! @elem array(int) oid //! //! @elem array(int) oid_suffix
747c742007-09-14Henrik Grubbström (Grubba) //! @endarray array(int) generate_module_oid_segment(RoxenModule me)
a5c23c2007-09-10Henrik Grubbström (Grubba) { string s = otomod[me]; array(string) a = s/"#";
747c742007-09-14Henrik Grubbström (Grubba)  return ({ sizeof(a[0]), @((array(int))a[0]), ((int)a[1]) + 1 });
a5c23c2007-09-10Henrik Grubbström (Grubba) } ADT.Trie generate_module_mib(array(int) oid,
747c742007-09-14Henrik Grubbström (Grubba)  array(int) oid_suffix,
a5c23c2007-09-10Henrik Grubbström (Grubba)  RoxenModule me, ModuleInfo moduleinfo, ModuleCopies module) {
747c742007-09-14Henrik Grubbström (Grubba)  array(int) segment = generate_module_oid_segment(me);
a5c23c2007-09-10Henrik Grubbström (Grubba)  return SNMP.SimpleMIB(oid,
747c742007-09-14Henrik Grubbström (Grubba)  oid_suffix + segment,
a5c23c2007-09-10Henrik Grubbström (Grubba)  ({ UNDEFINED,
7adb102007-11-05Henrik Grubbström (Grubba)  SNMP.Integer(segment[-1], "moduleCopy"),
a6750f2007-09-12Henrik Grubbström (Grubba)  SNMP.String(otomod[me], "moduleIdentifier"), SNMP.Integer(moduleinfo->type, "moduleType"), SNMP.String(me->cvs_version || "", "moduleVersion"),
a5c23c2007-09-10Henrik Grubbström (Grubba)  })); }
b1fca01996-11-12Per Hedbor 
9ac0332002-11-05Anders Johansson // Cache some configuration variables. private int sub_req_limit = 30; private string internal_location = "/_internal/";
56f6952009-01-28Martin Jonsson #ifdef HTTP_COMPRESSION int(0..1) http_compr_enabled; mapping(string:int) http_compr_main_mimes = ([]); mapping(string:int) http_compr_exact_mimes = ([]); int http_compr_minlen; int http_compr_maxlen; int(0..1) http_compr_dynamic_reqs;
37820a2010-03-26Martin Jonsson Thread.Local gz_file_pool = Thread.Local();
56f6952009-01-28Martin Jonsson #endif
fdcb7a2011-02-09Martin Jonsson int handler_queue_timeout;
2f1e892000-08-15Martin Nilsson // The logging format used. This will probably move to the above
b1fca01996-11-12Per Hedbor // mentioned module in the future.
0ab4942006-10-13Martin Stjernholm private mapping (int|string:string) log_format = ([]);
b1fca01996-11-12Per Hedbor 
c5e0961999-10-04Per Hedbor // A list of priority objects
053a552000-10-04Per Hedbor array (Priority) pri = allocate_pris();
b1fca01996-11-12Per Hedbor 
9c19002001-02-27Per Hedbor mapping modules = ([]);
1dd64a2000-09-19Mattias Wingstedt //! All enabled modules in this site.
e75fd12000-07-26Johan Sundström //! The format is "module":{ "copies":([ num:instance, ... ]) }
b1fca01996-11-12Per Hedbor 
9c19002001-02-27Per Hedbor mapping (RoxenModule:string) otomod = ([]);
e75fd12000-07-26Johan Sundström //! A mapping from the module objects to module names
b1fca01996-11-12Per Hedbor 
ed47402010-06-06Martin Stjernholm int module_set_counter = 1; //! Incremented whenever the set of enabled modules changes, or if a //! module is reloaded.
de26832008-03-17Henrik Grubbström (Grubba) mapping(string:int) counters = ([]);
b1fca01996-11-12Per Hedbor  // Caches to speed up the handling of the module search. // They are all sorted in priority order, and created by the functions // below. private array (function) url_module_cache, last_module_cache; private array (function) logger_module_cache, first_module_cache; private array (function) filter_module_cache; private array (array (string|function)) location_module_cache; private mapping (string:array (function)) file_extension_module_cache=([]);
e351dd1999-11-29Per Hedbor private mapping (string:array (RoxenModule)) provider_module_cache=([]);
3342dd2001-01-19Per Hedbor private array (RoxenModule) auth_module_cache, userdb_module_cache;
b1fca01996-11-12Per Hedbor 
38dca81996-12-10Per Hedbor 
0e15572001-02-23Martin Stjernholm void unregister_urls()
38dca81996-12-10Per Hedbor {
9c3c6c2001-11-09Henrik Grubbström (Grubba)  foreach( registered_urls + failed_urls, string url ) roxen.unregister_url(url, this_object());
dffa222000-12-10Per Hedbor  registered_urls = ({});
38dca81996-12-10Per Hedbor }
0e15572001-02-23Martin Stjernholm private void safe_stop_module (RoxenModule mod, string desc) {
4586f52009-10-31Martin Stjernholm  if (mixed err = catch (mod && mod->stop &&
9ed1442010-05-07Martin Stjernholm  call_module_func_with_cbs (mod, "stop", 0)))
0e15572001-02-23Martin Stjernholm  report_error ("While stopping " + desc + ": " + describe_backtrace (err)); }
d10c0e2010-06-06Martin Stjernholm private Thread.Mutex stop_all_modules_mutex = Thread.Mutex();
79b7c32001-09-13Honza Petrous 
d10c0e2010-06-06Martin Stjernholm private void do_stop_all_modules (Thread.MutexKey stop_lock) { mapping(RoxenModule:string) allmods = otomod + ([]);
0e15572001-02-23Martin Stjernholm  if (types_module) {
d10c0e2010-06-06Martin Stjernholm  safe_stop_module (types_module, "type module"); m_delete (allmods, types_module);
0e15572001-02-23Martin Stjernholm  }
d10c0e2010-06-06Martin Stjernholm 
0e15572001-02-23Martin Stjernholm  if (dir_module) {
d10c0e2010-06-06Martin Stjernholm  safe_stop_module (dir_module, "directory module"); m_delete (allmods, dir_module);
0e15572001-02-23Martin Stjernholm  }
d10c0e2010-06-06Martin Stjernholm 
0e15572001-02-23Martin Stjernholm  for(int i=0; i<10; i++) if (Priority p = pri[i]) { #define STOP_MODULES(MODS, DESC) \ foreach(MODS, RoxenModule m) \ if (allmods[m]) { \
d10c0e2010-06-06Martin Stjernholm  safe_stop_module (m, DESC); \ m_delete (allmods, m); \
0e15572001-02-23Martin Stjernholm  } STOP_MODULES (p->url_modules, "url module"); STOP_MODULES (p->logger_modules, "logging module"); STOP_MODULES (p->filter_modules, "filter module"); STOP_MODULES (p->location_modules, "location module"); STOP_MODULES (p->last_modules, "last module"); STOP_MODULES (p->first_modules, "first module"); STOP_MODULES (indices (p->provider_modules), "provider module"); }
d10c0e2010-06-06Martin Stjernholm 
a3aee42012-06-08Martin Stjernholm  end_logger();
d10c0e2010-06-06Martin Stjernholm 
0e15572001-02-23Martin Stjernholm  STOP_MODULES(indices (allmods), "unclassified module"); #undef STOP_MODULES
d10c0e2010-06-06Martin Stjernholm  destruct (stop_lock); } void stop (void|int asynch) //! Unregisters the urls and calls stop in all modules. Uses a handler //! thread to lessen the impact if a module hangs. Doesn't wait for //! all modules to finish if @[asynch] is nonzero. { if (Thread.MutexKey lock = stop_all_modules_mutex->trylock()) { #ifdef SNMP_AGENT if(query("snmp_process") && objectp(roxen->snmpagent)) { roxen->snmpagent->vs_stop_trap(get_config_id()); roxen->snmpagent->del_virtserv(get_config_id()); }
0e15572001-02-23Martin Stjernholm #endif
d10c0e2010-06-06Martin Stjernholm  unregister_urls();
8b51062012-02-14Martin Stjernholm  if (roxen.handler_threads_on_hold()) // Run do_stop_all_modules synchronously if there are no handler // threads running (typically during the RoxenTest_help self test). do_stop_all_modules (lock); else // Seems meaningless to queue this in a handler thread and then // just wait for it below if asynch isn't set - could just as // well do the work in this thread then. But now isn't a good // moment to mess around with it. /mast roxen.handle (do_stop_all_modules, lock);
0e15572001-02-23Martin Stjernholm  }
82219d2010-06-06Martin Stjernholm  if (!asynch) stop_all_modules_mutex->lock (1);
0e15572001-02-23Martin Stjernholm }
d449d52003-06-02Henrik Grubbström (Grubba) string|array(string) type_from_filename( string file, int|void to, string|void myext )
b1fca01996-11-12Per Hedbor {
f28c112000-03-06Martin Nilsson  array(string)|string tmp;
14179b1997-01-29Per Hedbor  if(!types_fun) return to?({ "application/octet-stream", 0 }):"application/octet-stream";
0ba0382001-08-28Henrik Grubbström (Grubba)  string ext = lower_case(myext || Roxen.extension(file));
41b77c1999-07-15David Hedbor 
14179b1997-01-29Per Hedbor  if(tmp = types_fun(ext)) {
0ba0382001-08-28Henrik Grubbström (Grubba)  // FIXME: Ought to support several levels of "strip". if (tmp[0] == "strip")
14179b1997-01-29Per Hedbor  {
d64d902007-01-03Henrik Grubbström (Grubba)  array(string) tmp2 = file/"."; string nx;
0ba0382001-08-28Henrik Grubbström (Grubba)  if (sizeof(tmp2) > 2)
d64d902007-01-03Henrik Grubbström (Grubba)  nx = lower_case(tmp2[-2]); tmp[0] = (nx && types_fun(nx)) || types_fun("default") || "application/octet-stream";
14179b1997-01-29Per Hedbor  }
0ba0382001-08-28Henrik Grubbström (Grubba)  } else if (!(tmp = types_fun("default"))) {
9be5aa1998-07-03Henrik Grubbström (Grubba)  tmp = ({ "application/octet-stream", 0 });
14179b1997-01-29Per Hedbor  }
9be5aa1998-07-03Henrik Grubbström (Grubba)  return to?tmp:tmp[0];
b1fca01996-11-12Per Hedbor }
e351dd1999-11-29Per Hedbor array (RoxenModule) get_providers(string provides)
2f1e892000-08-15Martin Nilsson //! Returns an array with all provider modules that provides "provides".
ae32d01998-03-23David Hedbor {
2f1e892000-08-15Martin Nilsson  // This cache is cleared in the invalidate_cache() call.
ae32d01998-03-23David Hedbor  if(!provider_module_cache[provides])
10c7e11999-12-28Martin Nilsson  {
ae32d01998-03-23David Hedbor  int i; provider_module_cache[provides] = ({ }); for(i = 9; i >= 0; i--) {
951e6f2007-08-06Fredrik Noring  array(RoxenModule) modules = indices(pri[i]->provider_modules); array(string) module_identifiers = modules->module_identifier(); sort(module_identifiers, modules); foreach(modules, RoxenModule d)
10c7e11999-12-28Martin Nilsson  if(pri[i]->provider_modules[ d ][ provides ])
ae32d01998-03-23David Hedbor  provider_module_cache[provides] += ({ d }); } } return provider_module_cache[provides]; }
14179b1997-01-29Per Hedbor 
e351dd1999-11-29Per Hedbor RoxenModule get_provider(string provides)
2f1e892000-08-15Martin Nilsson //! Returns the first provider module that provides "provides".
ae32d01998-03-23David Hedbor {
e351dd1999-11-29Per Hedbor  array (RoxenModule) prov = get_providers(provides);
ae32d01998-03-23David Hedbor  if(sizeof(prov)) return prov[0]; return 0; }
bdb8da1998-09-02Johan Schön array(mixed) map_providers(string provides, string fun, mixed ... args)
2f1e892000-08-15Martin Nilsson //! Maps the function "fun" over all matching provider modules.
ae32d01998-03-23David Hedbor {
e351dd1999-11-29Per Hedbor  array (RoxenModule) prov = get_providers(provides);
0e1f262002-01-29Martin Stjernholm  mixed error;
bdb8da1998-09-02Johan Schön  array a=({ }); mixed m;
10c7e11999-12-28Martin Nilsson  foreach(prov, RoxenModule mod)
9bb8131998-09-12Per Hedbor  {
ae32d01998-03-23David Hedbor  if(!objectp(mod)) continue;
10c7e11999-12-28Martin Nilsson  if(functionp(mod[fun]))
bdb8da1998-09-02Johan Schön  error = catch(m=mod[fun](@args));
0e1f262002-01-29Martin Stjernholm  if(error) { report_debug("Error in map_providers(): " + describe_backtrace(error));
24fbd41999-04-21David Hedbor  }
bdb8da1998-09-02Johan Schön  else
9bb8131998-09-12Per Hedbor  a += ({ m });
18be211998-05-07Henrik Grubbström (Grubba)  error = 0;
ae32d01998-03-23David Hedbor  }
9bb8131998-09-12Per Hedbor  return a;
ae32d01998-03-23David Hedbor } mixed call_provider(string provides, string fun, mixed ... args)
2f1e892000-08-15Martin Nilsson //! Maps the function "fun" over all matching provider modules and //! returns the first positive response.
ae32d01998-03-23David Hedbor {
10c7e11999-12-28Martin Nilsson  foreach(get_providers(provides), RoxenModule mod)
e351dd1999-11-29Per Hedbor  {
d8b7721998-05-28Henrik Grubbström (Grubba)  function f; if(objectp(mod) && functionp(f = mod[fun])) {
a51a902001-11-12Martin Stjernholm  mixed ret; if (ret = f(@args)) { return ret;
d8b7721998-05-28Henrik Grubbström (Grubba)  } }
ae32d01998-03-23David Hedbor  } }
14179b1997-01-29Per Hedbor 
517c7e2000-09-30Per Hedbor array (function) file_extension_modules(string ext)
b1fca01996-11-12Per Hedbor {
0ba0382001-08-28Henrik Grubbström (Grubba)  if(!file_extension_module_cache[ext = lower_case(ext)])
10c7e11999-12-28Martin Nilsson  {
b1fca01996-11-12Per Hedbor  int i; file_extension_module_cache[ext] = ({ }); for(i=9; i>=0; i--) {
e351dd1999-11-29Per Hedbor  array(RoxenModule) d; RoxenModule p;
b1fca01996-11-12Per Hedbor  if(d = pri[i]->file_extension_modules[ext]) foreach(d, p) file_extension_module_cache[ext] += ({ p->handle_file_extension }); } } return file_extension_module_cache[ext]; }
517c7e2000-09-30Per Hedbor array (function) url_modules()
b1fca01996-11-12Per Hedbor { if(!url_module_cache) { int i; url_module_cache=({ }); for(i=9; i>=0; i--) {
e351dd1999-11-29Per Hedbor  array(RoxenModule) d; RoxenModule p;
b1fca01996-11-12Per Hedbor  if(d=pri[i]->url_modules) foreach(d, p) url_module_cache += ({ p->remap_url }); } } return url_module_cache; }
fc40392008-08-15Martin Stjernholm protected mapping api_module_cache = ([]);
e351dd1999-11-29Per Hedbor mapping api_functions(void|RequestID id)
4f4bc11998-02-04Per Hedbor {
fc9d8e2000-08-28Per Hedbor  return api_module_cache+([]);
4f4bc11998-02-04Per Hedbor }
517c7e2000-09-30Per Hedbor array (function) logger_modules()
b1fca01996-11-12Per Hedbor { if(!logger_module_cache) { int i; logger_module_cache=({ }); for(i=9; i>=0; i--) {
e351dd1999-11-29Per Hedbor  array(RoxenModule) d; RoxenModule p;
b1fca01996-11-12Per Hedbor  if(d=pri[i]->logger_modules) foreach(d, p) if(p->log) logger_module_cache += ({ p->log }); } } return logger_module_cache; }
517c7e2000-09-30Per Hedbor array (function) last_modules()
b1fca01996-11-12Per Hedbor { if(!last_module_cache) { int i; last_module_cache=({ }); for(i=9; i>=0; i--) {
e351dd1999-11-29Per Hedbor  array(RoxenModule) d; RoxenModule p;
b1fca01996-11-12Per Hedbor  if(d=pri[i]->last_modules) foreach(d, p) if(p->last_resort) last_module_cache += ({ p->last_resort }); } } return last_module_cache; }
fc40392008-08-15Martin Stjernholm protected mixed strip_fork_information(RequestID id)
ca44e51998-07-02Henrik Grubbström (Grubba) {
bc37d32005-01-05Jonas Wallden  if (uname()->sysname == "Darwin") { // Look for Mac OS X special filenames that are used access files in // magic ways: // // foo.txt/..namedfork/data (same as foo.txt) // foo.txt/..namedfork/rsrc (resource fork of foo.txt) // foo.txt/rsrc (resource fork of foo.txt) // .DS_Store (Finder info file with catalog data) if (has_value(id->not_query, "..namedfork/") || has_suffix(id->not_query, "/rsrc") || has_value(lower_case(id->not_query), ".ds_store"))
6f9fb02012-02-03Jonas Wallden  // Skip elaborate error page since we get these e.g. for WebDAV // mounts in OS X Finder.
1e54fa2013-12-14Jonas Walldén  return Roxen.http_status(404, "No such file.");
bc37d32005-01-05Jonas Wallden  }
ca44e51998-07-02Henrik Grubbström (Grubba)  array a = id->not_query/"::";
9934d62001-09-05Jonas Wallden  // FIX: Must not subtract ":" chars since it breaks proper URL:s, // e.g. "/internal-roxen-colorbar:x,y,z" and several others. // id->not_query = a[0]-":"; id->not_query = a[0];
ca44e51998-07-02Henrik Grubbström (Grubba)  id->misc->fork_information = a[1..];
c5e0961999-10-04Per Hedbor  return 0;
ca44e51998-07-02Henrik Grubbström (Grubba) }
517c7e2000-09-30Per Hedbor array (function) first_modules()
b1fca01996-11-12Per Hedbor { if(!first_module_cache) { int i;
bc37d32005-01-05Jonas Wallden  first_module_cache = ({ }); // Add special fork handlers on Windows and Mac OS X if (
ca44e51998-07-02Henrik Grubbström (Grubba) #ifdef __NT__
bc37d32005-01-05Jonas Wallden  1 || #endif uname()->sysname == "Darwin") { first_module_cache= ({ strip_fork_information, // Always first! }); }
b1fca01996-11-12Per Hedbor  for(i=9; i>=0; i--) {
e351dd1999-11-29Per Hedbor  array(RoxenModule) d; RoxenModule p;
f128901997-08-15Henrik Grubbström (Grubba)  if(d=pri[i]->first_modules) { foreach(d, p) { if(p->first_try) {
b1fca01996-11-12Per Hedbor  first_module_cache += ({ p->first_try });
f128901997-08-15Henrik Grubbström (Grubba)  } } }
b1fca01996-11-12Per Hedbor  } }
f128901997-08-15Henrik Grubbström (Grubba) 
b1fca01996-11-12Per Hedbor  return first_module_cache; }
4d10232001-06-26Per Hedbor void set_userdb_module_cache( array to ) // Used by the config_filesystem.pike module to enforce the usage of // the config userdb module, for now. { userdb_module_cache = to; }
3342dd2001-01-19Per Hedbor array(UserDB) user_databases() { if( userdb_module_cache ) return userdb_module_cache; array tmp = ({}); foreach( values( modules ), mapping m ) foreach( values(m->copies), RoxenModule mo ) if( mo->module_type & MODULE_USERDB ) tmp += ({ ({ mo->query( "_priority" ), mo }) }); sort( tmp );
96d86e2001-01-29Per Hedbor // tmp += ({ ({ 0, roxen->config_userdb_module }) });
3342dd2001-01-19Per Hedbor  return userdb_module_cache = reverse(column(tmp,1)); } array(AuthModule) auth_modules() { if( auth_module_cache ) return auth_module_cache; array tmp = ({}); foreach( values( modules ), mapping m ) foreach( values(m->copies), RoxenModule mo ) if( mo->module_type & MODULE_AUTH ) tmp += ({ ({ mo->query( "_priority" ), mo }) }); sort( tmp ); return auth_module_cache = reverse(column(tmp,1)); }
b1fca01996-11-12Per Hedbor 
517c7e2000-09-30Per Hedbor array location_modules()
3342dd2001-01-19Per Hedbor //! Return an array of all location modules the request should be
cd92872000-08-15Johan Sundström //! mapped through, by order of priority.
b1fca01996-11-12Per Hedbor { if(!location_module_cache) { int i;
8cc31b1997-10-12Henrik Grubbström (Grubba)  array new_location_module_cache=({ });
b1fca01996-11-12Per Hedbor  for(i=9; i>=0; i--) {
10c7e11999-12-28Martin Nilsson  array(RoxenModule) d;
e351dd1999-11-29Per Hedbor  RoxenModule p;
8cc31b1997-10-12Henrik Grubbström (Grubba)  if(d=pri[i]->location_modules) { array level_find_files = ({}); array level_locations = ({}); foreach(d, p) { string location; // FIXME: Should there be a catch() here? if(p->find_file && (location = p->query_location())) { level_find_files += ({ p->find_file }); level_locations += ({ location }); } }
dffddc2000-10-10Johan Sundström  sort(map(level_locations, sizeof), level_locations, level_find_files);
8cc31b1997-10-12Henrik Grubbström (Grubba)  int j; for (j = sizeof(level_locations); j--;) { // Order after longest path first. new_location_module_cache += ({ ({ level_locations[j], level_find_files[j] }) }); } }
b1fca01996-11-12Per Hedbor  }
8cc31b1997-10-12Henrik Grubbström (Grubba)  location_module_cache = new_location_module_cache;
b1fca01996-11-12Per Hedbor  } return location_module_cache; }
517c7e2000-09-30Per Hedbor array(function) filter_modules()
b1fca01996-11-12Per Hedbor { if(!filter_module_cache) { int i; filter_module_cache=({ }); for(i=9; i>=0; i--) {
10c7e11999-12-28Martin Nilsson  array(RoxenModule) d;
e351dd1999-11-29Per Hedbor  RoxenModule p;
b1fca01996-11-12Per Hedbor  if(d=pri[i]->filter_modules) foreach(d, p) if(p->filter) filter_module_cache+=({ p->filter }); } } return filter_module_cache; }
a3aee42012-06-08Martin Stjernholm void end_logger() { if (mixed err = catch { if (roxen.LogFile logger = log_function && function_object (log_function)) { logger->close(); } }) report_error ("While stopping the logger: " + describe_backtrace (err)); log_function = 0; }
b1fca01996-11-12Per Hedbor 
1c78232000-03-13Per Hedbor void init_log_file() {
a3aee42012-06-08Martin Stjernholm  end_logger();
1c78232000-03-13Per Hedbor  // Only try to open the log file if logging is enabled!! if(query("Log")) { string logfile = query("LogFile");
f7d9811997-09-12Per Hedbor  if(strlen(logfile))
8dc3822006-09-08Fredrik Noring  log_function = roxen.LogFile(logfile, query("LogFileCompressor"))->write;
1c78232000-03-13Per Hedbor  }
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor private void parse_log_formats() { array foo=query("LogFormat")/"\n";
0ab4942006-10-13Martin Stjernholm  log_format = ([]); foreach(foo; int i; string b) if(strlen(b) && b[0] != '#') { if (sscanf (b, "%d:%*[\t ]%s", int status, b)) log_format[status] = b; else if (sscanf (b, "*:%*[\t ]%s", b)) log_format[0] = b; else if (sscanf (b, "%[-_.#a-zA-Z0-9*]/%[-_.#a-zA-Z0-9*]:%*[\t ]%s", string facility, string action, b) >= 2) log_format[facility + "/" + action] = b; else // Ought to be an error when the variable is set, but that's // not entirely backward compatible. report_warning ("Unrecognized format on line %d " "in log format setting: %O\n", i + 1, b); }
14179b1997-01-29Per Hedbor }
b1fca01996-11-12Per Hedbor 
9c19002001-02-27Per Hedbor void log(mapping file, RequestID request_id)
14179b1997-01-29Per Hedbor {
6533f22001-08-23Martin Nilsson  // Call all logging functions
00d8f72006-05-22Henrik Grubbström (Grubba)  array(function) log_funs = logger_module_cache||logger_modules(); if (sizeof(log_funs)) {
72ba2c2006-09-15Marcus Wellhardh  request_id->init_cookies(1);
00d8f72006-05-22Henrik Grubbström (Grubba)  foreach(log_funs, function f) if( f( request_id, file ) ) return; }
14179b1997-01-29Per Hedbor 
7a243b2000-08-19Per Hedbor  if( !log_function )
6533f22001-08-23Martin Nilsson  return; // No file is open for logging.
14179b1997-01-29Per Hedbor 
7a243b2000-08-19Per Hedbor  if(do_not_log_patterns && Roxen._match(request_id->remoteaddr, do_not_log_patterns))
14179b1997-01-29Per Hedbor  return;
10c7e11999-12-28Martin Nilsson 
7a243b2000-08-19Per Hedbor  string form;
0ab4942006-10-13Martin Stjernholm  if(!(form=log_format[(int) file->error]))
7a243b2000-08-19Per Hedbor  form = log_format[0];
14179b1997-01-29Per Hedbor  if(!form) return;
7a243b2000-08-19Per Hedbor  roxen.run_log_format( form, log_function, request_id, file );
14179b1997-01-29Per Hedbor }
0ab4942006-10-13Martin Stjernholm void log_event (string facility, string action, string resource, void|mapping(string:mixed) info) //! Log an event. //! //! This function is primarily intended for logging arbitrary internal
86e6072009-01-08Martin Stjernholm //! events for performance monitoring purposes. The events are sent to //! the access log, where they typically are formatted in a CommonLog //! lookalike format.
0ab4942006-10-13Martin Stjernholm //! //! The intention is to extend this function to be able to collect //! statistics of these events for polling by e.g. SNMP. //! //! @param facility //! An identifier for the module or subsystem that the event comes //! from. This defaults to the module identifier returned by //! @[RoxenModule.module_local_id] when the @[RoxenModule.log_event] //! wrapper is used. It should be unique within the configuration. //! Valid characters are @expr{[-_.#a-zA-Z0-9]@} but the first //! character has to be alphanumeric. //! //! @param action //! An identifier for the specific event within the facility. Should //! be enumerable. Valid characters are @expr{[-_.#a-zA-Z0-9]@}. //! //! @param resource
33c5b42009-11-10Martin Stjernholm //! Identifies the resource that the event acts on. Pass zero if a //! resource isn't applicable.
0ab4942006-10-13Martin Stjernholm //! //! If applicable, this is the path within the virtual file system //! of the module, beginning with a "@expr{/@}". //! //! Otherwise it is some other string, not beginning with //! "@expr{/@}", that has a format suitable for describing the //! resource handled by the facility, e.g. "@expr{pclass:17@}". //! //! This string should preferably contain URI valid chars only, but //! other chars are allowed and will be encoded if necessary. //! //! @param info //! An optional mapping containing arbitrary info about the event. //! The entries here can be accessed as @expr{$@} format specifiers //! in the @expr{LogFormat@} configuration variable. //! //! The values must be castable to strings. The strings should //! preferably contain URI valid chars only, but other chars are //! allowed and will be encoded if necessary. //! //! The strings should preferably never be empty. If a string might //! be, it should be documented in the doc blurb for the //! @expr{LogFormat@} configuration variable. //! //! Most but not all of the predefined format specifiers can be //! overridden this way, but if any is overridden it should map very //! closely to the syntax and semantics of the original. //! //! Note that "@expr{_@}" cannot be used in names in the indices //! here since the log formatter code replaces "@expr{_@}" with //! "@expr{-@}" before doing lookups. //! //! @note //! Events should be documented in the doc blurb for the //! @expr{LogFormat@} configuration variable. { // Currently this bypasses logger modules. Might change in the future. if( !log_function ) return; // No file is open for logging. if(do_not_log_patterns && Roxen._match("0.0.0.0", do_not_log_patterns)) return; sscanf (facility, "%[^#]", string modname); if (string format = log_format[facility + "/" + action] || log_format[facility + "/*"] || // Also try without the module copy number if the facility // appears to be a module identifier. modname != "" && (log_format[modname + "/" + action] || log_format[modname + "/*"]) || log_format["*/*"]) roxen.run_log_event_format (format, log_function,
33c5b42009-11-10Martin Stjernholm  facility, action, resource || "-", info);
0ab4942006-10-13Martin Stjernholm }
9c19002001-02-27Per Hedbor array(string) userinfo(string u, RequestID|void id)
6533f22001-08-23Martin Nilsson //! @note //! DEPRECATED COMPATIBILITY FUNCTION
3342dd2001-01-19Per Hedbor //!
cd81922000-09-19Johan Sundström //! Fetches user information from the authentication module by calling //! its userinfo() method. Returns zero if no auth module was present.
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
14179b1997-01-29Per Hedbor {
3342dd2001-01-19Per Hedbor  User uid; foreach( user_databases(), UserDB m ) if( uid = m->find_user( u ) )
fc40392008-08-15Martin Stjernholm  return uid->compat_userinfo();
14179b1997-01-29Per Hedbor }
9c19002001-02-27Per Hedbor array(string) userlist(RequestID|void id)
6533f22001-08-23Martin Nilsson //! @note //! DEPRECATED COMPATIBILITY FUNCTION
3342dd2001-01-19Per Hedbor //!
cd81922000-09-19Johan Sundström //! Fetches the full list of valid usernames from the authentication //! module by calling its userlist() method. Returns zero if no auth //! module was present.
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
14179b1997-01-29Per Hedbor {
3342dd2001-01-19Per Hedbor  array(string) list = ({}); foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  list |= m->list_users(id);
3342dd2001-01-19Per Hedbor  return list;
14179b1997-01-29Per Hedbor }
9c19002001-02-27Per Hedbor array(string) user_from_uid(int u, RequestID|void id)
6533f22001-08-23Martin Nilsson //! @note //! DEPRECATED COMPATIBILITY FUNCTION
3342dd2001-01-19Per Hedbor //!
cd81922000-09-19Johan Sundström //! Return the user data for id u from the authentication module. The //! id parameter might be left out if FTP. Returns zero if no auth //! module was present.
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
14179b1997-01-29Per Hedbor {
3342dd2001-01-19Per Hedbor  User uid; foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  if( uid = m->find_user_from_uid( u,id ) )
3342dd2001-01-19Per Hedbor  return uid->compat_userinfo();
14179b1997-01-29Per Hedbor }
e3f4662001-01-19Per Hedbor UserDB find_user_database( string name )
96d86e2001-01-29Per Hedbor //! Given a user database name, returns it if it exists in this //! configuration, otherwise returns 0.
e3f4662001-01-19Per Hedbor { foreach( user_databases(), UserDB m ) if( m->name == name ) return m; } AuthModule find_auth_module( string name )
96d86e2001-01-29Per Hedbor //! Given a authentication method name, returns it if it exists in //! this configuration, otherwise returns 0.
e3f4662001-01-19Per Hedbor { foreach( auth_modules(), AuthModule m ) if( m->name == name ) return m; }
3342dd2001-01-19Per Hedbor 
96d86e2001-01-29Per Hedbor User authenticate( RequestID id, UserDB|void database)
3342dd2001-01-19Per Hedbor //! Try to authenticate the request with users from the specified user //! database. If no @[database] is specified, all datbases in the
96d86e2001-01-29Per Hedbor //! current configuration are searched in priority order.
3342dd2001-01-19Per Hedbor //! //! The return value is the autenticated user.
0cde962001-01-21Per Hedbor //! id->misc->authenticated_user is always set to the return value.
3342dd2001-01-19Per Hedbor { User u;
61636c2004-04-29Martin Stjernholm  if (!zero_type (u = id->misc->authenticated_user)) return u;
9a4c052001-01-28Per Hedbor  foreach( auth_modules(), AuthModule method ) if( u = method->authenticate( id, database ) ) return id->misc->authenticated_user = u;
3342dd2001-01-19Per Hedbor }
96d86e2001-01-29Per Hedbor mapping authenticate_throw( RequestID id, string realm, UserDB|void database)
3342dd2001-01-19Per Hedbor //! Returns a reply mapping, similar to @[Roxen.http_rxml_reply] with
0b1eae2009-04-03Henrik Grubbström (Grubba) //! friends. If no @[database] is specified, all databases in the
96d86e2001-01-29Per Hedbor //! current configuration are searched in priority order.
3342dd2001-01-19Per Hedbor { mapping m;
9a4c052001-01-28Per Hedbor  foreach( auth_modules(), AuthModule method ) if( m = method->authenticate_throw( id, realm, database ) ) return m;
96d86e2001-01-29Per Hedbor } User find_user( string user, RequestID|void id ) //! Tries to find the specified user in the currently available user //! databases. If id is specified, this function defaults to the //! database that the currently authenticated user came from, if any. //! //! The other user databases are processed in priority order
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
96d86e2001-01-29Per Hedbor { User uid;
d2fcaf2001-01-30Per Hedbor  if( id && id->misc->authenticated_user
ac77452001-06-13Per Hedbor  && ( uid = id->misc->authenticated_user->database->find_user(user,id)))
96d86e2001-01-29Per Hedbor  return uid; foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  if( uid = m->find_user( user,id ) )
96d86e2001-01-29Per Hedbor  return uid; }
ac77452001-06-13Per Hedbor array(string) list_users(RequestID|void id)
d2fcaf2001-01-30Per Hedbor //! Fetches the full list of valid usernames from the authentication //! modules by calling the list-users() methods.
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
d2fcaf2001-01-30Per Hedbor { array(string) list = ({}); foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  list |= m->list_users(id);
d2fcaf2001-01-30Per Hedbor  return list; }
ac77452001-06-13Per Hedbor array(string) list_groups(RequestID|void id)
d2fcaf2001-01-30Per Hedbor //! Fetches the full list of valid groupnames from the authentication //! modules by calling the list-users() methods.
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
d2fcaf2001-01-30Per Hedbor { array(string) list = ({}); foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  list |= m->list_groups(id);
d2fcaf2001-01-30Per Hedbor  return list; }
96d86e2001-01-29Per Hedbor Group find_group( string group, RequestID|void id ) //! Tries to find the specified group in the currently available user //! databases. If id is specified, this function defaults to the //! database that the currently authenticated user came from, if any. //! //! The other user databases are processed in priority order
ac77452001-06-13Per Hedbor //! //! Note that you should always supply id if it's possible, some user //! databases require it (such as the htaccess database)
96d86e2001-01-29Per Hedbor { Group uid;
d2fcaf2001-01-30Per Hedbor  if( id && id->misc->authenticated_user
96d86e2001-01-29Per Hedbor  && ( uid = id->misc->authenticated_user->database->find_group( group ) )) return uid; foreach( user_databases(), UserDB m )
ac77452001-06-13Per Hedbor  if( uid = m->find_group( group,id ) )
96d86e2001-01-29Per Hedbor  return uid;
3342dd2001-01-19Per Hedbor }
9c19002001-02-27Per Hedbor string last_modified_by(Stdio.File file, RequestID id)
6103bc1999-10-09Henrik Grubbström (Grubba) {
1f4a6c2000-08-28Per Hedbor  Stat s;
6103bc1999-10-09Henrik Grubbström (Grubba)  int uid;
9f9fc32000-02-17Per Hedbor  array u;
10c7e11999-12-28Martin Nilsson 
6103bc1999-10-09Henrik Grubbström (Grubba)  if(objectp(file)) s=file->stat(); if(!s || sizeof(s)<5) return "A. Nonymous"; uid=s[5]; u=user_from_uid(uid, id); if(u) return u[0]; return "A. Nonymous"; }
14179b1997-01-29Per Hedbor  // Some clients does _not_ handle the magic 'internal-gopher-...'. // So, lets do it here instead. private mapping internal_gopher_image(string from) { sscanf(from, "%s.gif", from); sscanf(from, "%s.jpg", from); from -= "."; // Disallow "internal-gopher-..", it won't really do much harm, but a list of // all files in '..' might be retrieved (that is, the actual directory // file was sent to the browser)
1c78232000-03-13Per Hedbor  Stdio.File f = lopen("roxen-images/dir/"+from+".gif","r"); if (f)
5848842000-08-28Per Hedbor  return (["file":f, "type":"image/gif", "stat":f->stat(),]); else
06026c1999-10-19Henrik Grubbström (Grubba)  return 0;
5848842000-08-28Per Hedbor  // File not found.
14179b1997-01-29Per Hedbor } #ifdef MODULE_LEVEL_SECURITY
5910aa2004-02-17Martin Stjernholm private mapping(RoxenModule:array) security_level_cache = set_weak_flag (([]), 1);
14179b1997-01-29Per Hedbor 
3e3bab2001-01-19Per Hedbor int|mapping check_security(function|RoxenModule a, RequestID id, void|int slevel)
14179b1997-01-29Per Hedbor { array seclevels;
12a9c51997-08-11Henrik Grubbström (Grubba)  // NOTE: // ip_ok and auth_ok are three-state variables. // Valid contents for them are: // 0 Unknown state -- No such restriction encountered yet. // 1 May be bad -- Restriction encountered, and test failed. // ~0 OK -- Test passed.
1935351997-04-28Henrik Grubbström (Grubba) 
5910aa2004-02-17Martin Stjernholm  if (RoxenModule mod = Roxen.get_owning_module (a)) { // Only store the module objects in the cache and not `a' directly // since it can be (in) an object that is very short lived. if (!(seclevels = security_level_cache[mod])) { if(mod->query_seclevels) seclevels = ({ mod->query_seclevels(), mod->query("_seclvl"), }); else seclevels = ({0,0}); security_level_cache[mod] = seclevels; }
2b86422000-06-29Martin Stjernholm  }
5910aa2004-02-17Martin Stjernholm  else seclevels = ({0,0});
14179b1997-01-29Per Hedbor 
bc0fa02001-03-08Per Hedbor  if(slevel && (seclevels[1] > slevel)) // "Trustlevel" to low.
5a42932001-08-30Per Hedbor  // Regarding memory cache: This won't have any impact, since it's // always the same, regardless of the client requesting the file.
14179b1997-01-29Per Hedbor  return 1;
10c7e11999-12-28Martin Nilsson 
18be211998-05-07Henrik Grubbström (Grubba)  mixed err;
bc0fa02001-03-08Per Hedbor  if( function(RequestID:int|mapping) f = seclevels[0] )
5a42932001-08-30Per Hedbor  // And here we don't have to take notice of the RAM-cache either, // since the security patterns themselves does that. // // All patterns that varies depending on the client must use // NOCACHE(), to force the request to be uncached. //
bc0fa02001-03-08Per Hedbor  err=catch { return f( id ); }; else return 0; // Ok if there are no patterns.
18be211998-05-07Henrik Grubbström (Grubba) 
bc0fa02001-03-08Per Hedbor  report_error("check_security(): %s:\n%s\n", LOC_M(39, "Error during module security check"), describe_backtrace(err));
18be211998-05-07Henrik Grubbström (Grubba) 
bc0fa02001-03-08Per Hedbor  return 1;
14179b1997-01-29Per Hedbor } #endif // Empty all the caches above.
8cc31b1997-10-12Henrik Grubbström (Grubba) void invalidate_cache()
14179b1997-01-29Per Hedbor { last_module_cache = 0; filter_module_cache = 0;
3342dd2001-01-19Per Hedbor  userdb_module_cache = 0; auth_module_cache = 0;
14179b1997-01-29Per Hedbor  first_module_cache = 0; url_module_cache = 0; location_module_cache = 0; logger_module_cache = 0; file_extension_module_cache = ([]);
ae32d01998-03-23David Hedbor  provider_module_cache = ([]);
14179b1997-01-29Per Hedbor #ifdef MODULE_LEVEL_SECURITY
5910aa2004-02-17Martin Stjernholm  security_level_cache = set_weak_flag (([ ]), 1);
14179b1997-01-29Per Hedbor #endif }
ec058c1998-07-04Henrik Grubbström (Grubba) // Empty all the caches above AND the ones in the loaded modules. void clear_memory_caches() { invalidate_cache();
10c7e11999-12-28Martin Nilsson  foreach(indices(otomod), RoxenModule m) if (m && m->clear_memory_caches) if (mixed err = catch( m->clear_memory_caches() ))
434bac2000-07-14Andreas Lange  report_error("clear_memory_caches() "+
49cd282000-08-11Andreas Lange  LOC_M(40, "failed for module %O:\n%s\n"),
67f60e2000-07-09Martin Nilsson  otomod[m], describe_backtrace(err));
ec058c1998-07-04Henrik Grubbström (Grubba) }
7de44b2000-09-13Jonas Wallden // Returns tuple < image, mime-type >
fc40392008-08-15Martin Stjernholm protected array(string) draw_saturation_bar(int hue,int brightness, int where, int small_version)
fc94331997-10-25Per Hedbor {
1b664a2004-03-08Jonas Wallden  Image.Image bar = small_version ? Image.Image(16, 128) : Image.Image(30, 256);
fc94331997-10-25Per Hedbor  for(int i=0;i<128;i++) {
4205aa2004-03-06Jonas Wallden  int j = i * 2; array color = hsv_to_rgb(hue, 255 - j, brightness);
1b664a2004-03-08Jonas Wallden  if (small_version) { bar->line(0, i, 15, i, @color); } else { bar->line(0, j, 29, j, @color); bar->line(0, j + 1,29, j + 1, @color); }
fc94331997-10-25Per Hedbor  }
e2d5772004-03-13Jonas Wallden  if (where >= 0 && where <= 255) { where = 255 - where; int hilite = (brightness > 128) ? 0 : 255; if (small_version) bar->line(0, where / 2, 15, where / 2, hilite, hilite, hilite); else bar->line(0, where, 29, where, hilite, hilite, hilite); }
1b664a2004-03-08Jonas Wallden 
2a346d2000-09-19Per Hedbor #if constant(Image.JPEG) && constant(Image.JPEG.encode)
7de44b2000-09-13Jonas Wallden  return ({ Image.JPEG.encode(bar), "image/jpeg" });
2a346d2000-09-19Per Hedbor #else return ({ Image.PNG.encode(bar), "image/png" });
7de44b2000-09-13Jonas Wallden #endif
fc94331997-10-25Per Hedbor }
3072012005-10-23Jonas Wallden #if constant(Image.GIF) && constant(Image.PNG) array(mapping) spinner_data = 0; // Returns tuple < image, mime type >
fc40392008-08-15Martin Stjernholm protected array(string) draw_spinner(string bgcolor)
3072012005-10-23Jonas Wallden { // Parse color array color = parse_color(bgcolor); // Load all spinner PNGs if (!spinner_data) {
fdac492006-02-23Jonas Wallden  array(mapping) temp_spinner_data = ({ });
3072012005-10-23Jonas Wallden  for (int i = 0; i < 12; i++) { string src = lopen("roxen-images/spinner" + i + ".png", "r")->read();
fdac492006-02-23Jonas Wallden  temp_spinner_data += ({ Image.PNG._decode(src) });
3072012005-10-23Jonas Wallden  }
fdac492006-02-23Jonas Wallden  spinner_data = temp_spinner_data;
3072012005-10-23Jonas Wallden  } // Create non-transparent Image object for each frame array(Image.Image) frames = ({ }); foreach(spinner_data, mapping data) { Image.Image frame = Image.Image(17, 17, @color); frame->paste_mask(data->image, data->alpha); frames += ({ frame }); } // Create animated GIF using colortable based on first frame (all of // them have the same set of colors) Image.Colortable colors = Image.Colortable(frames[0]); string res = Image.GIF.header_block(17, 17, colors); foreach(frames, Image.Image frame) res += Image.GIF.render_block(frame, colors, 0, 0, 0, 1); res += Image.GIF.netscape_loop_block(0) + Image.GIF.end_block(); return ({ res, "image/gif" }); } #endif
14179b1997-01-29Per Hedbor // Inspired by the internal-gopher-... thingie, this is the images
a6ef1f2000-03-28Johan Sundström // from the administration interface. :-)
2a346d2000-09-19Per Hedbor private mapping internal_roxen_image( string from, RequestID id )
14179b1997-01-29Per Hedbor { sscanf(from, "%s.gif", from); sscanf(from, "%s.jpg", from);
2b658c2000-02-07Per Hedbor  sscanf(from, "%s.xcf", from); sscanf(from, "%s.png", from);
fc94331997-10-25Per Hedbor 
3072012005-10-23Jonas Wallden #if constant(Image.GIF) && constant(Image.PNG) // Animated spinner image if (has_prefix(from, "spinner-")) { array(string) spinner = draw_spinner(from[8..]); return ([ "data" : spinner[0], "type" : spinner[1], "stat" : ({ 0, 0, 0, 900000000, 0, 0, 0 }) ]); } #endif
1c78232000-03-13Per Hedbor  // Automatically generated colorbar. Used by wizard code...
fc94331997-10-25Per Hedbor  int hue,bright,w;
1b664a2004-03-08Jonas Wallden  string colorbar; if(sscanf(from, "%s:%d,%d,%d", colorbar, hue, bright,w)==4) { array bar = draw_saturation_bar(hue, bright, w, colorbar == "colorbar-small");
7de44b2000-09-13Jonas Wallden  return Roxen.http_string_answer(bar[0], bar[1]); }
fc94331997-10-25Per Hedbor 
2b658c2000-02-07Per Hedbor  Stdio.File f;
7310762000-09-19Per Hedbor  if( !id->misc->internal_get ) if(f = lopen("roxen-images/"+from+".gif", "r")) return (["file":f, "type":"image/gif", "stat":f->stat()]);
1c78232000-03-13Per Hedbor  if(f = lopen("roxen-images/"+from+".png", "r"))
5848842000-08-28Per Hedbor  return (["file":f, "type":"image/png", "stat":f->stat()]);
7310762000-09-19Per Hedbor  if(f = lopen("roxen-images/"+from+".jpg", "r")) return (["file":f, "type":"image/jpeg", "stat":f->stat()]);
1c78232000-03-13Per Hedbor  if(f = lopen("roxen-images/"+from+".xcf", "r"))
5848842000-08-28Per Hedbor  return (["file":f, "type":"image/x-gimp-image", "stat":f->stat()]);
7310762000-09-19Per Hedbor  if(f = lopen("roxen-images/"+from+".gif", "r")) return (["file":f, "type":"image/gif", "stat":f->stat()]);
2b658c2000-02-07Per Hedbor  // File not found. return 0;
14179b1997-01-29Per Hedbor } mapping (mixed:function|int) locks = ([]); #ifdef THREADS
e5bad21998-02-10Per Hedbor // import Thread;
e6aff01997-05-25Henrik Grubbström (Grubba) 
55a89e1997-09-14Per Hedbor mapping locked = ([]), thread_safe = ([]);
e351dd1999-11-29Per Hedbor mixed _lock(object|function f)
14179b1997-01-29Per Hedbor {
3e3bab2001-01-19Per Hedbor  Thread.MutexKey key;
1fab6f1997-09-03Henrik Grubbström (Grubba)  function|int l;
9c19002001-02-27Per Hedbor  TIMER_START(module_lock);
1fab6f1997-09-03Henrik Grubbström (Grubba)  if (functionp(f)) { f = function_object(f); }
55a89e1997-09-14Per Hedbor  if (l = locks[f]) { if (l != -1) {
1fab6f1997-09-03Henrik Grubbström (Grubba)  // Allow recursive locks.
beb1f21998-05-23Mattias Wingstedt  catch{
91d3c32001-03-12Martin Nilsson  // report_debug("lock %O\n", f);
55a89e1997-09-14Per Hedbor  locked[f]++;
1fab6f1997-09-03Henrik Grubbström (Grubba)  key = l();
beb1f21998-05-23Mattias Wingstedt  };
55a89e1997-09-14Per Hedbor  } else thread_safe[f]++;
1fab6f1997-09-03Henrik Grubbström (Grubba)  } else if (f->thread_safe) { locks[f]=-1;
55a89e1997-09-14Per Hedbor  thread_safe[f]++;
14179b1997-01-29Per Hedbor  } else {
55a89e1997-09-14Per Hedbor  if (!locks[f]) {
1fab6f1997-09-03Henrik Grubbström (Grubba)  // Needed to avoid race-condition.
e5bad21998-02-10Per Hedbor  l = Thread.Mutex()->lock;
1fab6f1997-09-03Henrik Grubbström (Grubba)  if (!locks[f]) { locks[f]=l; }
14179b1997-01-29Per Hedbor  }
91d3c32001-03-12Martin Nilsson  // report_debug("lock %O\n", f);
55a89e1997-09-14Per Hedbor  locked[f]++;
1fab6f1997-09-03Henrik Grubbström (Grubba)  key = l();
b1fca01996-11-12Per Hedbor  }
9c19002001-02-27Per Hedbor  TIMER_END(module_lock);
14179b1997-01-29Per Hedbor  return key;
b1fca01996-11-12Per Hedbor }
14179b1997-01-29Per Hedbor #define LOCK(X) key=_lock(X)
d7b0871997-08-31Per Hedbor #define UNLOCK() do{key=0;}while(0)
14179b1997-01-29Per Hedbor #else #define LOCK(X)
c95bd51998-01-30Henrik Grubbström (Grubba) #define UNLOCK()
14179b1997-01-29Per Hedbor #endif
a1334f1998-02-20Mirar (Pontus Hagland) string examine_return_mapping(mapping m) { string res; if (m->extra_heads) m->extra_heads=mkmapping(Array.map(indices(m->extra_heads), lower_case), values(m->extra_heads)); else m->extra_heads=([]); switch (m->error||200) { case 302: // redirect
10c7e11999-12-28Martin Nilsson  if (m->extra_heads &&
a1334f1998-02-20Mirar (Pontus Hagland)  (m->extra_heads->location))
dff8322003-10-20Martin Stjernholm  res = sprintf("Returned redirect to %O ", m->extra_heads->location);
a1334f1998-02-20Mirar (Pontus Hagland)  else
1a89f42000-08-14Martin Stjernholm  res = "Returned redirect, but no location header. ";
a1334f1998-02-20Mirar (Pontus Hagland)  break; case 401: if (m->extra_heads["www-authenticate"])
dff8322003-10-20Martin Stjernholm  res = sprintf("Returned authentication failed: %O ",
67f60e2000-07-09Martin Nilsson  m->extra_heads["www-authenticate"]);
a1334f1998-02-20Mirar (Pontus Hagland)  else
1a89f42000-08-14Martin Stjernholm  res = "Returned authentication failed. ";
a1334f1998-02-20Mirar (Pontus Hagland)  break; case 200:
1a89f42000-08-14Martin Stjernholm  res = "Returned ok. ";
a1334f1998-02-20Mirar (Pontus Hagland)  break;
a8f1b32000-01-31Per Hedbor 
a1334f1998-02-20Mirar (Pontus Hagland)  default:
dff8322003-10-20Martin Stjernholm  res = sprintf("Returned %O. ", m->error);
a1334f1998-02-20Mirar (Pontus Hagland)  } if (!zero_type(m->len)) if (m->len<0)
434bac2000-07-14Andreas Lange  res += "No data ";
a1334f1998-02-20Mirar (Pontus Hagland)  else
dff8322003-10-20Martin Stjernholm  res += sprintf("%O bytes ", m->len);
a1334f1998-02-20Mirar (Pontus Hagland)  else if (stringp(m->data))
434bac2000-07-14Andreas Lange  res += sprintf("%d bytes ", strlen(m->data));
a1334f1998-02-20Mirar (Pontus Hagland)  else if (objectp(m->file)) if (catch {
1f4a6c2000-08-28Per Hedbor  Stat a=m->file->stat();
dff8322003-10-20Martin Stjernholm  res += sprintf("%O bytes ", a[1]-m->file->tell());
b461641998-10-11Henrik Grubbström (Grubba)  })
1a89f42000-08-14Martin Stjernholm  res += "? bytes ";
a1334f1998-02-20Mirar (Pontus Hagland) 
1a89f42000-08-14Martin Stjernholm  if (m->data) res += "(static)";
434bac2000-07-14Andreas Lange  else if (m->file) res += "(open file)";
a1334f1998-02-20Mirar (Pontus Hagland) 
dffa222000-12-10Per Hedbor  if (stringp(m->extra_heads["content-type"]) ||
b461641998-10-11Henrik Grubbström (Grubba)  stringp(m->type)) {
dff8322003-10-20Martin Stjernholm  res += sprintf(" of %O", m->type||m->extra_heads["content-type"]);
b461641998-10-11Henrik Grubbström (Grubba)  }
a1334f1998-02-20Mirar (Pontus Hagland)  return res; }
c5e0961999-10-04Per Hedbor 
3e09172004-05-03Henrik Grubbström (Grubba) //! Find all applicable locks for this user on @[path]. multiset(DAVLock) find_locks(string path, int(0..1) recursive, int(0..1) exclude_shared, RequestID id) {
3681412004-05-07Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(0, "find_locks(%O, %O, %O, X)", path, recursive, exclude_shared);
3e09172004-05-03Henrik Grubbström (Grubba)  multiset(DAVLock) locks = (<>); foreach(location_module_cache||location_modules(), [string loc, function func]) {
3681412004-05-07Henrik Grubbström (Grubba)  SIMPLE_TRACE_ENTER(function_object(func), "Finding locks in %O.", loc);
3e09172004-05-03Henrik Grubbström (Grubba)  string subpath; if (has_prefix(path, loc)) { // path == loc + subpath. subpath = path[sizeof(loc)..]; } else if (recursive && has_prefix(loc, path)) { // loc == path + ignored. subpath = "/"; } else { // Does not apply to this location module.
e4acd92004-05-06Henrik Grubbström (Grubba)  TRACE_LEAVE("Skip this module.");
3e09172004-05-03Henrik Grubbström (Grubba)  continue; }
e4acd92004-05-06Henrik Grubbström (Grubba)  TRACE_ENTER(sprintf("subpath: %O", subpath), function_object(func)->find_locks);
3e09172004-05-03Henrik Grubbström (Grubba)  multiset(DAVLock) sub_locks = function_object(func)->find_locks(subpath, recursive, exclude_shared, id);
e4acd92004-05-06Henrik Grubbström (Grubba)  TRACE_LEAVE(""); if (sub_locks) {
3681412004-05-07Henrik Grubbström (Grubba)  SIMPLE_TRACE_LEAVE("Got some locks: %O", sub_locks);
e4acd92004-05-06Henrik Grubbström (Grubba)  locks |= sub_locks; } else { TRACE_LEAVE("Got no locks."); }
3e09172004-05-03Henrik Grubbström (Grubba)  }
3681412004-05-07Henrik Grubbström (Grubba)  SIMPLE_TRACE_LEAVE("Returning %O", locks);
3e09172004-05-03Henrik Grubbström (Grubba)  return locks; }
38c76b2004-04-30Henrik Grubbström (Grubba) //! Check if there are any applicable locks for this user on @[path].
c85afa2004-05-07Martin Stjernholm DAVLock|LockFlag check_locks(string path, int(0..1) recursive, RequestID id)
38c76b2004-04-30Henrik Grubbström (Grubba) {
c85afa2004-05-07Martin Stjernholm  LockFlag state = 0;
38c76b2004-04-30Henrik Grubbström (Grubba)  foreach(location_module_cache||location_modules(), [string loc, function func]) { string subpath;
c85afa2004-05-07Martin Stjernholm  int check_above;
38c76b2004-04-30Henrik Grubbström (Grubba)  if (has_prefix(path, loc)) { // path == loc + subpath. subpath = path[sizeof(loc)..]; } else if (recursive && has_prefix(loc, path)) { // loc == path + ignored.
c85afa2004-05-07Martin Stjernholm  subpath = ""; check_above = 1;
38c76b2004-04-30Henrik Grubbström (Grubba)  } else { // Does not apply to this location module. continue; }
c85afa2004-05-07Martin Stjernholm  int/*LockFlag*/|DAVLock lock_info =
38c76b2004-04-30Henrik Grubbström (Grubba)  function_object(func)->check_locks(subpath, recursive, id);
9f8a912004-05-04Henrik Grubbström (Grubba)  if (objectp(lock_info)) {
c85afa2004-05-07Martin Stjernholm  if (!check_above) {
9f8a912004-05-04Henrik Grubbström (Grubba)  return lock_info; } else {
c85afa2004-05-07Martin Stjernholm  lock_info = LOCK_OWN_BELOW; // We have a lock on some subpath.
9f8a912004-05-04Henrik Grubbström (Grubba)  } }
c85afa2004-05-07Martin Stjernholm  else if (check_above && (lock_info & 1)) // Convert LOCK_*_AT to LOCK_*_BELOW. lock_info &= ~1;
9f8a912004-05-04Henrik Grubbström (Grubba)  if (lock_info > state) state = lock_info;
c85afa2004-05-07Martin Stjernholm  if (state == LOCK_EXCL_AT) return LOCK_EXCL_AT; // Doesn't get any worse.
b8f6272010-06-28Martin Jonsson  if (function_object(func)->webdav_opaque) break;
38c76b2004-04-30Henrik Grubbström (Grubba)  }
9f8a912004-05-04Henrik Grubbström (Grubba)  return state;
38c76b2004-04-30Henrik Grubbström (Grubba) }
fc40392008-08-15Martin Stjernholm protected multiset(DAVLock) active_locks = (<>);
4dfab22004-05-14Henrik Grubbström (Grubba) 
9874072004-05-04Henrik Grubbström (Grubba) //! Unlock the lock represented by @[lock] on @[path].
38c76b2004-04-30Henrik Grubbström (Grubba) //! //! @returns //! Returns a result-mapping on error, and @expr{0@} (zero) on success.
9874072004-05-04Henrik Grubbström (Grubba) mapping(string:mixed) unlock_file(string path, DAVLock lock, RequestID id)
38c76b2004-04-30Henrik Grubbström (Grubba) { // Canonicalize path. if (!has_suffix(path, "/")) path+="/"; foreach(location_module_cache||location_modules(), [string loc, function func]) { if (has_prefix(path, loc)) { // path == loc + subpath. mapping(string:mixed) ret =
9874072004-05-04Henrik Grubbström (Grubba)  function_object(func)->unlock_file(path[sizeof(loc)..], lock, id); // FIXME: Semantics for partial unlocking? if (ret) return ret; } else if (lock->recursive && has_prefix(loc, path)) { // loc == path + ignored. mapping(string:mixed) ret = function_object(func)->unlock_file("/", lock, id);
38c76b2004-04-30Henrik Grubbström (Grubba)  // FIXME: Semantics for partial unlocking? if (ret) return ret; }
b8f6272010-06-28Martin Jonsson  if (function_object(func)->webdav_opaque) break;
38c76b2004-04-30Henrik Grubbström (Grubba)  }
4dfab22004-05-14Henrik Grubbström (Grubba)  active_locks[lock] = 0;
9874072004-05-04Henrik Grubbström (Grubba)  // destruct(lock);
38c76b2004-04-30Henrik Grubbström (Grubba)  return 0; }
4dfab22004-05-14Henrik Grubbström (Grubba) //! Force expiration of any locks that have timed out. int expire_locks(RequestID id) { int t = time(1); int min_time = 0x7fffffff; foreach(active_locks; DAVLock lock;) { if (lock->expiry_time) { if (lock->expiry_time < t) { unlock_file(lock->path, lock, id); } else if (lock->expiry_time < min_time) { min_time = lock->expiry_time; } } } return min_time - t; }
9cda8f2010-11-02Martin Jonsson mixed expire_lock_loop_handle;
fc40392008-08-15Martin Stjernholm protected void expire_lock_loop()
4dfab22004-05-14Henrik Grubbström (Grubba) { int t = expire_locks(0); // NOTE: Called with RequestID 0! if (sizeof(active_locks)) {
9cda8f2010-11-02Martin Jonsson  t = max (t, 1); // Wait at least one second before the next run. t = min (t, 3600); // Expire locks at least once every hour. if (expire_lock_loop_handle) remove_call_out (expire_lock_loop_handle); expire_lock_loop_handle = roxen.background_run(t, expire_lock_loop);
4dfab22004-05-14Henrik Grubbström (Grubba)  } } //! Refresh a lock. //! //! Update the expiry time for the lock. void refresh_lock(DAVLock lock) { if (lock->expiry_delta) {
9cda8f2010-11-02Martin Jonsson  // Use time() instead of time(1) to avoid expiring the lock too // early if the returned time is old. Probably unlikely, but // anyways. lock->expiry_time = lock->expiry_delta + time();
4dfab22004-05-14Henrik Grubbström (Grubba)  } }
29f2292004-04-28Henrik Grubbström (Grubba) //! Attempt to lock @[path]. //! //! @param path //! Path to lock. //! //! @param locktype //! Type of lock (currently only @expr{"DAV:write"@} is defined). //! //! @param lockscope //! Scope of lock either @expr{"DAV:exclusive"@} or //! @expr{"DAV:shared"@}. //!
d1728c2004-05-14Henrik Grubbström (Grubba) //! @param expiry_delta //! Idle time in seconds before the lock expires. @expr{0@} (zero) //! means no expiry. //!
29f2292004-04-28Henrik Grubbström (Grubba) //! @returns //! Returns a result mapping on failure,
38c76b2004-04-30Henrik Grubbström (Grubba) //! and the resulting @[DAVLock] on success. mapping(string:mixed)|DAVLock lock_file(string path, int(0..1) recursive, string lockscope, string locktype,
fefe702004-05-14Henrik Grubbström (Grubba)  int(0..) expiry_delta,
73f9322004-05-06Martin Stjernholm  array(Parser.XML.Tree.Node) owner,
38c76b2004-04-30Henrik Grubbström (Grubba)  RequestID id)
29f2292004-04-28Henrik Grubbström (Grubba) { // Canonicalize path. if (!has_suffix(path, "/")) path+="/"; // First check if there's already some lock on path that prevents // us from locking it.
c85afa2004-05-07Martin Stjernholm  int/*LockFlag*/|DAVLock lock_info = check_locks(path, recursive, id);
38c76b2004-04-30Henrik Grubbström (Grubba)  if (!intp(lock_info)) { // We already hold a lock that prevents us.
476e9c2004-05-10Henrik Grubbström (Grubba)  if (id->request_headers->if) { return Roxen.http_status(412, "Precondition Failed"); } else { return Roxen.http_status(423, "Locked"); }
c85afa2004-05-07Martin Stjernholm  } else if (lockscope == "DAV:exclusive" ? lock_info >= LOCK_SHARED_BELOW : lock_info >= LOCK_OWN_BELOW) {
38c76b2004-04-30Henrik Grubbström (Grubba)  // Some other lock prevents us. return Roxen.http_status(423, "Locked");
29f2292004-04-28Henrik Grubbström (Grubba)  }
38c76b2004-04-30Henrik Grubbström (Grubba)  // Create the new lock.
29f2292004-04-28Henrik Grubbström (Grubba) 
803cdc2004-05-03Henrik Grubbström (Grubba)  string locktoken = "opaquelocktoken:" + roxen->new_uuid_string();
38c76b2004-04-30Henrik Grubbström (Grubba)  DAVLock lock = DAVLock(locktoken, path, recursive, lockscope, locktype,
fefe702004-05-14Henrik Grubbström (Grubba)  expiry_delta, owner);
29f2292004-04-28Henrik Grubbström (Grubba)  foreach(location_module_cache||location_modules(), [string loc, function func]) { string subpath; if (has_prefix(path, loc)) { // path == loc + subpath. subpath = path[sizeof(loc)..]; } else if (recursive && has_prefix(loc, path)) { // loc == path + ignored. subpath = "/"; } else { // Does not apply to this location module. continue; }
4dfab22004-05-14Henrik Grubbström (Grubba)  mapping(string:mixed) lock_error =
38c76b2004-04-30Henrik Grubbström (Grubba)  function_object(func)->lock_file(subpath, lock, id);
4dfab22004-05-14Henrik Grubbström (Grubba)  if (lock_error) {
9874072004-05-04Henrik Grubbström (Grubba)  // Failure. Unlock the new lock. foreach(location_module_cache||location_modules(), [string loc2, function func2]) { if (has_prefix(path, loc2)) { // path == loc2 + subpath. mapping(string:mixed) ret = function_object(func2)->unlock_file(path[sizeof(loc2)..], lock, id); } else if (recursive && has_prefix(loc2, path)) { // loc2 == path + ignored. mapping(string:mixed) ret = function_object(func2)->unlock_file("/", lock, id); } if (func == func2) break;
29f2292004-04-28Henrik Grubbström (Grubba)  }
9874072004-05-04Henrik Grubbström (Grubba)  // destruct(lock);
4dfab22004-05-14Henrik Grubbström (Grubba)  return lock_error;
29f2292004-04-28Henrik Grubbström (Grubba)  }
b8f6272010-06-28Martin Jonsson  if (function_object(func)->webdav_opaque) break;
29f2292004-04-28Henrik Grubbström (Grubba)  }
4dfab22004-05-14Henrik Grubbström (Grubba)  if (expiry_delta) { // Lock with timeout. // FIXME: Race-conditions. if (!sizeof(active_locks)) { // Start the lock expiration loop. active_locks[lock] = 1; expire_lock_loop(); } else { active_locks[lock] = 1; } }
29f2292004-04-28Henrik Grubbström (Grubba)  // Success.
38c76b2004-04-30Henrik Grubbström (Grubba)  return lock;
29f2292004-04-28Henrik Grubbström (Grubba) }
cd92872000-08-15Johan Sundström mapping|int(-1..0) low_get_file(RequestID id, int|void no_magic) //! The function that actually tries to find the data requested. All //! modules except last and filter type modules are mapped, in order, //! and the first one that returns a suitable response is used. If //! `no_magic' is set to one, the internal magic roxen images and the
8062042004-04-20Henrik Grubbström (Grubba) //! @[find_internal()] callbacks will be ignored.
cd92872000-08-15Johan Sundström //! //! The return values 0 (no such file) and -1 (the data is a //! directory) are only returned when `no_magic' was set to 1; //! otherwise a result mapping is always generated.
14179b1997-01-29Per Hedbor { #ifdef MODULE_LEVEL_SECURITY int slevel; #endif #ifdef THREADS
3e3bab2001-01-19Per Hedbor  Thread.MutexKey key;
14179b1997-01-29Per Hedbor #endif
db5f6b2001-07-31Per Hedbor  id->not_query = VFS.normalize_path( id->not_query );
5655dc2001-06-06Per Hedbor 
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Request for %s", id->not_query), 0);
14179b1997-01-29Per Hedbor  string file=id->not_query; string loc; function funp; mixed tmp, tmp2;
cd92872000-08-15Johan Sundström  mapping|object(Stdio.File)|int fid;
14179b1997-01-29Per Hedbor  if(!no_magic) {
9c19002001-02-27Per Hedbor  TIMER_START(internal_magic);
41b77c1999-07-15David Hedbor #ifndef NO_INTERNAL_HACK
fd93022000-05-05Martin Nilsson  // Find internal-foo-bar images
41b77c1999-07-15David Hedbor  // min length == 17 (/internal-roxen-?..) // This will save some time indeed.
fd93022000-05-05Martin Nilsson  string type;
67f60e2000-07-09Martin Nilsson  if(sizeof(file) > 17 &&
51d4d22001-01-04Martin Nilsson #if ROXEN_COMPAT <= 2.1
67f60e2000-07-09Martin Nilsson  (file[0] == '/') &&
cd92872000-08-15Johan Sundström  sscanf(file, "%*s/internal-%s-%[^/]", type, loc) == 3
67f60e2000-07-09Martin Nilsson #else
cd92872000-08-15Johan Sundström  sscanf(file, "/internal-%s-%[^/]", type, loc) == 2
67f60e2000-07-09Martin Nilsson #endif
cd92872000-08-15Johan Sundström  ) {
41b77c1999-07-15David Hedbor  switch(type) {
fd93022000-05-05Martin Nilsson  case "roxen":
d738ba2002-04-10Jonas Wallden  // Mark all /internal-roxen-* as cacheable even though the user might be // authenticated (which normally disables protocol-level caching).
f910602004-05-20Jonas Wallden  RAISE_CACHE(60 * 60 * 24 * 365); // 1 year
beddd12003-11-25Anders Johansson  PROTO_CACHE();
dd71832010-11-10Jonas Wallden  id->set_response_header("Cache-Control", "public, max-age=31536000");
d738ba2002-04-10Jonas Wallden 
a7d0342000-11-13Martin Nilsson  TRACE_LEAVE("Magic internal roxen image");
5eb2832001-01-03Martin Nilsson  if(loc=="unit" || loc=="pixel-of-destiny")
9c19002001-02-27Per Hedbor  { TIMER_END(internal_magic); return (["data":"GIF89a\1\0\1\0\200ÿ\0ÀÀÀ\0\0\0!ù\4\1\0\0\0\0," "\0\0\0\0\1\0\1\0\0\1\1""2\0;",
2f51ba2002-03-22Martin Stjernholm  "type":"image/gif", "stat": ({0, 0, 0, 900000000, 0, 0, 0})]);
9c19002001-02-27Per Hedbor  }
5eb2832001-01-03Martin Nilsson  if(has_prefix(loc, "pixel-"))
9c19002001-02-27Per Hedbor  { TIMER_END(internal_magic); return (["data":sprintf("GIF89a\1\0\1\0\200\0\0\0\0\0%c%c%c,\0\0\0" "\0\1\0\1\0\0\2\2L\1\0;",
5eb2832001-01-03Martin Nilsson  @parse_color(loc[6..])),
2f51ba2002-03-22Martin Stjernholm  "type":"image/gif", "stat": ({0, 0, 0, 900000000, 0, 0, 0})]);
9c19002001-02-27Per Hedbor  } TIMER_END(internal_magic);
2a346d2000-09-19Per Hedbor  return internal_roxen_image(loc, id);
fd93022000-05-05Martin Nilsson 
41b77c1999-07-15David Hedbor  case "gopher":
a7d0342000-11-13Martin Nilsson  TRACE_LEAVE("Magic internal gopher image");
9c19002001-02-27Per Hedbor  TIMER_END(internal_magic);
14179b1997-01-29Per Hedbor  return internal_gopher_image(loc);
41d0f91998-02-20Per Hedbor  }
14179b1997-01-29Per Hedbor  } #endif
10c7e11999-12-28Martin Nilsson 
fd93022000-05-05Martin Nilsson  // Locate internal location resources.
9ac0332002-11-05Anders Johansson  if(has_prefix(file, internal_location))
5839c31999-01-22Marcus Comstedt  {
434bac2000-07-14Andreas Lange  TRACE_ENTER("Magic internal module location", 0);
e351dd1999-11-29Per Hedbor  RoxenModule module;
5839c31999-01-22Marcus Comstedt  string name, rest; function find_internal;
9ac0332002-11-05Anders Johansson  if(2==sscanf(file[strlen(internal_location)..], "%s/%s", name, rest) &&
5839c31999-01-22Marcus Comstedt  (module = find_module(replace(name, "!", "#"))) && (find_internal = module->find_internal)) { #ifdef MODULE_LEVEL_SECURITY if(tmp2 = check_security(find_internal, id, slevel)) if(intp(tmp2)) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission to access module denied.");
5839c31999-01-22Marcus Comstedt  find_internal = 0; } else { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Request denied.");
9c19002001-02-27Per Hedbor  TIMER_END(internal_magic);
5839c31999-01-22Marcus Comstedt  return tmp2; } #endif
41b77c1999-07-15David Hedbor  if(find_internal)
5839c31999-01-22Marcus Comstedt  {
434bac2000-07-14Andreas Lange  TRACE_ENTER("Calling find_internal()...", find_internal);
4717052001-05-07Per Hedbor  PROF_ENTER("find_internal","location");
5839c31999-01-22Marcus Comstedt  LOCK(find_internal); fid=find_internal( rest, id ); UNLOCK();
02c6642001-08-22Martin Stjernholm  //TRACE_LEAVE(sprintf("find_internal has returned %O", fid)); TRACE_LEAVE("");
4717052001-05-07Per Hedbor  PROF_LEAVE("find_internal","location");
5839c31999-01-22Marcus Comstedt  if(fid) { if(mappingp(fid)) { TRACE_LEAVE(""); TRACE_LEAVE(examine_return_mapping(fid));
9c19002001-02-27Per Hedbor  TIMER_END(internal_magic);
5839c31999-01-22Marcus Comstedt  return fid; } else { #ifdef MODULE_LEVEL_SECURITY int oslevel = slevel;
e280792004-03-03Anders Johansson  array slca; if(slca = security_level_cache[ Roxen.get_owning_module (find_internal) ]) slevel = slca[1];
5910aa2004-02-17Martin Stjernholm  // security_level_cache from
5839c31999-01-22Marcus Comstedt  // check_security id->misc->seclevel = slevel; #endif if(objectp(fid))
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned open filedescriptor. "
5839c31999-01-22Marcus Comstedt #ifdef MODULE_LEVEL_SECURITY +(slevel != oslevel?
434bac2000-07-14Andreas Lange  sprintf(" The security level is now %d.", slevel):"")
5839c31999-01-22Marcus Comstedt #endif
67f60e2000-07-09Martin Nilsson  );
5839c31999-01-22Marcus Comstedt  else
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned directory indicator."
5839c31999-01-22Marcus Comstedt #ifdef MODULE_LEVEL_SECURITY +(oslevel != slevel?
434bac2000-07-14Andreas Lange  sprintf(" The security level is now %d.", slevel):"")
5839c31999-01-22Marcus Comstedt #endif ); } } else TRACE_LEAVE(""); } else TRACE_LEAVE(""); } else TRACE_LEAVE(""); }
9c19002001-02-27Per Hedbor  TIMER_END(internal_magic);
14179b1997-01-29Per Hedbor  } // Well, this just _might_ be somewhat over-optimized, since it is
10c7e11999-12-28Martin Nilsson  // quite unreadable, but, you cannot win them all..
5839c31999-01-22Marcus Comstedt  if(!fid) {
14179b1997-01-29Per Hedbor #ifdef URL_MODULES
f128901997-08-15Henrik Grubbström (Grubba)  // Map URL-modules
9c19002001-02-27Per Hedbor  TIMER_START(url_modules);
517c7e2000-09-30Per Hedbor  foreach(url_module_cache||url_modules(), funp)
41d0f91998-02-20Per Hedbor  {
4717052001-05-07Per Hedbor  PROF_ENTER(Roxen.get_owning_module(funp)->module_name,"url module");
5839c31999-01-22Marcus Comstedt  LOCK(funp);
434bac2000-07-14Andreas Lange  TRACE_ENTER("URL module", funp);
5839c31999-01-22Marcus Comstedt  tmp=funp( id, file ); UNLOCK();
4717052001-05-07Per Hedbor  PROF_LEAVE(Roxen.get_owning_module(funp)->module_name,"url module");
10c7e11999-12-28Martin Nilsson  if(mappingp(tmp))
5839c31999-01-22Marcus Comstedt  { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
9c19002001-02-27Per Hedbor  TIMER_END(url_modules);
5839c31999-01-22Marcus Comstedt  return tmp; } if(objectp( tmp )) {
a51a902001-11-12Martin Stjernholm  mixed err;
a8f1b32000-01-31Per Hedbor 
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->get_file_nest++;
5839c31999-01-22Marcus Comstedt  err = catch {
6906942004-05-21Henrik Grubbström (Grubba)  if( id->misc->get_file_nest < 20 )
5839c31999-01-22Marcus Comstedt  tmp = (id->conf || this_object())->low_get_file( tmp, no_magic ); else {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Too deep recursion");
5839c31999-01-22Marcus Comstedt  error("Too deep recursion in roxen::get_file() while mapping " +file+".\n"); } };
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->get_file_nest = 0;
5839c31999-01-22Marcus Comstedt  if(err) throw(err); TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
9c19002001-02-27Per Hedbor  TIMER_END(url_modules);
5839c31999-01-22Marcus Comstedt  return tmp; }
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
9c19002001-02-27Per Hedbor  TIMER_END(url_modules);
41d0f91998-02-20Per Hedbor  }
14179b1997-01-29Per Hedbor #endif
c5e0961999-10-04Per Hedbor 
9c19002001-02-27Per Hedbor  TIMER_START(location_modules);
517c7e2000-09-30Per Hedbor  foreach(location_module_cache||location_modules(), tmp)
14179b1997-01-29Per Hedbor  {
5839c31999-01-22Marcus Comstedt  loc = tmp[0];
73f9322004-05-06Martin Stjernholm  if(has_prefix(file, loc))
5839c31999-01-22Marcus Comstedt  {
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Location module [%s] ", loc), tmp[1]);
df8d711998-02-27Per Hedbor #ifdef MODULE_LEVEL_SECURITY
5839c31999-01-22Marcus Comstedt  if(tmp2 = check_security(tmp[1], id, slevel)) if(intp(tmp2)) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission to access module denied.");
5839c31999-01-22Marcus Comstedt  continue; } else { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Request denied.");
9c19002001-02-27Per Hedbor  TIMER_END(location_modules);
5839c31999-01-22Marcus Comstedt  return tmp2; }
14179b1997-01-29Per Hedbor #endif
7937df2001-05-16Per Hedbor  PROF_ENTER(Roxen.get_owning_module(tmp[1])->module_name,"location");
434bac2000-07-14Andreas Lange  TRACE_ENTER("Calling find_file()...", 0);
5839c31999-01-22Marcus Comstedt  LOCK(tmp[1]); fid=tmp[1]( file[ strlen(loc) .. ] + id->extra_extension, id); UNLOCK();
b8b1072000-03-24Per Hedbor  TRACE_LEAVE("");
7937df2001-05-16Per Hedbor  PROF_LEAVE(Roxen.get_owning_module(tmp[1])->module_name,"location");
5839c31999-01-22Marcus Comstedt  if(fid)
14179b1997-01-29Per Hedbor  {
5839c31999-01-22Marcus Comstedt  id->virtfile = loc;
a8f1b32000-01-31Per Hedbor 
5839c31999-01-22Marcus Comstedt  if(mappingp(fid)) {
fe616a2003-06-10Anders Johansson  TRACE_LEAVE(""); // Location module [...]
5839c31999-01-22Marcus Comstedt  TRACE_LEAVE(examine_return_mapping(fid));
9c19002001-02-27Per Hedbor  TIMER_END(location_modules);
5839c31999-01-22Marcus Comstedt  return fid; } else {
14179b1997-01-29Per Hedbor #ifdef MODULE_LEVEL_SECURITY
5839c31999-01-22Marcus Comstedt  int oslevel = slevel;
e280792004-03-03Anders Johansson  array slca; if(slca = security_level_cache[ Roxen.get_owning_module (tmp[1]) ]) slevel = slca[1];
5910aa2004-02-17Martin Stjernholm  // security_level_cache from
5839c31999-01-22Marcus Comstedt  // check_security id->misc->seclevel = slevel;
14179b1997-01-29Per Hedbor #endif
5839c31999-01-22Marcus Comstedt  if(objectp(fid))
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned open filedescriptor."
41d0f91998-02-20Per Hedbor #ifdef MODULE_LEVEL_SECURITY
5839c31999-01-22Marcus Comstedt  +(slevel != oslevel?
434bac2000-07-14Andreas Lange  sprintf(" The security level is now %d.", slevel):"")
41d0f91998-02-20Per Hedbor #endif
a8f1b32000-01-31Per Hedbor 
67f60e2000-07-09Martin Nilsson  );
5839c31999-01-22Marcus Comstedt  else
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned directory indicator."
41d0f91998-02-20Per Hedbor #ifdef MODULE_LEVEL_SECURITY
5839c31999-01-22Marcus Comstedt  +(oslevel != slevel?
434bac2000-07-14Andreas Lange  sprintf(" The security level is now %d.", slevel):"")
41d0f91998-02-20Per Hedbor #endif
5839c31999-01-22Marcus Comstedt  ); break; } } else TRACE_LEAVE("");
41b77c1999-07-15David Hedbor  } else if(strlen(loc)-1==strlen(file) && file+"/" == loc) {
10c7e11999-12-28Martin Nilsson  // This one is here to allow accesses to /local, even if
5839c31999-01-22Marcus Comstedt  // the mountpoint is /local/. It will slow things down, but...
67f60e2000-07-09Martin Nilsson 
434bac2000-07-14Andreas Lange  TRACE_ENTER("Automatic redirect to location_module.", tmp[1]);
fe616a2003-06-10Anders Johansson  TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
a8f1b32000-01-31Per Hedbor 
41b77c1999-07-15David Hedbor  // Keep query (if any).
fd93022000-05-05Martin Nilsson  // FIXME: Should probably keep config <foo>
f491312004-10-11Martin Stjernholm  string new_query = Roxen.http_encode_invalids(id->not_query) + "/" +
41b77c1999-07-15David Hedbor  (id->query?("?"+id->query):"");
fd93022000-05-05Martin Nilsson  new_query=Roxen.add_pre_state(new_query, id->prestate);
a8f1b32000-01-31Per Hedbor 
9c19002001-02-27Per Hedbor  TIMER_END(location_modules);
dfe0362000-03-19Martin Nilsson  return Roxen.http_redirect(new_query, id);
41d0f91998-02-20Per Hedbor  }
14179b1997-01-29Per Hedbor  }
9c19002001-02-27Per Hedbor  TIMER_END(location_modules);
14179b1997-01-29Per Hedbor  }
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  if(fid == -1) {
41d0f91998-02-20Per Hedbor  if(no_magic) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("No magic requested. Returning -1.");
41d0f91998-02-20Per Hedbor  return -1; }
9c19002001-02-27Per Hedbor  TIMER_START(directory_module);
14179b1997-01-29Per Hedbor  if(dir_module) {
7937df2001-05-16Per Hedbor  PROF_ENTER(dir_module->module_name,"directory");
14179b1997-01-29Per Hedbor  LOCK(dir_module);
434bac2000-07-14Andreas Lange  TRACE_ENTER("Directory module", dir_module);
14179b1997-01-29Per Hedbor  fid = dir_module->parse_directory(id);
fe616a2003-06-10Anders Johansson  TRACE_LEAVE("");
14179b1997-01-29Per Hedbor  UNLOCK();
7937df2001-05-16Per Hedbor  PROF_LEAVE(dir_module->module_name,"directory");
14179b1997-01-29Per Hedbor  } else
41d0f91998-02-20Per Hedbor  {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("No directory module. Returning 'no such file'");
14179b1997-01-29Per Hedbor  return 0;
41d0f91998-02-20Per Hedbor  }
9c19002001-02-27Per Hedbor  TIMER_END(directory_module);
10c7e11999-12-28Martin Nilsson  if(mappingp(fid))
41d0f91998-02-20Per Hedbor  {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
41d0f91998-02-20Per Hedbor  return (mapping)fid; }
14179b1997-01-29Per Hedbor  }
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  // Map the file extensions, but only if there is a file...
9c19002001-02-27Per Hedbor  TIMER_START(extension_module);
41b77c1999-07-15David Hedbor  if(objectp(fid) &&
f511c52001-08-31Per Hedbor  (tmp = file_extension_modules(loc = lower_case(Roxen.extension(id->not_query, id)))))
517c7e2000-09-30Per Hedbor  {
14179b1997-01-29Per Hedbor  foreach(tmp, funp) {
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Extension module [%s] ", loc), funp);
14179b1997-01-29Per Hedbor #ifdef MODULE_LEVEL_SECURITY if(tmp=check_security(funp, id, slevel)) if(intp(tmp)) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission to access module denied.");
14179b1997-01-29Per Hedbor  continue; } else
41d0f91998-02-20Per Hedbor  { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission denied");
9c19002001-02-27Per Hedbor  TIMER_END(extension_module);
14179b1997-01-29Per Hedbor  return tmp;
41d0f91998-02-20Per Hedbor  }
14179b1997-01-29Per Hedbor #endif
7937df2001-05-16Per Hedbor  PROF_ENTER(Roxen.get_owning_module(funp)->module_name,"ext");
14179b1997-01-29Per Hedbor  LOCK(funp); tmp=funp(fid, loc, id); UNLOCK();
7937df2001-05-16Per Hedbor  PROF_LEAVE(Roxen.get_owning_module(funp)->module_name,"ext");
14179b1997-01-29Per Hedbor  if(tmp) { if(!objectp(tmp))
41d0f91998-02-20Per Hedbor  { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
9c19002001-02-27Per Hedbor  TIMER_END(extension_module);
14179b1997-01-29Per Hedbor  return tmp;
41d0f91998-02-20Per Hedbor  }
cd92872000-08-15Johan Sundström  if(fid && tmp != fid)
41b77c1999-07-15David Hedbor  destruct(fid);
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned new open file");
14179b1997-01-29Per Hedbor  fid = tmp; break;
41d0f91998-02-20Per Hedbor  } else TRACE_LEAVE("");
14179b1997-01-29Per Hedbor  }
41b77c1999-07-15David Hedbor  }
9c19002001-02-27Per Hedbor  TIMER_END(extension_module);
fd93022000-05-05Martin Nilsson 
14179b1997-01-29Per Hedbor  if(objectp(fid)) {
9c19002001-02-27Per Hedbor  TIMER_START(content_type_module);
41b77c1999-07-15David Hedbor  if(stringp(id->extension)) {
14179b1997-01-29Per Hedbor  id->not_query += id->extension;
f511c52001-08-31Per Hedbor  loc = lower_case(Roxen.extension(id->not_query, id));
41b77c1999-07-15David Hedbor  }
434bac2000-07-14Andreas Lange  TRACE_ENTER("Content-type mapping module", types_module);
41b77c1999-07-15David Hedbor  tmp=type_from_filename(id->not_query, 1, loc);
42dfe62008-12-17Jonas Wallden  TRACE_LEAVE(tmp?sprintf("Returned type %O %s.", tmp[0], tmp[1]||"")
434bac2000-07-14Andreas Lange  : "Missing type.");
14179b1997-01-29Per Hedbor  if(tmp)
41d0f91998-02-20Per Hedbor  { TRACE_LEAVE("");
9c19002001-02-27Per Hedbor  TIMER_END(content_type_module);
14179b1997-01-29Per Hedbor  return ([ "file":fid, "type":tmp[0], "encoding":tmp[1] ]);
10c7e11999-12-28Martin Nilsson  }
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
9c19002001-02-27Per Hedbor  TIMER_END(content_type_module);
14179b1997-01-29Per Hedbor  return ([ "file":fid, ]); }
9c19002001-02-27Per Hedbor 
41d0f91998-02-20Per Hedbor  if(!fid)
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned 'no such file'.");
41d0f91998-02-20Per Hedbor  else
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning data");
14179b1997-01-29Per Hedbor  return fid; }
6877622004-04-19Martin Stjernholm #define TRY_FIRST_MODULES(FILE, RECURSE_CALL) do { \ TIMER_START(first_modules); \ foreach(first_module_cache||first_modules(), function funp) \ { \ TRACE_ENTER ("First try module", funp); \ if(FILE = funp( id )) { \ TRACE_LEAVE ("Got response"); \ break; \ } \ TRACE_LEAVE ("No response"); \ if(id->conf != this_object()) { \ TRACE_ENTER (sprintf ("Configuration changed to %O - " \ "redirecting", id->conf), 0); \ TRACE_LEAVE (""); \ TIMER_END (first_modules); \ TIMER_END (handle_request); \ return id->conf->RECURSE_CALL; \ } \ } \ TIMER_END(first_modules); \ } while (0) #define TRY_LAST_MODULES(FILE, RECURSE_CALL) do { \ mixed ret; \ TIMER_START(last_modules); \ foreach(last_module_cache||last_modules(), function funp) { \ TRACE_ENTER ("Last try module", funp); \ if(ret = funp(id)) { \ if (ret == 1) { \ TRACE_LEAVE ("Request rewritten - try again"); \ TIMER_END(last_modules); \ TIMER_END(handle_request); \ return RECURSE_CALL; \ } \ TRACE_LEAVE ("Got response"); \ break; \ } \ TRACE_LEAVE ("No response"); \ } \ FILE = ret; \ TIMER_END(last_modules); \ } while (0) mixed handle_request( RequestID id, void|int recurse_count)
c7a5f01999-02-16Per Hedbor { mixed file;
10c7e11999-12-28Martin Nilsson  REQUEST_WERR("handle_request()");
6877622004-04-19Martin Stjernholm  if (recurse_count > 50) { TRACE_ENTER ("Looped " + recurse_count + " times in internal redirects - giving up", 0); TRACE_LEAVE (""); return 0;
c7a5f01999-02-16Per Hedbor  }
6877622004-04-19Martin Stjernholm  TIMER_START(handle_request); TRY_FIRST_MODULES (file, handle_request (id, recurse_count + 1));
fd8b151999-05-19David Hedbor  if(!mappingp(file) && !mappingp(file = get_file(id)))
6877622004-04-19Martin Stjernholm  TRY_LAST_MODULES (file, handle_request(id, recurse_count + 1));
9c19002001-02-27Per Hedbor  TIMER_END(handle_request);
6877622004-04-19Martin Stjernholm 
10c7e11999-12-28Martin Nilsson  REQUEST_WERR("handle_request(): Done");
9c19002001-02-27Per Hedbor  MERGE_TIMERS(roxen);
c7a5f01999-02-16Per Hedbor  return file; }
9a0d4b2004-04-20Henrik Grubbström (Grubba) mapping|int get_file(RequestID id, int|void no_magic, int|void internal_get)
cd92872000-08-15Johan Sundström //! Return a result mapping for the id object at hand, mapping all //! modules, including the filter modules. This function is mostly a
09a0682004-04-20Henrik Grubbström (Grubba) //! wrapper for @[low_get_file()].
14179b1997-01-29Per Hedbor {
9c19002001-02-27Per Hedbor  TIMER_START(get_file);
576c112000-03-07Martin Stjernholm  int orig_internal_get = id->misc->internal_get; id->misc->internal_get = internal_get;
483b062002-05-16Stefan Wallström  RequestID root_id = id->root_id || id; root_id->misc->_request_depth++; if(sub_req_limit && root_id->misc->_request_depth > sub_req_limit)
9ac0332002-11-05Anders Johansson  error("Subrequest limit reached. (Possibly an insertion loop.)");
576c112000-03-07Martin Stjernholm 
fd93022000-05-05Martin Nilsson  mapping|int res; mapping res2;
14179b1997-01-29Per Hedbor  function tmp; res = low_get_file(id, no_magic);
9c19002001-02-27Per Hedbor  TIMER_END(get_file);
fd93022000-05-05Martin Nilsson 
14179b1997-01-29Per Hedbor  // finally map all filter type modules. // Filter modules are like TYPE_LAST modules, but they get called // for _all_ files.
9c19002001-02-27Per Hedbor  TIMER_START(filter_modules);
517c7e2000-09-30Per Hedbor  foreach(filter_module_cache||filter_modules(), tmp)
41d0f91998-02-20Per Hedbor  {
434bac2000-07-14Andreas Lange  TRACE_ENTER("Filter module", tmp);
4717052001-05-07Per Hedbor  PROF_ENTER(Roxen.get_owning_module(tmp)->module_name,"filter");
14179b1997-01-29Per Hedbor  if(res2=tmp(res,id)) {
8b815d2004-04-20Martin Stjernholm  if(mappingp(res) && res->file && (res2->file != res->file))
14179b1997-01-29Per Hedbor  destruct(res->file);
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Rewrote result.");
14179b1997-01-29Per Hedbor  res=res2;
41d0f91998-02-20Per Hedbor  } else TRACE_LEAVE("");
4717052001-05-07Per Hedbor  PROF_LEAVE(Roxen.get_owning_module(tmp)->module_name,"filter");
41d0f91998-02-20Per Hedbor  }
9c19002001-02-27Per Hedbor  TIMER_END(filter_modules);
576c112000-03-07Martin Stjernholm 
483b062002-05-16Stefan Wallström  root_id->misc->_request_depth--;
576c112000-03-07Martin Stjernholm  id->misc->internal_get = orig_internal_get;
14179b1997-01-29Per Hedbor  return res; }
9a8a152000-09-25Per Hedbor array(string) find_dir(string file, RequestID id, void|int(0..1) verbose)
14179b1997-01-29Per Hedbor {
fd93022000-05-05Martin Nilsson  array dir;
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("List directory %O.", file), 0);
10c7e11999-12-28Martin Nilsson 
db43d12000-11-24Martin Stjernholm  if(!sizeof (file) || file[0] != '/')
1935351997-04-28Henrik Grubbström (Grubba)  file = "/" + file;
f128901997-08-15Henrik Grubbström (Grubba) #ifdef URL_MODULES
fc0e5d1997-08-26Henrik Grubbström (Grubba) #ifdef THREADS
3e3bab2001-01-19Per Hedbor  Thread.MutexKey key;
fc0e5d1997-08-26Henrik Grubbström (Grubba) #endif
f128901997-08-15Henrik Grubbström (Grubba)  // Map URL-modules
517c7e2000-09-30Per Hedbor  foreach(url_modules(), function funp)
f128901997-08-15Henrik Grubbström (Grubba)  { string of = id->not_query; id->not_query = file; LOCK(funp);
434bac2000-07-14Andreas Lange  TRACE_ENTER("URL module", funp);
3e3bab2001-01-19Per Hedbor  mixed remap=funp( id, file );
f128901997-08-15Henrik Grubbström (Grubba)  UNLOCK();
fd93022000-05-05Martin Nilsson  if(mappingp( remap ))
f128901997-08-15Henrik Grubbström (Grubba)  { id->not_query=of;
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned 'No thanks'.");
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
f128901997-08-15Henrik Grubbström (Grubba)  return 0; }
fd93022000-05-05Martin Nilsson  if(objectp( remap ))
f128901997-08-15Henrik Grubbström (Grubba)  {
a51a902001-11-12Martin Stjernholm  mixed err;
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->find_dir_nest++;
10c7e11999-12-28Martin Nilsson 
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Recursing");
f128901997-08-15Henrik Grubbström (Grubba)  file = id->not_query; err = catch {
6906942004-05-21Henrik Grubbström (Grubba)  if( id->misc->find_dir_nest < 20 )
fd93022000-05-05Martin Nilsson  dir = (id->conf || this_object())->find_dir( file, id );
f128901997-08-15Henrik Grubbström (Grubba)  else error("Too deep recursion in roxen::find_dir() while mapping " +file+".\n"); };
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->find_dir_nest = 0;
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
f128901997-08-15Henrik Grubbström (Grubba)  if(err) throw(err);
fd93022000-05-05Martin Nilsson  return dir;
f128901997-08-15Henrik Grubbström (Grubba)  } id->not_query=of; }
a86c6c1997-09-22Henrik Grubbström (Grubba) #endif /* URL_MODULES */
f128901997-08-15Henrik Grubbström (Grubba) 
3e3bab2001-01-19Per Hedbor  array | mapping d;
e927de2000-05-06Martin Nilsson  array(string) locks=({});
3e3bab2001-01-19Per Hedbor  RoxenModule mod;
e927de2000-05-06Martin Nilsson  string loc;
517c7e2000-09-30Per Hedbor  foreach(location_modules(), array tmp)
14179b1997-01-29Per Hedbor  { loc = tmp[0];
a476711997-10-20Henrik Grubbström (Grubba)  if(!search(file, loc)) { /* file == loc + subpath */
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Location module [%s] ", loc), tmp[1]);
a78a591997-04-28Henrik Grubbström (Grubba) #ifdef MODULE_LEVEL_SECURITY
41d0f91998-02-20Per Hedbor  if(check_security(tmp[1], id)) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission denied");
41d0f91998-02-20Per Hedbor  continue; }
a78a591997-04-28Henrik Grubbström (Grubba) #endif
e927de2000-05-06Martin Nilsson  mod=function_object(tmp[1]); if(d=mod->find_dir(file[strlen(loc)..], id))
41d0f91998-02-20Per Hedbor  {
29a8071998-08-21David Hedbor  if(mappingp(d)) {
10c7e11999-12-28Martin Nilsson  if(d->files) {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Got exclusive directory."); TRACE_LEAVE(sprintf("Returning list of %d files.", sizeof(d->files)));
fd93022000-05-05Martin Nilsson  return d->files;
29a8071998-08-21David Hedbor  } else TRACE_LEAVE(""); } else {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Got files.");
e927de2000-05-06Martin Nilsson  if(!dir) dir=({ });
29a8071998-08-21David Hedbor  dir |= d; }
e927de2000-05-06Martin Nilsson  } else { if(verbose && mod->list_lock_files) locks |= mod->list_lock_files();
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
e927de2000-05-06Martin Nilsson  }
a476711997-10-20Henrik Grubbström (Grubba)  } else if((search(loc, file)==0) && (loc[strlen(file)-1]=='/') && (loc[0]==loc[-1]) && (loc[-1]=='/') &&
6c67c81998-02-28Johan Schön  (function_object(tmp[1])->stat_file(".", id))) {
a476711997-10-20Henrik Grubbström (Grubba)  /* loc == file + "/" + subpath + "/"
24fa081998-02-28Henrik Grubbström (Grubba)  * and stat_file(".") returns non-zero.
a476711997-10-20Henrik Grubbström (Grubba)  */
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Location module [%s] ", loc), tmp[1]);
a476711997-10-20Henrik Grubbström (Grubba)  loc=loc[strlen(file)..]; sscanf(loc, "%s/", loc);
df40452000-06-19Henrik Grubbström (Grubba)  if (dir) { dir |= ({ loc }); } else { dir = ({ loc }); }
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Added module mountpoint.");
14179b1997-01-29Per Hedbor  } }
e927de2000-05-06Martin Nilsson  if(!dir) return verbose ? ({0})+locks : ([])[0];
14179b1997-01-29Per Hedbor  if(sizeof(dir))
41d0f91998-02-20Per Hedbor  {
434bac2000-07-14Andreas Lange  TRACE_LEAVE(sprintf("Returning list of %d files.", sizeof(dir)));
14179b1997-01-29Per Hedbor  return dir;
10c7e11999-12-28Martin Nilsson  }
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returning 'No such directory'.");
fd93022000-05-05Martin Nilsson  return 0;
14179b1997-01-29Per Hedbor }
10c7e11999-12-28Martin Nilsson // Stat a virtual file.
14179b1997-01-29Per Hedbor 
9a8a152000-09-25Per Hedbor array(int)|Stat stat_file(string file, RequestID id)
14179b1997-01-29Per Hedbor {
1f4a6c2000-08-28Per Hedbor  mixed s, tmp;
c5e0961999-10-04Per Hedbor #ifdef THREADS
3e3bab2001-01-19Per Hedbor  Thread.MutexKey key;
c5e0961999-10-04Per Hedbor #endif
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Stat file %O.", file), 0);
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  file=replace(file, "//", "/"); // "//" is really "/" here...
f128901997-08-15Henrik Grubbström (Grubba) 
7484e22010-06-20Jonas Wallden  if (has_prefix(file, internal_location)) { TRACE_LEAVE("");
132efa2008-08-28Jonas Wallden  return 0;
7484e22010-06-20Jonas Wallden  }
132efa2008-08-28Jonas Wallden 
f128901997-08-15Henrik Grubbström (Grubba) #ifdef URL_MODULES // Map URL-modules
6906942004-05-21Henrik Grubbström (Grubba)  string of = id->not_query; id->not_query = file; foreach(url_module_cache||url_modules(), function funp)
f128901997-08-15Henrik Grubbström (Grubba)  {
434bac2000-07-14Andreas Lange  TRACE_ENTER("URL module", funp);
f128901997-08-15Henrik Grubbström (Grubba)  LOCK(funp); tmp=funp( id, file ); UNLOCK();
6906942004-05-21Henrik Grubbström (Grubba)  if (tmp) { if(mappingp( tmp )) { id->not_query = of; TRACE_LEAVE(""); TRACE_LEAVE("Returned 'No thanks'."); return 0; } if(objectp( tmp )) { mixed err; id->misc->stat_file_nest++; TRACE_LEAVE("Recursing"); err = catch { if( id->misc->stat_file_nest < 20 )
b2e9e12005-02-25Henrik Grubbström (Grubba)  tmp = (id->conf || this_object())->stat_file(id->not_query, id );
6906942004-05-21Henrik Grubbström (Grubba)  else error("Too deep recursion in roxen::stat_file() while mapping " +file+".\n"); };
b2e9e12005-02-25Henrik Grubbström (Grubba)  id->not_query = of;
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->stat_file_nest = 0; if(err) throw(err); TRACE_LEAVE(""); TRACE_LEAVE("Returning data"); return tmp; }
f128901997-08-15Henrik Grubbström (Grubba)  }
8fd51f1998-03-01Per Hedbor  TRACE_LEAVE("");
f128901997-08-15Henrik Grubbström (Grubba)  }
6906942004-05-21Henrik Grubbström (Grubba)  id->not_query = of;
f128901997-08-15Henrik Grubbström (Grubba) #endif
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  // Map location-modules.
6906942004-05-21Henrik Grubbström (Grubba)  foreach(location_module_cache||location_modules(), [string loc, function fun]) {
14179b1997-01-29Per Hedbor  if((file == loc) || ((file+"/")==loc))
41d0f91998-02-20Per Hedbor  {
6906942004-05-21Henrik Grubbström (Grubba)  TRACE_ENTER(sprintf("Location module [%s] ", loc), fun);
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Exact match.");
41d0f91998-02-20Per Hedbor  TRACE_LEAVE("");
6906942004-05-21Henrik Grubbström (Grubba)  return Stdio.Stat(({ 0775, -3, 0, 0, 0, 0, 0 }));
41d0f91998-02-20Per Hedbor  }
6906942004-05-21Henrik Grubbström (Grubba)  if(has_prefix(file, loc))
14179b1997-01-29Per Hedbor  {
6906942004-05-21Henrik Grubbström (Grubba)  TRACE_ENTER(sprintf("Location module [%s] ", loc), fun);
a78a591997-04-28Henrik Grubbström (Grubba) #ifdef MODULE_LEVEL_SECURITY
6906942004-05-21Henrik Grubbström (Grubba)  if(check_security(fun, id)) {
8fd51f1998-03-01Per Hedbor  TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Permission denied");
41d0f91998-02-20Per Hedbor  continue; }
a78a591997-04-28Henrik Grubbström (Grubba) #endif
6906942004-05-21Henrik Grubbström (Grubba)  if(s=function_object(fun)->stat_file(file[strlen(loc)..], id))
41d0f91998-02-20Per Hedbor  { TRACE_LEAVE("");
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Stat ok.");
14179b1997-01-29Per Hedbor  return s;
41d0f91998-02-20Per Hedbor  } TRACE_LEAVE("");
14179b1997-01-29Per Hedbor  } }
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned 'no such file'.");
14179b1997-01-29Per Hedbor }
73ce3d2000-12-11Per Hedbor mapping error_file( RequestID id ) {
b05f962010-09-28Martin Stjernholm  mapping res; // Avoid recursion in 404 messages. if (id->root_id->misc->generate_file_not_found || // The most popular 404 request ever? Skip the fancy error page. id->not_query == "/favicon.ico") {
39504b2008-09-04Jonas Wallden  res = Roxen.http_string_answer("No such file", "text/plain");
fca2732012-05-10Martin Stjernholm  res->error = 404;
39504b2008-09-04Jonas Wallden  } else {
b05f962010-09-28Martin Stjernholm  id->root_id->misc->generate_file_not_found = 1;
fca2732012-05-10Martin Stjernholm  string data = "<return code='404' />" + query("ZNoSuchFile");
51d4d22001-01-04Martin Nilsson #if ROXEN_COMPAT <= 2.1
39504b2008-09-04Jonas Wallden  data = replace(data,({"$File", "$Me"}), ({"&page.virtfile;", "&roxen.server;"}));
73ce3d2000-12-11Per Hedbor #endif
39504b2008-09-04Jonas Wallden  res = Roxen.http_rxml_answer( data, id, 0, "text/html" );
b05f962010-09-28Martin Stjernholm  id->root_id->misc->generate_file_not_found = 0;
39504b2008-09-04Jonas Wallden  } NOCACHE();
1371e02001-06-22Martin Nilsson  return res;
73ce3d2000-12-11Per Hedbor }
fafdea2005-12-13Anders Johansson mapping auth_failed_file( RequestID id, string message ) {
b05f962010-09-28Martin Stjernholm  // Avoid recursion in 401 messages. This could occur if the 401 // messages used files that also cause access denied. if(id->root_id->misc->generate_auth_failed)
3e61442006-11-07Marcus Wellhardh  return Roxen.http_low_answer(401, "<title>Access Denied</title>" "<h2 align=center>Access Denied</h2>");
b05f962010-09-28Martin Stjernholm  id->root_id->misc->generate_auth_failed = 1;
3e61442006-11-07Marcus Wellhardh 
fca2732012-05-10Martin Stjernholm  string data = "<return code='401' />" + query("ZAuthFailed");
fafdea2005-12-13Anders Johansson  NOCACHE(); mapping res = Roxen.http_rxml_answer( data, id, 0, "text/html" );
b05f962010-09-28Martin Stjernholm  id->root_id->misc->generate_auth_failed = 0;
fafdea2005-12-13Anders Johansson  return res; }
e8790b1998-02-19Per Hedbor // this is not as trivial as it sounds. Consider gtext. :-)
6877622004-04-19Martin Stjernholm array open_file(string fname, string mode, RequestID id, void|int internal_get, void|int recurse_count)
e8790b1998-02-19Per Hedbor {
6877622004-04-19Martin Stjernholm  mapping|int(0..1) file; string oq = id->not_query;
517c7e2000-09-30Per Hedbor  if( id->conf && (id->conf != this_object()) )
6877622004-04-19Martin Stjernholm  return id->conf->open_file( fname, mode, id, internal_get, recurse_count );
517c7e2000-09-30Per Hedbor 
6877622004-04-19Martin Stjernholm  if (recurse_count > 50) { TRACE_ENTER ("Looped " + recurse_count + " times in internal redirects - giving up", 0); TRACE_LEAVE (""); }
cd7ea41998-12-14Peter Bortas 
6877622004-04-19Martin Stjernholm  else { Configuration oc = id->conf; id->not_query = fname;
2c30832013-10-01Henrik Grubbström (Grubba)  // Make sure RXML defines don't survive <insert file/>. // Fixes [bug 6631] where the return code for the outer // RXML scope caused the <insert file/> to fail. m_delete(id->misc, "defines"); m_delete(id->misc, "error_code");
6877622004-04-19Martin Stjernholm  TRY_FIRST_MODULES (file, open_file (fname, mode, id, internal_get, recurse_count + 1)); fname = id->not_query;
c7a5f01999-02-16Per Hedbor 
6877622004-04-19Martin Stjernholm  if(search(mode, "R")!=-1) // raw (as in not parsed..)
e8790b1998-02-19Per Hedbor  {
6877622004-04-19Martin Stjernholm  string f; mode -= "R"; if(f = real_file(fname, id)) { // report_debug("opening "+fname+" in raw mode.\n"); return ({ open(f, mode), ([]) }); } // return ({ 0, (["error":302]) });
e8790b1998-02-19Per Hedbor  }
6877622004-04-19Martin Stjernholm  if(mode!="r") { id->not_query = oq; return ({ 0, (["error":501, "data":"Not implemented." ]) });
41d0f91998-02-20Per Hedbor  }
e8790b1998-02-19Per Hedbor  if(!file) {
517c7e2000-09-30Per Hedbor  file = get_file( id, 0, internal_get );
6877622004-04-19Martin Stjernholm  if(!file) TRY_LAST_MODULES (file, open_file (id->not_query, mode, id, internal_get, recurse_count + 1));
e8790b1998-02-19Per Hedbor  }
6877622004-04-19Martin Stjernholm  }
e8790b1998-02-19Per Hedbor 
6877622004-04-19Martin Stjernholm  if(!mappingp(file)) { if(id->misc->error_code) file = Roxen.http_low_answer(id->misc->error_code, "Failed" ); else if(id->method!="GET"&&id->method != "HEAD"&&id->method!="POST") file = Roxen.http_low_answer(501, "Not implemented."); else file = error_file( id );
e8790b1998-02-19Per Hedbor 
6877622004-04-19Martin Stjernholm  id->not_query = oq;
10c7e11999-12-28Martin Nilsson 
6877622004-04-19Martin Stjernholm  return ({ 0, file }); }
e8790b1998-02-19Per Hedbor 
6877622004-04-19Martin Stjernholm  if( file->data ) { file->file = StringFile(file->data); m_delete(file, "data");
e8790b1998-02-19Per Hedbor  } id->not_query = oq;
7db9292004-05-18Anders Johansson  return ({ file->file || StringFile(""), file });
e8790b1998-02-19Per Hedbor }
9a8a152000-09-25Per Hedbor mapping(string:array(mixed)) find_dir_stat(string file, RequestID id)
a86c6c1997-09-22Henrik Grubbström (Grubba) { string loc;
a476711997-10-20Henrik Grubbström (Grubba)  mapping(string:array(mixed)) dir = ([]); mixed d, tmp;
a86c6c1997-09-22Henrik Grubbström (Grubba) 
c7a5f01999-02-16Per Hedbor 
a86c6c1997-09-22Henrik Grubbström (Grubba)  file=replace(file, "//", "/");
10c7e11999-12-28Martin Nilsson 
db43d12000-11-24Martin Stjernholm  if(!sizeof (file) || file[0] != '/')
a86c6c1997-09-22Henrik Grubbström (Grubba)  file = "/" + file;
ae60b61998-05-23Henrik Grubbström (Grubba)  // FIXME: Should I append a "/" to file if missing?
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Request for directory and stat's \"%s\".", file), 0);
ae60b61998-05-23Henrik Grubbström (Grubba) 
a86c6c1997-09-22Henrik Grubbström (Grubba) #ifdef URL_MODULES #ifdef THREADS
3e3bab2001-01-19Per Hedbor  Thread.MutexKey key;
a86c6c1997-09-22Henrik Grubbström (Grubba) #endif // Map URL-modules
517c7e2000-09-30Per Hedbor  foreach(url_modules(), function funp)
a86c6c1997-09-22Henrik Grubbström (Grubba)  { string of = id->not_query; id->not_query = file; LOCK(funp);
434bac2000-07-14Andreas Lange  TRACE_ENTER("URL module", funp);
a86c6c1997-09-22Henrik Grubbström (Grubba)  tmp=funp( id, file ); UNLOCK(); if(mappingp( tmp )) { id->not_query=of;
3510fb1997-11-09Henrik Grubbström (Grubba) #ifdef MODULE_DEBUG
91d3c32001-03-12Martin Nilsson  report_debug("conf->find_dir_stat(\"%s\"): url_module returned mapping:%O\n", file, tmp);
3510fb1997-11-09Henrik Grubbström (Grubba) #endif /* MODULE_DEBUG */
64f3dc2004-08-18Martin Stjernholm  TRACE_LEAVE("Returned mapping.");
07014c1999-05-24Per Hedbor  TRACE_LEAVE("");
a86c6c1997-09-22Henrik Grubbström (Grubba)  return 0; } if(objectp( tmp )) {
a51a902001-11-12Martin Stjernholm  mixed err;
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->find_dir_stat_nest++;
10c7e11999-12-28Martin Nilsson 
a86c6c1997-09-22Henrik Grubbström (Grubba)  file = id->not_query; err = catch {
6906942004-05-21Henrik Grubbström (Grubba)  if( id->misc->find_dir_stat_nest < 20 )
a86c6c1997-09-22Henrik Grubbström (Grubba)  tmp = (id->conf || this_object())->find_dir_stat( file, id );
ae60b61998-05-23Henrik Grubbström (Grubba)  else {
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Too deep recursion");
a86c6c1997-09-22Henrik Grubbström (Grubba)  error("Too deep recursion in roxen::find_dir_stat() while mapping " +file+".\n");
ae60b61998-05-23Henrik Grubbström (Grubba)  }
a86c6c1997-09-22Henrik Grubbström (Grubba)  };
6906942004-05-21Henrik Grubbström (Grubba)  id->misc->find_dir_stat_nest = 0;
a86c6c1997-09-22Henrik Grubbström (Grubba)  if(err) throw(err);
3510fb1997-11-09Henrik Grubbström (Grubba) #ifdef MODULE_DEBUG
91d3c32001-03-12Martin Nilsson  report_debug("conf->find_dir_stat(\"%s\"): url_module returned object:\n", file);
3510fb1997-11-09Henrik Grubbström (Grubba) #endif /* MODULE_DEBUG */
434bac2000-07-14Andreas Lange  TRACE_LEAVE("Returned object."); TRACE_LEAVE("Returning it.");
ae60b61998-05-23Henrik Grubbström (Grubba)  return tmp; // FIXME: Return 0 instead?
a86c6c1997-09-22Henrik Grubbström (Grubba)  } id->not_query=of;
ae60b61998-05-23Henrik Grubbström (Grubba)  TRACE_LEAVE("");
a86c6c1997-09-22Henrik Grubbström (Grubba)  } #endif /* URL_MODULES */
517c7e2000-09-30Per Hedbor  foreach(location_modules(), tmp)
a86c6c1997-09-22Henrik Grubbström (Grubba)  { loc = tmp[0];
a476711997-10-20Henrik Grubbström (Grubba) 
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("Location module [%s] ", loc), 0);
a476711997-10-20Henrik Grubbström (Grubba)  /* Note that only new entries are added. */
a86c6c1997-09-22Henrik Grubbström (Grubba)  if(!search(file, loc)) {
a476711997-10-20Henrik Grubbström (Grubba)  /* file == loc + subpath */
a86c6c1997-09-22Henrik Grubbström (Grubba) #ifdef MODULE_LEVEL_SECURITY
8b44de2003-10-07Anders Johansson  if(check_security(tmp[1], id)) { TRACE_LEAVE("Security check failed."); continue; }
a86c6c1997-09-22Henrik Grubbström (Grubba) #endif
e351dd1999-11-29Per Hedbor  RoxenModule c = function_object(tmp[1]);
a86c6c1997-09-22Henrik Grubbström (Grubba)  string f = file[strlen(loc)..]; if (c->find_dir_stat) {
64f3dc2004-08-18Martin Stjernholm  SIMPLE_TRACE_ENTER(c, "Calling find_dir_stat().");
a86c6c1997-09-22Henrik Grubbström (Grubba)  if (d = c->find_dir_stat(f, id)) {
64f3dc2004-08-18Martin Stjernholm  SIMPLE_TRACE_LEAVE("Returned mapping with %d entries.", sizeof (d));
a476711997-10-20Henrik Grubbström (Grubba)  dir = d | dir;
a86c6c1997-09-22Henrik Grubbström (Grubba)  }
64f3dc2004-08-18Martin Stjernholm  else SIMPLE_TRACE_LEAVE("Returned zero."); } else { SIMPLE_TRACE_ENTER(c, "Calling find_dir()."); if(d = c->find_dir(f, id)) { SIMPLE_TRACE_LEAVE("Returned array with %d entries.", sizeof (d)); dir = mkmapping(d, Array.map(d, lambda(string fn) { return c->stat_file(f + fn, id); })) | dir; } else SIMPLE_TRACE_LEAVE("Returned zero.");
a86c6c1997-09-22Henrik Grubbström (Grubba)  }
a476711997-10-20Henrik Grubbström (Grubba)  } else if(search(loc, file)==0 && loc[strlen(file)-1]=='/' && (loc[0]==loc[-1]) && loc[-1]=='/' &&
8935ad1998-03-30Johan Schön  (function_object(tmp[1])->stat_file(".", id))) {
a476711997-10-20Henrik Grubbström (Grubba)  /* loc == file + "/" + subpath + "/"
8935ad1998-03-30Johan Schön  * and stat_file(".") returns non-zero.
a476711997-10-20Henrik Grubbström (Grubba)  */
434bac2000-07-14Andreas Lange  TRACE_ENTER(sprintf("The file %O is on the path to the mountpoint %O.",
67f60e2000-07-09Martin Nilsson  file, loc), 0);
a476711997-10-20Henrik Grubbström (Grubba)  loc=loc[strlen(file)..]; sscanf(loc, "%s/", loc); if (!dir[loc]) {
cd92872000-08-15Johan Sundström  dir[loc] = ({ 0775, -3, 0, 0, 0, 0, 0 });
a86c6c1997-09-22Henrik Grubbström (Grubba)  }
ae60b61998-05-23Henrik Grubbström (Grubba)  TRACE_LEAVE("");
a86c6c1997-09-22Henrik Grubbström (Grubba)  }
ae60b61998-05-23Henrik Grubbström (Grubba)  TRACE_LEAVE("");
a86c6c1997-09-22Henrik Grubbström (Grubba)  } if(sizeof(dir)) return dir; }
14179b1997-01-29Per Hedbor  // Access a virtual file?
9a8a152000-09-25Per Hedbor array access(string file, RequestID id)
14179b1997-01-29Per Hedbor { string loc; array s, tmp;
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  file=replace(file, "//", "/"); // "//" is really "/" here...
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  // Map location-modules.
517c7e2000-09-30Per Hedbor  foreach(location_modules(), tmp)
14179b1997-01-29Per Hedbor  { loc = tmp[0];
4770421999-11-22Henrik Grubbström (Grubba)  if((file+"/")==loc) { #ifdef MODULE_LEVEL_SECURITY if(check_security(tmp[1], id)) continue; #endif if(s=function_object(tmp[1])->access("", id)) return s; } else if(!search(file, loc)) {
14179b1997-01-29Per Hedbor #ifdef MODULE_LEVEL_SECURITY if(check_security(tmp[1], id)) continue; #endif if(s=function_object(tmp[1])->access(file[strlen(loc)..], id)) return s; } }
4770421999-11-22Henrik Grubbström (Grubba)  return 0;
14179b1997-01-29Per Hedbor }
9a8a152000-09-25Per Hedbor string real_file(string file, RequestID id)
9fd8092000-08-08Johan Sundström //! Return the _real_ filename of a virtual file, if any.
14179b1997-01-29Per Hedbor { string loc; string s; array tmp; file=replace(file, "//", "/"); // "//" is really "/" here...
10c7e11999-12-28Martin Nilsson 
14179b1997-01-29Per Hedbor  if(!id) error("No id passed to real_file"); // Map location-modules.
517c7e2000-09-30Per Hedbor  foreach(location_modules(), tmp)
14179b1997-01-29Per Hedbor  { loc = tmp[0];
10c7e11999-12-28Martin Nilsson  if(!search(file, loc))
14179b1997-01-29Per Hedbor  { #ifdef MODULE_LEVEL_SECURITY if(check_security(tmp[1], id)) continue; #endif
c5e0961999-10-04Per Hedbor  if(s=function_object(tmp[1])->real_file(file[strlen(loc)..], id))
14179b1997-01-29Per Hedbor  return s; } } }
7424772002-11-05Anders Johansson array(int)|Stat try_stat_file(string s, RequestID id, int|void not_internal) { RequestID fake_id; array(int)|Stat res; if(!objectp(id)) error("No ID passed to 'try_stat_file'\n"); // id->misc->common is here for compatibility; it's better to use // id->root_id->misc. if ( !id->misc ) id->misc = ([]);
1156ad2005-10-21Henrik Grubbström (Grubba)  fake_id = make_fake_id(s, id);
7424772002-11-05Anders Johansson  fake_id->misc->internal_get = !not_internal; fake_id->method = "GET"; res = stat_file(fake_id->not_query, fake_id); destruct (fake_id); return res; }
fc40392008-08-15Martin Stjernholm protected RequestID make_fake_id (string s, RequestID id)
14179b1997-01-29Per Hedbor {
e351dd1999-11-29Per Hedbor  RequestID fake_id;
14179b1997-01-29Per Hedbor 
9644732001-11-27Martin Stjernholm  // id->misc->common is here for compatibility; it's better to use // id->root_id->misc. if ( !id->misc->common ) id->misc->common = ([]);
e351dd1999-11-29Per Hedbor  fake_id = id->clone_me();
10c7e11999-12-28Martin Nilsson 
9644732001-11-27Martin Stjernholm  fake_id->misc->common = id->misc->common;
c8ff272002-09-20Anders Johansson  fake_id->conf = this_object();
e351dd1999-11-29Per Hedbor 
1ee9072007-12-21Henrik Grubbström (Grubba)  fake_id->raw_url=s;
4e0a532000-03-20Martin Stjernholm  if (fake_id->scan_for_query)
c65dbe2001-05-03Per Hedbor  // FIXME: If we're using e.g. ftp this doesn't exist. But the // right solution might be that clone_me() in an ftp id object // returns a vanilla (i.e. http) id instead when this function is // used.
4e0a532000-03-20Martin Stjernholm  s = fake_id->scan_for_query (s);
c65dbe2001-05-03Per Hedbor 
1156ad2005-10-21Henrik Grubbström (Grubba)  s = http_decode_string(s);
c65dbe2001-05-03Per Hedbor  s = Roxen.fix_relative (s, id);
1156ad2005-10-21Henrik Grubbström (Grubba)  // s is sent to Unix API's that take NUL-terminated strings... if (search(s, "\0") != -1) sscanf(s, "%s\0", s);
14179b1997-01-29Per Hedbor  fake_id->not_query=s;
829e8c2004-04-13Martin Stjernholm  return fake_id; } int|string try_get_file(string s, RequestID id, int|void stat_only, int|void nocache, int|void not_internal, mapping|void result_mapping) //! Convenience function used in quite a lot of modules. Tries to read //! a file into memory, and then returns the resulting string. //! //! NOTE: A 'file' can be a cgi script, which will be executed, //! resulting in a horrible delay. //! //! Unless the not_internal flag is set, this tries to get an external //! or internal file. Here "internal" means a file that never should be //! sent directly as a request response. E.g. an internal redirect to a //! different file is still considered "external" since its contents is //! sent directly to the client. Internal requests are recognized by //! the id->misc->internal_get flag being non-zero. { string res; RequestID fake_id = make_fake_id (s, id); mapping m; fake_id->misc->internal_get = !not_internal;
7a41502001-08-30Henrik Grubbström (Grubba)  fake_id->method = "GET";
14179b1997-01-29Per Hedbor 
3a4d492011-05-24Martin Stjernholm  array a = open_file( fake_id->not_query, "r", fake_id, !not_internal );
7db9292004-05-18Anders Johansson  m = a[1];
e31cc72009-04-28Henrik Grubbström (Grubba)  // Propagate vary callbacks from the subrequest. id->propagate_vary_callbacks(fake_id);
d60aa22005-04-12Anders Johansson  if (result_mapping) {
7db9292004-05-18Anders Johansson  foreach(indices(m), string i) result_mapping[i] = m[i];
d60aa22005-04-12Anders Johansson  if (string|function(string:string) charset = fake_id->get_output_charset())
b14c2c2010-12-01Martin Stjernholm  // Note that a "charset" field currently isn't supported very // much in a response mapping. In particular, the http protocol // module doesn't look at it. // // Maybe we should read in the result and decode it using this // charset instead, just like we do in the m->raw case below.
d60aa22005-04-12Anders Johansson  result_mapping->charset = charset;
f259722006-10-05Anders Johansson  result_mapping->last_modified = fake_id->misc->last_modified;
d60aa22005-04-12Anders Johansson  }
7db9292004-05-18Anders Johansson  if(a[0]) {
829e8c2004-04-13Martin Stjernholm  m->file = a[0]; } else { destruct (fake_id); return 0;
c927a02000-03-18Martin Stjernholm  }
a59fc92000-08-16Per Hedbor  CACHE( fake_id->misc->cacheable );
c927a02000-03-18Martin Stjernholm  destruct (fake_id);
889d031999-10-04Per Hedbor 
96d86e2001-01-29Per Hedbor  // Allow 2* and 3* error codes, not only a few specific ones. if (!(< 0,2,3 >)[m->error/100]) return 0;
10c7e11999-12-28Martin Nilsson 
829e8c2004-04-13Martin Stjernholm  if(stat_only) return 1;
3510fb1997-11-09Henrik Grubbström (Grubba) 
10c7e11999-12-28Martin Nilsson  if(m->data)
a22f6f1999-05-12Per Hedbor  res = m->data;
10c7e11999-12-28Martin Nilsson  else
a22f6f1999-05-12Per Hedbor  res="";
10c7e11999-12-28Martin Nilsson 
4cb2212000-05-22Per Hedbor  if( objectp(m->file) )
14179b1997-01-29Per Hedbor  {
3a4d7e1997-09-03Per Hedbor  res += m->file->read();
2c66592001-09-06Henrik Grubbström (Grubba)  if (m->file) { // Some wrappers may destruct themselves in read()... destruct(m->file); }
14179b1997-01-29Per Hedbor  }
10c7e11999-12-28Martin Nilsson 
bf74532010-12-02Martin Stjernholm  if(m->raw) { if (compat_level() > 5.0) res = Roxen.parse_http_response (res, result_mapping, 0, "from " + s); else { // This function used to simply toss the headers and return the // data as-is, losing the content type and charset. Need to be // bug compatible with the lack of charset handling, since // callers might be compensating. (The old code also deleted all // CR's in the whole body, regardless of content type(!), but we // don't have to be bug compatible with that at least.) sscanf (res, "%*s\r\n\r\n%s", res) || sscanf (res, "%*s\n\n%s", res) || sscanf (res, "%*s\r\n%s", res) || sscanf (res, "%*s\n%s", res); } }
14179b1997-01-29Per Hedbor  return res; }
829e8c2004-04-13Martin Stjernholm mapping(string:string) try_get_headers(string s, RequestID id, int|void not_internal) //! Like @[try_get_file] but performs a HEAD request and only returns //! the response headers. Note that the returned headers are as they //! would be in a formatted response by the http protocol module, //! which is completely different from a response mapping. { RequestID fake_id = make_fake_id (s, id); mapping m; fake_id->misc->internal_get = !not_internal; fake_id->method = "HEAD"; array a = open_file( s, "r", fake_id, !not_internal ); if(a && a[1]) { if (a[0]) a[0]->close(); m = a[1]; } else { destruct (fake_id); return 0; } CACHE( fake_id->misc->cacheable ); if (!m->raw) m = fake_id->make_response_headers (m); else { Roxen.HeaderParser hp = Roxen.HeaderParser(); array res; if(m->data) res = hp->feed (m->data); if (!res && objectp(m->file)) { hp->feed (m->file->read()); if (m->file) { // Some wrappers may destruct themselves in read()... destruct(m->file); } } m = res && res[2]; } destruct (fake_id); return m; }
f47b662004-03-16Henrik Grubbström (Grubba) mapping(string:mixed) try_put_file(string path, string data, RequestID id) { TIMER_START(try_put_file); // id->misc->common is here for compatibility; it's better to use // id->root_id->misc. if ( !id->misc ) id->misc = ([]);
1156ad2005-10-21Henrik Grubbström (Grubba)  RequestID fake_id = make_fake_id(path, id);
f47b662004-03-16Henrik Grubbström (Grubba)  fake_id->root_id->misc->_request_depth++; if(sub_req_limit && fake_id->root_id->misc->_request_depth > sub_req_limit) error("Subrequest limit reached. (Possibly an insertion loop.)"); fake_id->method = "PUT"; fake_id->data = data; fake_id->misc->len = sizeof(data); fake_id->misc->internal_get = 1; mapping(string:mixed) res = low_get_file(fake_id, 1); TIMER_END(try_put_file); return res; }
7345b02001-09-13Martin Nilsson int(0..1) is_file(string virt_path, RequestID id, int(0..1)|void internal) //! Is @[virt_path] a file in our virtual filesystem? If @[internal] is //! set, internal files is "visible" as well.
14179b1997-01-29Per Hedbor {
7345b02001-09-13Martin Nilsson  if(internal) { int(0..1) was_internal = id->misc->internal_get; id->misc->internal_get = 1; int(0..1) res = !!stat_file(virt_path, id); if(!was_internal) m_delete(id->misc, "internal_get"); return res; } if(stat_file(virt_path, id) || has_suffix(virt_path, "/internal-roxen-unit")) return 1; string f = (virt_path/"/")[-1]; if( sscanf(f, "internal-roxen-%s", f) ) { if(internal_roxen_image(f, id) || has_prefix(f, "pixel-")) return 1; return 0; } if( sscanf(f, "internal-gopher-%s", f) && internal_gopher_image(f) ) return 1; return 0;
14179b1997-01-29Per Hedbor }
03aa492000-08-23Per Hedbor array registered_urls = ({}), failed_urls = ({ });
7a243b2000-08-19Per Hedbor array do_not_log_patterns = 0;
b83ae22009-04-21Martin Stjernholm int start(int num)
14179b1997-01-29Per Hedbor {
c109fc2001-07-21Martin Stjernholm  fix_my_url();
55c0522001-11-07Henrik Grubbström (Grubba) #if 0 report_debug(sprintf("configuration:start():\n" " registered_urls: ({ %{%O, %}})\n"
9c3c6c2001-11-09Henrik Grubbström (Grubba)  " failed_urls: ({ %{%O, %}})\n"
55c0522001-11-07Henrik Grubbström (Grubba)  " URLs: ({ %{%O, %}})\n", registered_urls, failed_urls, query("URLs"))); #endif /* 0 */
c5e0961999-10-04Per Hedbor  // Note: This is run as root if roxen is started as root
5d6c8d2000-09-01Per Hedbor  foreach( (registered_urls-query("URLs"))+failed_urls, string url ) { registered_urls -= ({ url });
9c3c6c2001-11-09Henrik Grubbström (Grubba)  roxen.unregister_url(url, this_object());
5d6c8d2000-09-01Per Hedbor  }
ecd9ab2000-08-23Per Hedbor 
03aa492000-08-23Per Hedbor  failed_urls = ({ });
ecd9ab2000-08-23Per Hedbor 
5d6c8d2000-09-01Per Hedbor  foreach( (query( "URLs" )-registered_urls), string url )
55c0522001-11-07Henrik Grubbström (Grubba)  {
ecd9ab2000-08-23Per Hedbor  if( roxen.register_url( url, this_object() ) ) registered_urls += ({ url });
03aa492000-08-23Per Hedbor  else failed_urls += ({ url });
55c0522001-11-07Henrik Grubbström (Grubba)  }
5f6dae2000-08-13Per Hedbor  if( !datacache ) datacache = DataCache( ); else datacache->init_from_variables();
7a243b2000-08-19Per Hedbor  parse_log_formats(); init_log_file(); do_not_log_patterns = query("NoLog"); if(!sizeof(do_not_log_patterns)) do_not_log_patterns = 0;
6f669a2001-08-14Honza Petrous 
b9c3872002-03-27Per Hedbor  if( query("throttle") ) { if( !throttler ) throttler=.throttler(); throttler->throttle(query("throttle_fill_rate"), query("throttle_bucket_depth"), query("throttle_min_grant"), query("throttle_max_grant")); } else if( throttler ) { // This is done to give old connections more bandwidth. throttler->throttle( 1000000000, 1000000000, // 800Mbit. 1024, 65536 ); // and new connections does not even have to care. throttler = 0; }
6f669a2001-08-14Honza Petrous #ifdef SNMP_AGENT if(query("snmp_process") && objectp(roxen->snmpagent)) roxen->snmpagent->add_virtserv(get_config_id()); #endif
7adb102007-11-05Henrik Grubbström (Grubba)  foreach(registered_urls, string url) { mapping(string:string|Configuration|Protocol) port_info = roxen.urls[url]; foreach((port_info && port_info->ports) || ({}), Protocol prot) { if ((prot->prot_name != "snmp") || (!prot->mib)) { continue; } string path = port_info->path || ""; if (has_prefix(path, "/")) { path = path[1..]; } if (has_suffix(path, "/")) { path = path[..sizeof(path)-2]; } array(int) oid_suffix = ({ sizeof(path), @((array(int))path) }); ADT.Trie mib = SNMP.SimpleMIB(query_oid(), oid_suffix, ({ UNDEFINED, UNDEFINED, SNMP.String(query_name, "siteName"), SNMP.String(comment, "siteComment"), SNMP.Counter64(lambda() { return sent; }, "sent"), SNMP.Counter64(lambda() { return received; }, "received"), SNMP.Counter64(lambda() { return hsent; }, "sentHeaders"), SNMP.Counter64(lambda() { return requests; }, "numRequests"), UNDEFINED, // NOTE: Reserved for modules!
9e03692010-05-19Fredrik Noring  ({ UNDEFINED, ({ UNDEFINED, ({ UNDEFINED, SNMP.Counter(lambda() { return request_acc_time/10000; }, "requestTime", "Accumulated total request time " "in centiseconds."), }), ({ UNDEFINED, SNMP.Counter(lambda() { return requests; }, "requestNumRuns", "Total number of request runs."), SNMP.Counter(lambda() { return request_num_runs_001s; }, "requestNumRuns001s", "Number of request runs longer than 0.01 seconds."), SNMP.Counter(lambda() { return request_num_runs_005s; }, "requestNumRuns005s", "Number of request runs longer than 0.05 seconds."), SNMP.Counter(lambda() { return request_num_runs_015s; }, "requestNumRuns015s", "Number of request runs longer than 0.15 seconds."), SNMP.Counter(lambda() { return request_num_runs_05s; }, "requestNumRuns05s", "Number of request runs longer than 0.5 seconds."), SNMP.Counter(lambda() { return request_num_runs_1s; }, "requestNumRuns1s", "Number of request runs longer than 1 second."), SNMP.Counter(lambda() { return request_num_runs_5s; }, "requestNumRuns5s", "Number of request runs longer than 5 seconds."), SNMP.Counter(lambda() { return request_num_runs_15s; }, "requestNumRuns15s", "Number of request runs longer than 15 seconds."), }), }), ({ UNDEFINED, ({ UNDEFINED, SNMP.Counter(lambda() { return handle_acc_time/10000; }, "handleTime", "Accumulated total handle time " "in centiseconds."), }), ({ UNDEFINED, SNMP.Counter(lambda() { return requests; }, "handleNumRuns", "Total number of handle runs."), SNMP.Counter(lambda() { return handle_num_runs_001s; }, "handleNumRuns001s", "Number of handle runs longer than 0.01 seconds."), SNMP.Counter(lambda() { return handle_num_runs_005s; }, "handleNumRuns005s", "Number of handle runs longer than 0.05 seconds."), SNMP.Counter(lambda() { return handle_num_runs_015s; }, "handleNumRuns015s", "Number of handle runs longer than 0.15 seconds."), SNMP.Counter(lambda() { return handle_num_runs_05s; }, "handleNumRuns05s", "Number of handle runs longer than 0.5 seconds."), SNMP.Counter(lambda() { return handle_num_runs_1s; }, "handleNumRuns1s", "Number of handle runs longer than 1 second."), SNMP.Counter(lambda() { return handle_num_runs_5s; }, "handleNumRuns5s", "Number of handle runs longer than 5 seconds."), SNMP.Counter(lambda() { return handle_num_runs_15s; }, "handleNumRuns15s", "Number of handle runs longer than 15 seconds."), }), }), ({ UNDEFINED, ({ UNDEFINED, SNMP.Counter(lambda() { return queue_acc_time/10000; }, "queueTime", "Accumulated total queue time " "in centiseconds."), }), ({ UNDEFINED, SNMP.Counter(lambda() { return requests; }, "queueNumRuns", "Total number of queue runs."), SNMP.Counter(lambda() { return queue_num_runs_001s; }, "queueNumRuns001s", "Number of queue runs longer than 0.01 seconds."), SNMP.Counter(lambda() { return queue_num_runs_005s; }, "queueNumRuns005s", "Number of queue runs longer than 0.05 seconds."), SNMP.Counter(lambda() { return queue_num_runs_015s; }, "queueNumRuns015s", "Number of queue runs longer than 0.15 seconds."), SNMP.Counter(lambda() { return queue_num_runs_05s; }, "queueNumRuns05s", "Number of queue runs longer than 0.5 seconds."), SNMP.Counter(lambda() { return queue_num_runs_1s; }, "queueNumRuns1s", "Number of queue runs longer than 1 second."), SNMP.Counter(lambda() { return queue_num_runs_5s; }, "queueNumRuns5s", "Number of queue runs longer than 5 seconds."), SNMP.Counter(lambda() { return queue_num_runs_15s; }, "queueNumRuns15s", "Number of queue runs longer than 15 seconds."), }), })
c7e40e2011-02-18Marcus Wellhardh  }), ({ UNDEFINED, SNMP.Counter(lambda() { return datacache->hits + datacache->misses; }, "protCacheLookups", "Number of protocol cache lookups."), SNMP.Counter(lambda() { return datacache->hits; }, "protCacheHits", "Number of protocol cache hits."), SNMP.Counter(lambda() { return datacache->misses; }, "protCacheMisses", "Number of protocol cache misses."), SNMP.Gauge(lambda() { return sizeof(datacache->cache); }, "protCacheEntries", "Number of protocol cache entries."), SNMP.Gauge(lambda() { return datacache->max_size/1024; }, "protCacheMaxSize", "Maximum size of protocol cache in KiB."), SNMP.Gauge(lambda() { return datacache->current_size/1024; }, "protCacheCurrSize", "Current size of protocol cache in KiB."),
9e03692010-05-19Fredrik Noring  })
7adb102007-11-05Henrik Grubbström (Grubba)  })); SNMP.set_owner(mib, this_object()); prot->mib->merge(mib); } }
b83ae22009-04-21Martin Stjernholm  if (retrieve ("EnabledModules", this)["config_filesystem#0"]) return 1; // Signal that this is the admin UI config. return 0;
14179b1997-01-29Per Hedbor }
4586f52009-10-31Martin Stjernholm // ([func: ([mod_name: ({cb, cb, ...})])]) protected mapping(string: mapping(string: array(function(RoxenModule,mixed...:void)))) module_pre_callbacks = ([]), module_post_callbacks = ([]); void add_module_pre_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb) { ASSERT_IF_DEBUG ((<"start", "stop">)[func]); mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_pre_callbacks[func] || (module_pre_callbacks[func] = ([])); if (func_cbs[mod_name] && has_value (func_cbs[mod_name], cb)) return; func_cbs[mod_name] += ({cb}); } void delete_module_pre_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb) { if (mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_pre_callbacks[func]) if (func_cbs[mod_name]) func_cbs[mod_name] -= ({cb}); } void add_module_post_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb) { ASSERT_IF_DEBUG ((<"start", "stop">)[func]); mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_post_callbacks[func] || (module_post_callbacks[func] = ([])); if (func_cbs[mod_name] && has_value (func_cbs[mod_name], cb)) return; func_cbs[mod_name] += ({cb}); } void delete_module_post_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb) { if (mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_post_callbacks[func]) if (func_cbs[mod_name]) func_cbs[mod_name] -= ({cb}); } void call_module_func_with_cbs (RoxenModule mod, string func, mixed... args) { string mod_name; if (mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_pre_callbacks[func]) { sscanf (mod->module_local_id(), "%[^#]", mod_name); array(function(RoxenModule,mixed...:void)) cbs; if (array(function(RoxenModule,mixed...:void)) a = func_cbs[mod_name]) { func_cbs[mod_name] = (a -= ({0})); cbs = a; } if (array(function(RoxenModule,mixed...:void)) a = func_cbs[0]) { func_cbs[0] = (a -= ({0})); if (cbs) cbs += a; else cbs = a; } if (cbs) { foreach (cbs, function(RoxenModule,mixed...:void) cb) { #ifdef MODULE_CB_DEBUG werror ("Calling callback before %O->%s: %O\n", mod, func, cb); #endif if (mixed err = catch (cb (mod, @args))) report_error ("Error calling callback %O before %O->%s:\n%s\n", cb, mod, func, describe_backtrace (err)); } } } // Exceptions thrown here are the responsibility of the caller. #ifdef MODULE_CB_DEBUG werror ("Calling %O->%s (%s)\n", mod, func, map (args, lambda (mixed arg) {return sprintf ("%O", arg);}) * ", "); #endif mod[func] (@args); if (mapping(string:array(function(RoxenModule,mixed...:void))) func_cbs = module_post_callbacks[func]) { if (!mod_name)
550cf82011-03-15Henrik Grubbström (Grubba)  sscanf (otomod[mod] || mod->module_local_id(), "%[^#]", mod_name);
4586f52009-10-31Martin Stjernholm  array(function(RoxenModule,mixed...:void)) cbs; if (array(function(RoxenModule,mixed...:void)) a = func_cbs[mod_name]) { func_cbs[mod_name] = (a -= ({0})); cbs = a; } if (array(function(RoxenModule,mixed...:void)) a = func_cbs[0]) { func_cbs[0] = (a -= ({0})); if (cbs) cbs += a; else cbs = a; } if (cbs) { foreach (cbs, function(RoxenModule,mixed...:void) cb) { #ifdef MODULE_CB_DEBUG werror ("Calling callback after %O->%s: %O\n", mod, func, cb); #endif if (mixed err = catch (cb (mod, @args))) report_error ("Error calling callback %O after %O->%s:\n%s\n", cb, mod, func, describe_backtrace (err)); } } } }
e7e6031999-11-05Per Hedbor void save_me() { save_one( 0 ); }
14179b1997-01-29Per Hedbor void save(int|void all)
2688e22000-07-26Johan Sundström //! Save this configuration. If all is included, save all configuration //! global variables as well, otherwise only all module variables.
14179b1997-01-29Per Hedbor { if(all) {
3f628a1999-12-09Martin Stjernholm  store("spider#0", variables, 0, this_object());
14179b1997-01-29Per Hedbor  start(2); }
10c7e11999-12-28Martin Nilsson 
a2d0792002-04-15Marcus Wellhardh  store( "EnabledModules", enabled_modules, 1, this_object());
3f628a1999-12-09Martin Stjernholm  foreach(indices(modules), string modname)
14179b1997-01-29Per Hedbor  {
3f628a1999-12-09Martin Stjernholm  foreach(indices(modules[modname]->copies), int i)
14179b1997-01-29Per Hedbor  {
9530d52011-06-19Martin Stjernholm  RoxenModule mod = modules[modname]->copies[i]; store(modname+"#"+i, mod->query(), 0, this); if (mixed err = mod->start && catch { call_module_func_with_cbs (mod, "start", 2, this, 0); })
49e3bc2001-03-12Anders Johansson  report_error("Error calling start in module.\n%s", describe_backtrace (err));
14179b1997-01-29Per Hedbor  } }
8cc31b1997-10-12Henrik Grubbström (Grubba)  invalidate_cache();
14179b1997-01-29Per Hedbor }
e351dd1999-11-29Per Hedbor int save_one( RoxenModule o )
2688e22000-07-26Johan Sundström //! Save all variables in a given module.
14179b1997-01-29Per Hedbor {
10c7e11999-12-28Martin Nilsson  if(!o)
14179b1997-01-29Per Hedbor  {
c5e0961999-10-04Per Hedbor  store("spider#0", variables, 0, this_object());
14179b1997-01-29Per Hedbor  start(2); return 1; }
2c13a81999-11-10Per Hedbor  string q = otomod[ o ]; if( !q ) error("Invalid module");
10c7e11999-12-28Martin Nilsson 
2c13a81999-11-10Per Hedbor  store(q, o->query(), 0, this_object()); invalidate_cache();
0d1e432000-11-02Per Hedbor  mixed error;
4586f52009-10-31Martin Stjernholm  if( o->start && (error = catch( call_module_func_with_cbs (o, "start", 2, this, 0) )) )
0d1e432000-11-02Per Hedbor  { if( objectp(error ) ) error = (array)error; if( sizeof(error)>1 && arrayp( error[1] ) ) { int i; for( i = 0; i<sizeof( error[1] ); i++ ) if( error[1][i][2] == save_one ) break; error[1] = error[1][i+1..]; } if( o->report_error ) o->report_error( "Call to start failed.\n"+describe_backtrace( error ) ); else report_error( "Call to start failed.\n"+describe_backtrace( error )); }
2c13a81999-11-10Per Hedbor  invalidate_cache(); return 1;
14179b1997-01-29Per Hedbor }
2f79202000-03-27Per Hedbor RoxenModule reload_module( string modname )
5964251999-11-19Per Hedbor {
e351dd1999-11-29Per Hedbor  RoxenModule old_module = find_module( modname );
2d4a562009-11-03Martin Stjernholm  sscanf (modname, "%s#%d", string base_modname, int mod_copy); ModuleInfo mi = roxen.find_module( base_modname );
ffdab52001-08-24Per Hedbor 
2f79202000-03-27Per Hedbor  if( !old_module ) return 0;
f569581999-11-23Per Hedbor 
b5e4a62007-05-31Martin Stjernholm  // Temporarily shift out of the rxml parsing context if we're inside // any (e.g. due to delayed loading from inside the admin // interface). RXML.Context old_ctx = RXML.get_context(); RXML.set_context (0); mixed err = catch {
1f56521999-11-23Per Hedbor 
b5e4a62007-05-31Martin Stjernholm  master()->clear_compilation_failures();
2f79202000-03-27Per Hedbor 
b5e4a62007-05-31Martin Stjernholm  if( !old_module->not_a_module ) { save_one( old_module ); master()->refresh_inherit( object_program( old_module ) ); master()->refresh( object_program( old_module ), 1 ); }
2141a12001-07-12Martin Stjernholm 
b5e4a62007-05-31Martin Stjernholm  array old_error_log = (array) old_module->error_log;
2141a12001-07-12Martin Stjernholm 
b5e4a62007-05-31Martin Stjernholm  RoxenModule nm;
ffdab52001-08-24Per Hedbor 
b5e4a62007-05-31Martin Stjernholm  // Load up a new instance.
2d4a562009-11-03Martin Stjernholm  nm = mi->instance( this_object(), 0, mod_copy);
b5e4a62007-05-31Martin Stjernholm  // If this is a faked module, let's call it a failure.
c789492010-11-17Martin Stjernholm  if (nm->module_is_disabled)
6a32272011-12-21Jonas Wallden  report_notice (LOC_C(1047, "Module is disabled") + "\n");
c789492010-11-17Martin Stjernholm  else if( nm->not_a_module )
b5e4a62007-05-31Martin Stjernholm  { old_module->report_error(LOC_C(385,"Reload failed")+"\n"); RXML.set_context (old_ctx); return old_module; }
ffdab52001-08-24Per Hedbor 
9ed1442010-05-07Martin Stjernholm  disable_module( modname, nm );
b5e4a62007-05-31Martin Stjernholm  destruct( old_module );
2141a12001-07-12Martin Stjernholm 
b5e4a62007-05-31Martin Stjernholm  mixed err = catch { mi->update_with( nm,0 ); // This is sort of nessesary... }; if (err) if (stringp (err)) { // Error from the register_module call. We can't enable the old // module now, and I don't dare changing the order so that // register_module starts to get called before the old module is // destructed. /mast report_error (err); report_error(LOC_C(385,"Reload failed")+"\n"); RXML.set_context (old_ctx); return 0; // Use a placeholder module instead? } else throw (err); enable_module( modname, nm, mi ); foreach (old_error_log, [string msg, array(int) times]) nm->error_log[msg] += times;
2141a12001-07-12Martin Stjernholm 
b5e4a62007-05-31Martin Stjernholm  nm->report_notice(LOC_C(11, "Reloaded %s.")+"\n", mi->get_name()); RXML.set_context (old_ctx); return nm; }; RXML.set_context (old_ctx);
1a517c2007-05-31Martin Stjernholm  throw (err);
5964251999-11-19Per Hedbor }
ec170b2008-11-19Martin Stjernholm void reload_all_modules() { if (!inited) enable_all_modules(); else { foreach (enabled_modules; string modname;) reload_module (modname); } }
beba572000-03-20Martin Stjernholm #ifdef THREADS Thread.Mutex enable_modules_mutex = Thread.Mutex();
c6bf402001-09-10Martin Stjernholm #define MODULE_LOCK(TYPE) \ Thread.MutexKey enable_modules_lock = enable_modules_mutex->lock (TYPE)
beba572000-03-20Martin Stjernholm #else
c6bf402001-09-10Martin Stjernholm #define MODULE_LOCK(TYPE)
beba572000-03-20Martin Stjernholm #endif
fc40392008-08-15Martin Stjernholm protected int enable_module_batch_msgs;
9a1d472000-01-12Martin Stjernholm 
2f79202000-03-27Per Hedbor RoxenModule enable_module( string modname, RoxenModule|void me,
bcf5662000-04-05Per Hedbor  ModuleInfo|void moduleinfo,
e6cd832002-04-09Marcus Wellhardh  int|void nostart, int|void nosave )
14179b1997-01-29Per Hedbor {
c6bf402001-09-10Martin Stjernholm  MODULE_LOCK (2);
c5e0961999-10-04Per Hedbor  int id;
e351dd1999-11-29Per Hedbor  ModuleCopies module;
c5e0961999-10-04Per Hedbor  mixed err; int module_type;
acff932001-08-14Per Hedbor  if( forcibly_added[modname] == 2 )
747a7c2001-08-13Per Hedbor  return search(otomod, modname);
4867e92000-09-05Per Hedbor  if( datacache ) datacache->flush();
8b16fe2000-09-05Per Hedbor 
e7e6031999-11-05Per Hedbor  if( sscanf(modname, "%s#%d", modname, id ) != 2 )
2f79202000-03-27Per Hedbor  while( modules[ modname ] && modules[ modname ][ id ] )
4f43eb1999-11-05Per Hedbor  id++;
c5e0961999-10-04Per Hedbor 
0e7d592009-11-01Martin Stjernholm #ifdef DEBUG if (mixed init_info = roxen->bootstrap_info->get()) if (arrayp (init_info)) error ("Invalid recursive call to enable_module while enabling %O/%s.\n", init_info[0], init_info[1]); #endif
a42fe42001-07-05Martin Nilsson #ifdef MODULE_DEBUG
7f00081998-03-20Per Hedbor  int start_time = gethrtime();
a42fe42001-07-05Martin Nilsson #endif
25171c1999-11-06Per Hedbor 
2f79202000-03-27Per Hedbor  if( !moduleinfo )
ad683e1998-05-09Henrik Grubbström (Grubba)  {
2e8d0f2001-06-28Martin Stjernholm  moduleinfo = roxen->find_module( modname );
2f79202000-03-27Per Hedbor  if (!moduleinfo) { report_warning("Failed to load %s. The module probably " "doesn't exist in the module path.\n", modname);
8b700e2000-09-19Martin Stjernholm  got_no_delayed_load = -1;
2f79202000-03-27Per Hedbor  return 0; }
c5e0961999-10-04Per Hedbor  }
9a1d472000-01-12Martin Stjernholm  string descr = moduleinfo->get_name() + (id ? " copy " + (id + 1) : "");
835c6c2001-06-17Martin Nilsson  // sscanf(descr, "%*s: %s", descr);
9a1d472000-01-12Martin Stjernholm 
3bbda81997-06-11Henrik Grubbström (Grubba) #ifdef MODULE_DEBUG
9a1d472000-01-12Martin Stjernholm  if (enable_module_batch_msgs) report_debug(" %-43s... \b", descr );
c5e0961999-10-04Per Hedbor  else
9a1d472000-01-12Martin Stjernholm  report_debug("Enabling " + descr + "\n");
3bbda81997-06-11Henrik Grubbström (Grubba) #endif
c5e0961999-10-04Per Hedbor 
0b7d2b1999-12-22Per Hedbor  module = modules[ modname ];
c5e0961999-10-04Per Hedbor  if(!module)
7e446c1999-11-24Per Hedbor  modules[ modname ] = module = ModuleCopies();
c5e0961999-10-04Per Hedbor 
f569581999-11-23Per Hedbor  if( !me )
c5e0961999-10-04Per Hedbor  {
2d4a562009-11-03Martin Stjernholm  if(err = catch(me = moduleinfo->instance(this_object(), 0, id)))
f569581999-11-23Per Hedbor  {
c45b2a1999-12-09Martin Stjernholm #ifdef MODULE_DEBUG
9a1d472000-01-12Martin Stjernholm  if (enable_module_batch_msgs) report_debug("\bERROR\n");
67f60e2000-07-09Martin Nilsson  if (err != "") { #endif string bt=describe_backtrace(err);
434bac2000-07-14Andreas Lange  report_error("enable_module(): " +
49cd282000-08-11Andreas Lange  LOC_M(41, "Error while initiating module copy of %s%s"),
434bac2000-07-14Andreas Lange  moduleinfo->get_name(), (bt ? ":\n"+bt : "\n"));
67f60e2000-07-09Martin Nilsson #ifdef MODULE_DEBUG }
c45b2a1999-12-09Martin Stjernholm #endif
f63aca2000-09-08Martin Stjernholm  got_no_delayed_load = -1;
0b7d2b1999-12-22Per Hedbor  return module[id];
f569581999-11-23Per Hedbor  }
c5e0961999-10-04Per Hedbor  }
0b7d2b1999-12-22Per Hedbor  if(module[id] && module[id] != me)
c5e0961999-10-04Per Hedbor  {
9ed1442010-05-07Martin Stjernholm  // Don't know when this happens, because reload_module has already // called disable_module on the old instance.
0e1f262002-01-29Martin Stjernholm  if( module[id]->stop ) {
9ed1442010-05-07Martin Stjernholm  if (err = catch( call_module_func_with_cbs (module[id], "stop", me) )) {
0e1f262002-01-29Martin Stjernholm  string bt=describe_backtrace(err); report_error("disable_module(): " + LOC_M(44, "Error while disabling module %s%s"), descr, (bt ? ":\n"+bt : "\n")); } }
ad683e1998-05-09Henrik Grubbström (Grubba)  }
14179b1997-01-29Per Hedbor 
c5e0961999-10-04Per Hedbor  me->set_configuration( this_object() ); module_type = moduleinfo->type;
1d5a022010-03-30Stephen R. van den Berg  if (module_type & MODULE_TYPE_MASK)
ad683e1998-05-09Henrik Grubbström (Grubba)  {
1d5a022010-03-30Stephen R. van den Berg  if(!(module_type & MODULE_CONFIG))
3b17831998-11-22Per Hedbor  {
4770421999-11-22Henrik Grubbström (Grubba)  if (err = catch {
23414a2000-07-21Andreas Lange  me->defvar("_priority", 5, DLOCALE(12, "Priority"), TYPE_INT_LIST, DLOCALE(13, "The priority of the module. 9 is highest and 0 is lowest."
4770421999-11-22Henrik Grubbström (Grubba)  " Modules with the same priority can be assumed to be "
ae57c12005-01-12Martin Stjernholm  "called in random order."),
4770421999-11-22Henrik Grubbström (Grubba)  ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); }) { throw(err); }
3b17831998-11-22Per Hedbor  }
10c7e11999-12-28Martin Nilsson 
21306c2000-11-02Per Hedbor #ifdef MODULE_LEVEL_SECURITY
b7b6532002-06-06Anders Johansson  if( (module_type & ~(MODULE_LOGGER|MODULE_PROVIDER|MODULE_USERDB)) != 0 )
ad683e1998-05-09Henrik Grubbström (Grubba)  {
bc0fa02001-03-08Per Hedbor // me->defvar("_sec_group", "user", DLOCALE(14, "Security: Realm"), // TYPE_STRING, // DLOCALE(15, "The realm to use when requesting password from the " // "client. Usually used as an informative message to the " // "user."));
23414a2000-07-21Andreas Lange  me->defvar("_seclevels", "", DLOCALE(16, "Security: Patterns"), TYPE_TEXT_FIELD,
89d1d82001-05-16Martin Nilsson  DLOCALE(245,
62e2cb2001-03-08Per Hedbor  "The syntax is:\n" " \n<dl>" " <dt><b>userdb</b> <i>userdatabase module</i></dt>\n" " <dd> Select a non-default userdatabase module. The default is to " " search all modules. The userdatabase module config_userdb is always " " present, and contains the configuration users</dd>\n" "<dt><b>authmethod</b> <i>authentication module</i></dt>\n" "<dd>Select a non-default authentication method.</dd>" "<dt><b>realm</b> <i>realm name</i></dt>\n" "<dd>The realm is used when user authentication info is requested</dd>" "</dl>\n" " Below, CMD is one of 'allow' and 'deny'\n" " <dl>\n" " <dt>CMD <b>ip</b>=<i>ip/bits</i> [return]<br />\n" " CMD <b>ip</b>=<i>ip:mask</i> [return] <br />\n" " CMD <b>ip</b>=<i>pattern[,pattern,...]</i> [return] <br /></dt>\n" " <dd>Match the remote IP-address.</dd>\n" " \n" " <dt>CMD <b>user</b>=<i>name[,name,...]</i> [return]</dt>\n"
209a322010-03-30Stephen R. van den Berg  " <dd>Requires an authenticated user. If the user name 'any' is used, any "
06f5ba2010-03-30Stephen R. van den Berg  "valid user will be OK; if the user name 'ANY' is used, " "a valid user is preferred, but not required. " "Otherwise, one of the listed users is required.</dd>"
62e2cb2001-03-08Per Hedbor  " <dt>CMD <b>group</b>=<i>name[,name,...]</i> [return]</dt>\n"
209a322010-03-30Stephen R. van den Berg  "<dd>Requires an authenticated user with a group. If the group name "
62e2cb2001-03-08Per Hedbor  " 'any' is used, any valid group will be OK. Otherwise, one of the " "listed groups are required.</dd>\n" " \n" "<dt>CMD <b>dns</b>=<i>pattern[,pattern,...]</i> [return]</dt>\n" "<dd>Require one of the specified DNS domain-names</dd>" " \n" "<dt>CMD <b>time</b>=<i>HH:mm-HH:mm</i> [return]</dt>\n" "<dd>Only allow access to the module from the first time to the " " second each day. Both times should be specified in 24-hour " " HH:mm format.</dd>\n" "<dt>CMD <b>day</b>=<i>day[,day,...]</i> [return]</dt>\n" "<dd>Only allow access during certain days. Day is either a numerical "
d5a37b2004-02-20Martin Stjernholm  " value (Monday=1, Sunday=7) or a string (monday, tuesday etc)</dd>"
62e2cb2001-03-08Per Hedbor  "</dl><p>\n" " pattern is always a glob pattern (* = any characters, ? = any character).\n" "</p><p>\n" " return means that reaching this command results in immediate\n" " return, only useful for 'allow'.</p>\n" " \n" " <p>'deny' always implies a return, no futher testing is done if a\n" " 'deny' match.</p>\n"));
23414a2000-07-21Andreas Lange 
c5e0961999-10-04Per Hedbor  if(!(module_type & MODULE_PROXY))
14179b1997-01-29Per Hedbor  {
23414a2000-07-21Andreas Lange  me->defvar("_seclvl", 0, DLOCALE(18, "Security: Security level"), TYPE_INT,
55a8662000-11-20Per Hedbor  DLOCALE(305, "The modules security level is used to determine if a "
3bf3451998-10-01Peter Bortas  " request should be handled by the module." "\n<p><h2>Security level vs Trust level</h2>" " Each module has a configurable <i>security level</i>." " Each request has an assigned trust level. Higher" " <i>trust levels</i> grants access to modules with higher" " <i>security levels</i>." "\n<p><h2>Definitions</h2><ul>"
6a32272011-12-21Jonas Wallden  " <li>A requests initial trust level is infinitely high.</li>"
3bf3451998-10-01Peter Bortas  " <li> A request will only be handled by a module if its" " <i>trust level</i> is higher or equal to the"
f498ed2000-07-11Martin Nilsson  " <i>security level</i> of the module.</li>"
3bf3451998-10-01Peter Bortas  " <li> Each time the request is handled by a module the"
6a32272011-12-21Jonas Wallden  " <i>trust level</i> of the request will be set to the"
3bf3451998-10-01Peter Bortas  " lower of its <i>trust level</i> and the modules"
90ecbb2000-11-15Per Hedbor  " <i>security level</i>, <i>unless</i> the security " " level of the module is 0, which is a special " " case and means that no change should be made.</li>"
f498ed2000-07-11Martin Nilsson  " </ul></p>"
3bf3451998-10-01Peter Bortas  "\n<p><h2>Example</h2>" " Modules:<ul>"
f498ed2000-07-11Martin Nilsson  " <li> User filesystem, <i>security level</i> 1</li>" " <li> Filesystem module, <i>security level</i> 3</li>" " <li> CGI module, <i>security level</i> 2</li>" " </ul></p>"
3bf3451998-10-01Peter Bortas  "\n<p>A request handled by \"User filesystem\" is assigned" " a <i>trust level</i> of one after the <i>security" " level</i> of that module. That request can then not be" " handled by the \"CGI module\" since that module has a" " higher <i>security level</i> than the requests trust"
f498ed2000-07-11Martin Nilsson  " level.</p>"
3bf3451998-10-01Peter Bortas  "\n<p>On the other hand, a request handled by the the"
6a32272011-12-21Jonas Wallden  " \"Filesystem module\" could later be handled by the"
f498ed2000-07-11Martin Nilsson  " \"CGI module\".</p>"));
ad683e1998-05-09Henrik Grubbström (Grubba)  } else { me->definvisvar("_seclvl", -10, TYPE_INT); /* A very low one */
14179b1997-01-29Per Hedbor  }
3bbda81997-06-11Henrik Grubbström (Grubba)  }
21306c2000-11-02Per Hedbor #endif
ad683e1998-05-09Henrik Grubbström (Grubba)  } else { me->defvar("_priority", 0, "", TYPE_INT, "", 0, 1); }
14179b1997-01-29Per Hedbor 
de26832008-03-17Henrik Grubbström (Grubba)  if (!module[id]) counters[moduleinfo->counter]++;
0b7d2b1999-12-22Per Hedbor  module[ id ] = me;
37c1b31999-10-12Per Hedbor  otomod[ me ] = modname+"#"+id;
ed47402010-06-06Martin Stjernholm  module_set_counter++;
10c7e11999-12-28Martin Nilsson 
dcdea72009-11-01Martin Stjernholm  // Below we may have recursive calls to this function. They may // occur already in setvars due to e.g. automatic dependencies in // Variable.ModuleChoice. mapping(string:mixed) stored_vars = retrieve(modname + "#" + id, this_object()); int has_stored_vars = sizeof (stored_vars); // A little ugly, but it suffices. me->setvars(stored_vars);
550cf82011-03-15Henrik Grubbström (Grubba)  if (me->not_a_module) nostart = 1;
bcf5662000-04-05Per Hedbor  if(!nostart) call_start_callbacks( me, moduleinfo, module ); #ifdef MODULE_DEBUG
be87a72002-04-17Marcus Wellhardh  if (enable_module_batch_msgs) { if(moduleinfo->config_locked[this_object()]) report_debug("\bLocked %6.1fms\n", (gethrtime()-start_time)/1000.0);
ce19332010-03-04Henrik Grubbström (Grubba)  else if (me->not_a_module) report_debug("\bN/A %6.1fms\n", (gethrtime()-start_time)/1000.0);
be87a72002-04-17Marcus Wellhardh  else report_debug("\bOK %6.1fms\n", (gethrtime()-start_time)/1000.0); } #else if(moduleinfo->config_locked[this_object()]) report_error(" Error: \"%s\" not loaded (license restriction).\n", moduleinfo->get_name());
ce19332010-03-04Henrik Grubbström (Grubba)  else if (me->not_a_module) report_debug(" Note: \"%s\" not available.\n", moduleinfo->get_name());
bcf5662000-04-05Per Hedbor #endif
fd24e22000-08-28Per Hedbor  if( !enabled_modules[modname+"#"+id] ) { enabled_modules[modname+"#"+id] = 1;
e6cd832002-04-09Marcus Wellhardh  if(!nosave) store( "EnabledModules", enabled_modules, 1, this_object());
fd24e22000-08-28Per Hedbor  }
6f595f2000-08-20Per Hedbor 
e6cd832002-04-09Marcus Wellhardh  if (!has_stored_vars && !nosave)
bcf5662000-04-05Per Hedbor  store (modname + "#" + id, me->query(), 0, this_object());
f63aca2000-09-08Martin Stjernholm  if( me->no_delayed_load && got_no_delayed_load >= 0 ) got_no_delayed_load = 1;
a8ba692000-08-29Marcus Wellhardh 
bcf5662000-04-05Per Hedbor  return me; } void call_start_callbacks( RoxenModule me, ModuleInfo moduleinfo,
0a1a762006-05-31Martin Stjernholm  ModuleCopies module, void|int newly_added)
bcf5662000-04-05Per Hedbor {
a8ba692000-08-29Marcus Wellhardh  call_low_start_callbacks( me, moduleinfo, module );
0a1a762006-05-31Martin Stjernholm  call_high_start_callbacks (me, moduleinfo, newly_added);
a8ba692000-08-29Marcus Wellhardh } void call_low_start_callbacks( RoxenModule me, ModuleInfo moduleinfo, ModuleCopies module ) {
40f0642000-09-13Per Hedbor  if(!me) return; if(!moduleinfo) return; if(!module) return;
a8ba692000-08-29Marcus Wellhardh  int module_type = moduleinfo->type, pr; mixed err;
10c7e11999-12-28Martin Nilsson  if (err = catch(pr = me->query("_priority")))
c5e0961999-10-04Per Hedbor  {
c45b2a1999-12-09Martin Stjernholm #ifdef MODULE_DEBUG
9a1d472000-01-12Martin Stjernholm  if (enable_module_batch_msgs) report_debug("\bERROR\n");
c45b2a1999-12-09Martin Stjernholm #endif
67f60e2000-07-09Martin Nilsson  string bt=describe_backtrace(err);
49cd282000-08-11Andreas Lange  report_error(LOC_M(41, "Error while initiating module copy of %s%s"),
67f60e2000-07-09Martin Nilsson  moduleinfo->get_name(), (bt ? ":\n"+bt : "\n"));
ad683e1998-05-09Henrik Grubbström (Grubba)  pr = 3; } api_module_cache |= me->api_functions();
10c7e11999-12-28Martin Nilsson  if(module_type & MODULE_EXTENSION)
c5e0961999-10-04Per Hedbor  {
434bac2000-07-14Andreas Lange  report_error("%s is an MODULE_EXTENSION, that type is no " "longer available.\nPlease notify the modules writer.\n" "Suitable replacement types include MODULE_FIRST and " " MODULE_LAST.\n", moduleinfo->get_name());
c5e0961999-10-04Per Hedbor  }
ad683e1998-05-09Henrik Grubbström (Grubba) 
c5e0961999-10-04