Roxen.git / server / base_server / configuration.pike

version» Context lines:

Roxen.git/server/base_server/configuration.pike:1:   // This file is part of Roxen WebServer.   // Copyright © 1996 - 2009, Roxen IS.   //      // @appears Configuration   //! A site's main configuration    - constant cvs_version = "$Id: configuration.pike,v 1.726 2012/05/10 17:48:29 mast Exp $"; + constant cvs_version = "$Id$";   #include <module.h>   #include <module_constants.h>   #include <roxen.h>   #include <request_trace.h>   #include <timers.h>      #define CATCH(P,X) do{mixed e;if(e=catch{X;})report_error("While "+P+"\n"+describe_backtrace(e));}while(0)      // Tell Pike.count_memory this is global.   constant pike_cycle_depth = 0;
Roxen.git/server/base_server/configuration.pike:209:   mapping(RequestID:mapping) connection_get( )   //! Return all currently active connections.   {    return current_connections;   }      // It's nice to have the name when the rest of __INIT executes.   string name = roxen->bootstrap_info->get();      //! The hierarchal cache used for the HTTP protocol cache. - class DataCache + class DataCacheImpl   {    protected typedef array(string|mapping(string:mixed))|string|    function(string, RequestID:string|int) EntryType;    -  mapping(string:EntryType) cache = ([]); +  protected mapping(string:EntryType) cache = ([]);    -  int current_size; -  int max_size; -  int max_file_size; +  protected int current_size; +  protected int max_size; +  protected int max_file_size;    -  int hits, misses; +  protected int hits, misses;    -  +  mapping get_cache_stats() +  { +  return ([ "current_size" : current_size, +  "max_size" : max_size, +  "max_file_size" : max_file_size, +  "entries" : sizeof(cache), +  "hits" : hits, +  "misses" : misses ]); +  } +     void flush()    {   #ifndef RAM_CACHE_NO_RELOAD_FLUSH    current_size = 0;    cache = ([]);   #endif    }       // 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 -  // can pretty much assume it has no other permanent refs. 128 is a +  // can pretty much assume it has no other permanent refs. 1024 is a    // constant penalty that accounts for the keypair in the mapping and -  // that leaf entries are stored in arrays. - #define CALC_ENTRY_SIZE(key, data) (sizeof (data) + sizeof (key) + 128) +  // 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)       // Expire a single entry.    protected void really_low_expire_entry(string key)    {    EntryType e = m_delete(cache, key);    if (arrayp(e)) {    current_size -= CALC_ENTRY_SIZE (key, e[0]);    if (e[1]->co_handle) {    remove_call_out(e[1]->co_handle);    }    if (CacheKey cachekey = e[1]->key) {    destruct (cachekey);    } -  +  } else if (!zero_type(e)) { +  current_size -= CALC_VARY_CB_SIZE(key);    }    }       // NOTE: Avoid using this function if possible! O(n)    protected int low_expire_entry(string key_prefix)    {    if (!key_prefix) return 0;    if (arrayp(cache[key_prefix])) {    // Leaf node. No need to loop.    really_low_expire_entry(key_prefix);
Roxen.git/server/base_server/configuration.pike:287:    return;    }    string url = key_prefix;    sscanf(url, "%[^\0]", url);    while(1) {    EntryType val;    if (arrayp(val = cache[key_prefix])) {    current_size -= CALC_ENTRY_SIZE (key_prefix, val[0]);    m_delete(cache, key_prefix);    return; +  } else if (!zero_type(val)) { +  current_size -= CALC_VARY_CB_SIZE(key_prefix);    }    if (!val) {    return;    }       string|array(string) key_frag;    if (stringp(val)) {    key_frag = id->request_headers[val];    } else {    key_frag = val(url, id);    }    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;    }    }       //! Clear ~1/10th of the cache. -  protected void clear_some_cache() +  void clear_some_cache()    {    // FIXME: Use an iterator to avoid indices() here.    array(string) q = indices(cache);    if(!sizeof(q))    {    current_size=0;    return;    }       // The following code should be ~O(n * log(n)).    sort(q); -  for(int i = 0; i < sizeof(q)/10; i++) { +  for(int i = 0; i < sizeof(q)/10 + 1; i++) {    int r = random(sizeof(q));    string key_prefix = q[r = random(sizeof(q))];    if (!key_prefix) continue;    for(;r < sizeof(q); r++,i++) {    if (!q[r]) continue;    if (!has_prefix(q[r], key_prefix)) break;    really_low_expire_entry(q[r]);    q[r] = 0;    }    }
Roxen.git/server/base_server/configuration.pike:362:    string|function(string, RequestID: string|int) vary_cb) {    array(string|mapping(string:mixed))|string|    function(string, RequestID:string|int) old = cache[key];    if (old && (old != vary_cb)) {    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)));    low_expire_entry(key); +  old = UNDEFINED; // Ensure that current size is updated below.    SIMPLE_TRACE_LEAVE ("");    }    cache[key] = vary_cb; -  +  if(!old) { +  current_size += CALC_VARY_CB_SIZE(key); +  }       SIMPLE_TRACE_ENTER (this, "Registering vary cb %O", vary_cb);       string key_frag;    if (stringp(vary_cb)) {    string|array(string) header = id->request_headers[vary_cb];    if (arrayp(header)) key_frag = header * ",";    else key_frag = header;    } else {    int|string frag = vary_cb(url, id);
Roxen.git/server/base_server/configuration.pike:390:    }       SIMPLE_TRACE_LEAVE ("Vary cb resolved to key fragment %O",    key_frag || "");       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; +  entry_size += 2 + sizeof(key_frag);    }       array(string|mapping(string:mixed))|string|    function(string, RequestID:string) old = cache[key];    if (old) {    SIMPLE_TRACE_LEAVE ("Entry conflicts with existing entry %s, "    "old entry expired",    (arrayp (old) ? "of size " + sizeof (old[0]) :    sprintf ("%O", old)));    low_expire_entry(key);
Roxen.git/server/base_server/configuration.pike:480:    while( (current_size > max_size) && (n++<10))    clear_some_cache();    }       protected void create()    {    init_from_variables();    }   }    +  + class DataCache { +  inherit DataCacheImpl; +  +  constant AUTH_PUBL = "AUTH |"; +  constant PUBL_ONLY = ""; +  +  void expire_entry(string key_prefix, RequestID|void id) +  { +  // Expire both authenticated and non-authenticated entries +  ::expire_entry(AUTH_PUBL + key_prefix, id); +  ::expire_entry(PUBL_ONLY + key_prefix, id); +  } +  +  void set(string url, string data, mapping meta, int expire, RequestID id) +  { +  if (id->rawauth) { +  // If this is an authenticated request that is flagged as cacheable +  // we should offer it to all future requests regardless of auth +  // status. +  // +  // We could technically drop the PUBL_ONLY copy, but expiring it may +  // kill subresources that have been built earlier so let's keep it +  // in two places. (The data strings are shared anyway.) +  ::set(AUTH_PUBL + url, data, meta, expire, id); +  ::set(PUBL_ONLY + url, data, meta, expire, id); +  } else { +  // If no authentication is present we only set/replace the PUBL_ONLY +  // entry. We don't need to expire the AUTH entry since it will either +  // be valid or expired manually from the outside. +  ::set(PUBL_ONLY + url, data, meta, expire, id); +  } +  } +  +  array(string|mapping(string:mixed)) get(string url, RequestID id) +  { +  // Only non-authenticated requests may search the PUBL_ONLY compartment. +  // If we don't get a hit we'll proceed to the second get() below, but +  // we then need to adjust the failed lookup statistics to not count +  // this twice. +  mixed res = UNDEFINED; +  if (!id->rawauth) { +  res = ::get(PUBL_ONLY + url, id); +  if (!res) +  misses--; +  } +  +  // Anyone can search the AUTH_PUBL compartment +  return res || ::get(AUTH_PUBL + url, id); +  } + } +  +    #include "rxml.pike";   constant store = roxen.store;   constant retrieve = roxen.retrieve;   constant remove = roxen.remove;      int config_id;   int get_config_id()   {    if(config_id) return config_id;    for(int i=sizeof(roxen->configurations); i;)
Roxen.git/server/base_server/configuration.pike:513:    return module->variables[variable]->name()+    "\n"+module->variables[ variable ]->doc();    }    if(variables[ variable ])    return variables[variable]->name()+    "\n"+variables[ variable ]->doc();   }      string query_internal_location(RoxenModule|void mod)   { -  return internal_location+(mod?replace(otomod[mod]||"", "#", "!")+"/":""); +  string ret = internal_location+(mod?replace(otomod[mod]||"", "#", "!")+"/":""); +  if (has_suffix(ret, "!0/")) { +  // Special case for module copy #0. +  ret = ret[..<sizeof("!0/")] + "/";    } -  +  return ret; + }      string query_name()   {    if(strlen(query("name")))    return query("name");    return name;   }      string comment()   {
Roxen.git/server/base_server/configuration.pike:542:    if (cached_compat_level == 0.0)    cached_compat_level = (float) query ("compat_level");    return cached_compat_level;   }      /* 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.    */    - array (Priority) allocate_pris() - { -  return allocate(10, Priority)(); - } -  +    array(int) query_oid()   {    return SNMP.RIS_OID_WEBSERVER + ({ 2 });   }      //! @returns   //! Returns an array with two elements:   //! @array   //! @elem array(int) oid   //!
Roxen.git/server/base_server/configuration.pike:607: Inside #if defined(HTTP_COMPRESSION)
  int(0..1) http_compr_dynamic_reqs;   Thread.Local gz_file_pool = Thread.Local();   #endif      int handler_queue_timeout;      // The logging format used. This will probably move to the above   // mentioned module in the future.   private mapping (int|string:string) log_format = ([]);    - // A list of priority objects - array (Priority) pri = allocate_pris(); + //! The modules sorted with regards to priority and type. + private array(RoxenModule) sorted_modules = ({});    -  + //! NB: May contain junk in the high-order bits. + private array(int) sorted_module_types = ({}); +    mapping modules = ([]);   //! All enabled modules in this site.   //! The format is "module":{ "copies":([ num:instance, ... ]) }      mapping (RoxenModule:string) otomod = ([]);   //! A mapping from the module objects to module names      int module_set_counter = 1;   //! Incremented whenever the set of enabled modules changes, or if a   //! module is reloaded.
Roxen.git/server/base_server/configuration.pike:634:   // 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=([]);   private mapping (string:array (RoxenModule)) provider_module_cache=([]);   private array (RoxenModule) auth_module_cache, userdb_module_cache;    + private int module_sort_key(RoxenModule me) + { +  int pri = me->query("_priority"); +  array(int|string) info = me->register_module(); +  return (pri << 32) | (info[0] & MODULE_TYPE_MASK); + }    -  + private void sort_modules() + { +  sorted_module_types = map(sorted_modules, module_sort_key); +  sort(sorted_module_types, sorted_modules); +  sorted_module_types = map(sorted_module_types, `&, MODULE_TYPE_MASK); +  invalidate_cache(); + } +  + // Generic lookup function for the various module caches. + private array(function|RoxenModule) low_module_lookup(int module_type_mask, +  string|void symbol) + { +  array(RoxenModule) modules = +  reverse(filter(sorted_modules, +  map(sorted_module_types, `&, module_type_mask))); +  if (!symbol) return modules; +  return modules[symbol] - ({ 0 }); + } +    void unregister_urls()   {    foreach( registered_urls + failed_urls, string url )    roxen.unregister_url(url, this_object());    registered_urls = ({});   }      private void safe_stop_module (RoxenModule mod, string desc)   {    if (mixed err = catch (mod && mod->stop &&    call_module_func_with_cbs (mod, "stop", 0)))    report_error ("While stopping " + desc + ": " + describe_backtrace (err));   }      private Thread.Mutex stop_all_modules_mutex = Thread.Mutex();      private void do_stop_all_modules (Thread.MutexKey stop_lock)   { -  mapping(RoxenModule:string) allmods = otomod + ([]); -  -  if (types_module) { -  safe_stop_module (types_module, "type module"); -  m_delete (allmods, types_module); +  foreach(sorted_modules, RoxenModule m) { +  safe_stop_module(m, "module");    }    -  if (dir_module) { -  safe_stop_module (dir_module, "directory module"); -  m_delete (allmods, dir_module); -  } +  end_logger();    -  for(int i=0; i<10; i++) -  if (Priority p = pri[i]) { - #define STOP_MODULES(MODS, DESC) \ -  foreach(MODS, RoxenModule m) \ -  if (allmods[m]) { \ -  safe_stop_module (m, DESC); \ -  m_delete (allmods, m); \ -  } -  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"); -  } -  -  if (mixed err = catch { -  if (object m = log_function && function_object (log_function)) { -  destruct (m); -  allmods[m] = 0; -  } -  }) report_error ("While stopping the logger: " + describe_backtrace (err)); -  -  STOP_MODULES(indices (allmods), "unclassified module"); - #undef STOP_MODULES -  +     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
Roxen.git/server/base_server/configuration.pike:723:    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);    }       if (!asynch) stop_all_modules_mutex->lock (1); +  destruct(json_logger);   }      string|array(string) type_from_filename( string file, int|void to,    string|void myext )   {    array(string)|string tmp;    if(!types_fun)    return to?({ "application/octet-stream", 0 }):"application/octet-stream";       string ext = lower_case(myext || Roxen.extension(file));
Roxen.git/server/base_server/configuration.pike:756:    } else if (!(tmp = types_fun("default"))) {    tmp = ({ "application/octet-stream", 0 });    }    return to?tmp:tmp[0];   }      array (RoxenModule) get_providers(string provides)   //! Returns an array with all provider modules that provides "provides".   {    // This cache is cleared in the invalidate_cache() call. -  if(!provider_module_cache[provides]) +  if(!sizeof(provider_module_cache))    { -  int i; -  provider_module_cache[provides] = ({ }); -  for(i = 9; i >= 0; i--) -  { -  array(RoxenModule) modules = indices(pri[i]->provider_modules); -  array(string) module_identifiers = modules->module_identifier(); -  sort(module_identifiers, modules); -  foreach(modules, RoxenModule d) -  if(pri[i]->provider_modules[ d ][ provides ]) -  provider_module_cache[provides] += ({ d }); +  provider_module_cache[0] = 0; // Initialization sentinel. +  int prev_pri = -1; +  array(RoxenModule) modules = ({}); +  foreach(low_module_lookup(MODULE_PROVIDER), RoxenModule me) { +  if (!me->query_provides) continue; +  int pri = me->query("_priority"); +  if (pri != prev_pri) { +  sort(modules->module_identifier(), modules); +  foreach(modules, RoxenModule p) { +  mixed provs = p->query_provides(); +  if (stringp(provs)) { +  provs = (< provs >); +  } else if (arrayp(provs)) { +  provs = mkmultiset(provs);    } -  +  if (provs) { +  foreach(provs; string provides;) { +  provider_module_cache[provides] += ({ p });    } -  return provider_module_cache[provides]; +     } -  +  } +  modules = ({}); +  } +  prev_pri = pri; +  modules += ({ me }); +  } +  sort(modules->module_identifier(), modules); +  foreach(modules, RoxenModule p) { +  mixed provs = p->query_provides(); +  if (stringp(provs)) { +  provs = (< provs >); +  } else if (arrayp(provs)) { +  provs = mkmultiset(provs); +  } +  if (provs) { +  foreach(provs; string provides;) { +  provider_module_cache[provides] += ({ p }); +  } +  } +  } +  } +  return provider_module_cache[provides] || ({}); + }      RoxenModule get_provider(string provides)   //! Returns the first provider module that provides "provides".   {    array (RoxenModule) prov = get_providers(provides);    if(sizeof(prov))    return prov[0];    return 0;   }   
Roxen.git/server/base_server/configuration.pike:821:    function f;    if(objectp(mod) && functionp(f = mod[fun])) {    mixed ret;    if (ret = f(@args)) {    return ret;    }    }    }   }    - array (function) file_extension_modules(string ext) + array(function) file_extension_modules(string ext)   { -  if(!file_extension_module_cache[ext = lower_case(ext)]) -  { -  int i; -  file_extension_module_cache[ext] = ({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d = pri[i]->file_extension_modules[ext]) -  foreach(d, p) -  file_extension_module_cache[ext] += ({ p->handle_file_extension }); +  if (!sizeof(file_extension_module_cache)) { +  file_extension_module_cache[0] = 0; // Initialization sentinel. +  foreach(low_module_lookup(MODULE_FILE_EXTENSION), RoxenModule me) { +  if (!me->handle_file_extension) continue; +  array(string) arr = me->query_file_extensions(); +  foreach(arr, string e) { +  file_extension_module_cache[e] += ({ me->handle_file_extension });    }    } -  +  }    return file_extension_module_cache[ext];   }    - array (function) url_modules() + array(function) url_modules()   {    if(!url_module_cache)    { -  int i; -  url_module_cache=({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d=pri[i]->url_modules) -  foreach(d, p) -  url_module_cache += ({ p->remap_url }); +  url_module_cache = low_module_lookup(MODULE_URL, "remap_url");    } -  } +     return url_module_cache;   }      protected mapping api_module_cache = ([]);   mapping api_functions(void|RequestID id)   {    return api_module_cache+([]);   }      array (function) logger_modules()   {    if(!logger_module_cache)    { -  int i; -  logger_module_cache=({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d=pri[i]->logger_modules) -  foreach(d, p) -  if(p->log) -  logger_module_cache += ({ p->log }); +  logger_module_cache = low_module_lookup(MODULE_LOGGER, "log");    } -  } +     return logger_module_cache;   }      array (function) last_modules()   {    if(!last_module_cache)    { -  int i; -  last_module_cache=({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d=pri[i]->last_modules) -  foreach(d, p) -  if(p->last_resort) -  last_module_cache += ({ p->last_resort }); +  last_module_cache = low_module_lookup(MODULE_LAST, "last_resort");    } -  } +     return last_module_cache;   }      protected mixed strip_fork_information(RequestID id)   {    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"))    // Skip elaborate error page since we get these e.g. for WebDAV    // mounts in OS X Finder. -  return Roxen.http_string_answer("No such file", "text/plain"); +  return Roxen.http_status(404, "No such file.");    }       array a = id->not_query/"::";    // 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];    id->misc->fork_information = a[1..];    return 0;   }      array (function) first_modules()   {    if(!first_module_cache)    { -  int i; +     first_module_cache = ({ });       // Add special fork handlers on Windows and Mac OS X    if (   #ifdef __NT__    1 ||   #endif    uname()->sysname == "Darwin") { -  first_module_cache= ({ +  first_module_cache += ({    strip_fork_information, // Always first!    });    }    -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; RoxenModule p; -  if(d=pri[i]->first_modules) { -  foreach(d, p) { -  if(p->first_try) { -  first_module_cache += ({ p->first_try }); +  first_module_cache += low_module_lookup(MODULE_FIRST, "first_try");    } -  } -  } -  } -  } +        return first_module_cache;   }      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;   }      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 ); - // tmp += ({ ({ 0, roxen->config_userdb_module }) }); -  return userdb_module_cache = reverse(column(tmp,1)); +  return userdb_module_cache = low_module_lookup(MODULE_USERDB);   }      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)); +  return auth_module_cache = low_module_lookup(MODULE_AUTH);   }      array location_modules()   //! Return an array of all location modules the request should be   //! mapped through, by order of priority.   {    if(!location_module_cache)    { -  int i; +     array new_location_module_cache=({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d=pri[i]->location_modules) { +  int prev_pri = -1;    array level_find_files = ({}); -  array level_locations = ({}); -  foreach(d, p) { -  string location; +  array(string) level_locations = ({}); +  foreach(low_module_lookup(MODULE_LOCATION), RoxenModule me) { +  int pri = me->query("_priority"); +  if (pri != prev_pri) { +  // Order after longest prefix length +  sort(map(level_locations, +  lambda(string loc) { return -sizeof(loc); }), +  level_locations, level_find_files); +  foreach(level_locations; int i; string path) { +  new_location_module_cache += ({ ({ path, level_find_files[i] }) }); +  } +  level_locations = ({}); +  level_find_files = ({}); +  } +  prev_pri = pri;    // FIXME: Should there be a catch() here? -  if(p->find_file && (location = p->query_location())) { -  level_find_files += ({ p->find_file }); +  string location = me->find_file && me->query_location(); +  if (!location) continue; +  level_find_files += ({ me->find_file });    level_locations += ({ location });    } -  +  +  sort(map(level_locations, +  lambda(string loc) { return -sizeof(loc); }), +  level_locations, level_find_files); +  foreach(level_locations; int i; string path) { +  new_location_module_cache += ({ ({ path, level_find_files[i] }) });    } -  sort(map(level_locations, sizeof), level_locations, level_find_files); -  int j; -  for (j = sizeof(level_locations); j--;) { -  // Order after longest path first. -  new_location_module_cache += ({ ({ level_locations[j], -  level_find_files[j] }) }); -  } -  } -  } +     location_module_cache = new_location_module_cache;    }    return location_module_cache;   }      array(function) filter_modules()   {    if(!filter_module_cache)    { -  int i; -  filter_module_cache=({ }); -  for(i=9; i>=0; i--) -  { -  array(RoxenModule) d; -  RoxenModule p; -  if(d=pri[i]->filter_modules) -  foreach(d, p) -  if(p->filter) -  filter_module_cache+=({ p->filter }); +  filter_module_cache = low_module_lookup(MODULE_FILTER, "filter");    } -  } +     return filter_module_cache;   }    -  - void init_log_file() + void end_logger()   { -  if(log_function) -  { -  // Free the old one. -  destruct(function_object(log_function)); +  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;   } -  +  + void init_log_file() + { +  end_logger();    // Only try to open the log file if logging is enabled!!    if(query("Log"))    {    string logfile = query("LogFile");    if(strlen(logfile))    log_function = roxen.LogFile(logfile, query("LogFileCompressor"))->write;    }   }      private void parse_log_formats()
Roxen.git/server/base_server/configuration.pike:1232:   //! DEPRECATED COMPATIBILITY FUNCTION   //!   //! Fetches the full list of valid usernames from the authentication   //! module by calling its userlist() method. Returns zero if no auth   //! module was present.   //!   //! Note that you should always supply id if it's possible, some user   //! databases require it (such as the htaccess database)   {    array(string) list = ({}); -  foreach( user_databases(), UserDB m ) +  foreach( user_databases(), UserDB m ) { +  if (!m->list_users) continue;    list |= m->list_users(id); -  +  }    return list;   }      array(string) user_from_uid(int u, RequestID|void id)   //! @note   //! DEPRECATED COMPATIBILITY FUNCTION   //!   //! 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.
Roxen.git/server/base_server/configuration.pike:1498:    otomod[m], describe_backtrace(err));   }      // Returns tuple < image, mime-type >   protected array(string) draw_saturation_bar(int hue,int brightness, int where,    int small_version)   {    Image.Image bar =    small_version ? Image.Image(16, 128) : Image.Image(30, 256);    +  hue &= 0xff; +  brightness &= 0xff; +     for(int i=0;i<128;i++)    {    int j = i * 2;    array color = hsv_to_rgb(hue, 255 - j, brightness);    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);    }
Roxen.git/server/base_server/configuration.pike:1600:    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");    return Roxen.http_string_answer(bar[0], bar[1]);    }       Stdio.File f;       if( !id->misc->internal_get ) -  if(f = lopen("roxen-images/"+from+".gif", "r")) -  return (["file":f, "type":"image/gif", "stat":f->stat()]); -  +     if(f = lopen("roxen-images/"+from+".png", "r"))    return (["file":f, "type":"image/png", "stat":f->stat()]);    -  +  if(f = lopen("roxen-images/"+from+".gif", "r")) +  return (["file":f, "type":"image/gif", "stat":f->stat()]); +     if(f = lopen("roxen-images/"+from+".jpg", "r"))    return (["file":f, "type":"image/jpeg", "stat":f->stat()]);       if(f = lopen("roxen-images/"+from+".xcf", "r"))    return (["file":f, "type":"image/x-gimp-image", "stat":f->stat()]);       if(f = lopen("roxen-images/"+from+".gif", "r"))    return (["file":f, "type":"image/gif", "stat":f->stat()]);    // File not found.    return 0;
Roxen.git/server/base_server/configuration.pike:1736:       if (stringp(m->extra_heads["content-type"]) ||    stringp(m->type)) {    res += sprintf(" of %O", m->type||m->extra_heads["content-type"]);    }       return res;   }      //! Find all applicable locks for this user on @[path]. - multiset(DAVLock) find_locks(string path, int(0..1) recursive, + multiset(DAVLock) find_locks(string path, int(-1..1) recursive,    int(0..1) exclude_shared, RequestID id)   {    SIMPLE_TRACE_ENTER(0, "find_locks(%O, %O, %O, X)",    path, recursive, exclude_shared);    multiset(DAVLock) locks = (<>);       foreach(location_module_cache||location_modules(),    [string loc, function func])    {    SIMPLE_TRACE_ENTER(function_object(func),
Roxen.git/server/base_server/configuration.pike:1950:    }    } else if (lockscope == "DAV:exclusive" ?    lock_info >= LOCK_SHARED_BELOW :    lock_info >= LOCK_OWN_BELOW) {    // Some other lock prevents us.    return Roxen.http_status(423, "Locked");    }       // Create the new lock.    -  string locktoken = "opaquelocktoken:" + roxen->new_uuid_string(); +  string locktoken = "urn:uuid:" + roxen->new_uuid_string();    DAVLock lock = DAVLock(locktoken, path, recursive, lockscope, locktype,    expiry_delta, owner);    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)) {
Roxen.git/server/base_server/configuration.pike:2249:   #endif    PROF_ENTER(Roxen.get_owning_module(tmp[1])->module_name,"location");    TRACE_ENTER("Calling find_file()...", 0);    LOCK(tmp[1]);    fid=tmp[1]( file[ strlen(loc) .. ] + id->extra_extension, id);    UNLOCK();    TRACE_LEAVE("");    PROF_LEAVE(Roxen.get_owning_module(tmp[1])->module_name,"location");    if(fid)    { +  if (id)    id->virtfile = loc;       if(mappingp(fid))    {    TRACE_LEAVE(""); // Location module [...]    TRACE_LEAVE(examine_return_mapping(fid));    TIMER_END(location_modules);    return fid;    }    else
Roxen.git/server/base_server/configuration.pike:2504:    root_id->misc->_request_depth++;    if(sub_req_limit && root_id->misc->_request_depth > sub_req_limit)    error("Subrequest limit reached. (Possibly an insertion loop.)");       mapping|int res;    mapping res2;    function tmp;    res = low_get_file(id, no_magic);    TIMER_END(get_file);    +  // Note: id may be destructed at this point already (when the +  // request is handled asynchronously, +  // i.e. Roxen.http_pipe_in_progress() is returned, but +  // id->send_result() finishes in another thread before the calling +  // thread gets to this point.) +  if (id && (!mappingp (res) || !res->pipe)) {    // finally map all filter type modules.    // Filter modules are like TYPE_LAST modules, but they get called    // for _all_ files.    TIMER_START(filter_modules);    foreach(filter_module_cache||filter_modules(), tmp)    {    TRACE_ENTER("Filter module", tmp);    PROF_ENTER(Roxen.get_owning_module(tmp)->module_name,"filter");    if(res2=tmp(res,id))    {    if(mappingp(res) && res->file && (res2->file != res->file))    destruct(res->file);    TRACE_LEAVE("Rewrote result.");    res=res2;    } else    TRACE_LEAVE("");    PROF_LEAVE(Roxen.get_owning_module(tmp)->module_name,"filter");    }    TIMER_END(filter_modules); -  +  }    -  +  if (root_id)    root_id->misc->_request_depth--; -  +  +  if (id)    id->misc->internal_get = orig_internal_get; -  +     return res;   }    -  + protected string combine_combiners(string s) + { +  if (String.width(s) <= 8) return s; +  return Unicode.normalize(s, "NFC"); + } +    array(string) find_dir(string file, RequestID id, void|int(0..1) verbose)   {    array dir;    TRACE_ENTER(sprintf("List directory %O.", file), 0);       if(!sizeof (file) || file[0] != '/')    file = "/" + file;      #ifdef URL_MODULES   #ifdef THREADS
Roxen.git/server/base_server/configuration.pike:2576: Inside #if defined(URL_MODULES)
   if( id->misc->find_dir_nest < 20 )    dir = (id->conf || this_object())->find_dir( file, id );    else    error("Too deep recursion in roxen::find_dir() while mapping "    +file+".\n");    };    id->misc->find_dir_nest = 0;    TRACE_LEAVE("");    if(err)    throw(err); +  if (arrayp(dir)) { +  return map(dir, combine_combiners); +  }    return dir;    } -  +  TRACE_LEAVE("");    id->not_query=of;    }   #endif /* URL_MODULES */       array | mapping d;    array(string) locks=({});    RoxenModule mod;    string loc;    foreach(location_modules(), array tmp)    {
Roxen.git/server/base_server/configuration.pike:2606:    }   #endif    mod=function_object(tmp[1]);    if(d=mod->find_dir(file[strlen(loc)..], id))    {    if(mappingp(d))    {    if(d->files) {    TRACE_LEAVE("Got exclusive directory.");    TRACE_LEAVE(sprintf("Returning list of %d files.", sizeof(d->files))); -  return d->files; +  return map(d->files, combine_combiners);    } else    TRACE_LEAVE("");    } else {    TRACE_LEAVE("Got files.");    if(!dir) dir=({ });    dir |= d;    }    }    else {    if(verbose && mod->list_lock_files)
Roxen.git/server/base_server/configuration.pike:2637:    loc=loc[strlen(file)..];    sscanf(loc, "%s/", loc);    if (dir) {    dir |= ({ loc });    } else {    dir = ({ loc });    }    TRACE_LEAVE("Added module mountpoint.");    }    } -  if(!dir) return verbose ? ({0})+locks : ([])[0]; +  if(!dir) { +  TRACE_LEAVE("No directory contents.\n"); +  return verbose ? ({0})+locks : ([])[0]; +  }    if(sizeof(dir))    {    TRACE_LEAVE(sprintf("Returning list of %d files.", sizeof(dir))); -  return dir; +  return map(dir, combine_combiners);    }    TRACE_LEAVE("Returning 'No such directory'.");    return 0;   }      // Stat a virtual file.      array(int)|Stat stat_file(string file, RequestID id)   {    mixed s, tmp;
Roxen.git/server/base_server/configuration.pike:2798:       if (recurse_count > 50) {    TRACE_ENTER ("Looped " + recurse_count +    " times in internal redirects - giving up", 0);    TRACE_LEAVE ("");    }       else {    Configuration oc = id->conf;    id->not_query = fname; +  +  // 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"); +     TRY_FIRST_MODULES (file, open_file (fname, mode, id,    internal_get, recurse_count + 1));    fname = id->not_query;       if(search(mode, "R")!=-1) // raw (as in not parsed..)    {    string f;    mode -= "R";    if(f = real_file(fname, id))    {
Roxen.git/server/base_server/configuration.pike:3517:    "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."),    }),    })    }),    ({    UNDEFINED,    SNMP.Counter(lambda() -  { return datacache->hits + datacache->misses; }, +  { +  mapping stats = +  datacache->get_cache_stats(); +  return stats->hits + stats->misses; +  },    "protCacheLookups",    "Number of protocol cache lookups."),    SNMP.Counter(lambda() -  { return datacache->hits; }, +  { +  return +  datacache->get_cache_stats()->hits; +  },    "protCacheHits",    "Number of protocol cache hits."),    SNMP.Counter(lambda() -  { return datacache->misses; }, +  { +  return +  datacache->get_cache_stats()-> +  misses; +  },    "protCacheMisses",    "Number of protocol cache misses."),    SNMP.Gauge(lambda() -  { return sizeof(datacache->cache); }, +  { +  return +  datacache->get_cache_stats()->entries; +  },    "protCacheEntries",    "Number of protocol cache entries."),    SNMP.Gauge(lambda() -  { return datacache->max_size/1024; }, +  { +  return +  datacache->get_cache_stats()-> +  max_size / 1024; +  },    "protCacheMaxSize",    "Maximum size of protocol cache in KiB."),    SNMP.Gauge(lambda() -  { return datacache->current_size/1024; }, +  { +  return +  datacache->get_cache_stats()-> +  current_size / 1024; +  },    "protCacheCurrSize",    "Current size of protocol cache in KiB."),    })    }));    SNMP.set_owner(mib, this_object());    prot->mib->merge(mib);    }    }       if (retrieve ("EnabledModules", this)["config_filesystem#0"])
Roxen.git/server/base_server/configuration.pike:3923:    }       me->set_configuration( this_object() );       module_type = moduleinfo->type;    if (module_type & MODULE_TYPE_MASK)    {    if(!(module_type & MODULE_CONFIG))    {    if (err = catch { -  me->defvar("_priority", 5, DLOCALE(12, "Priority"), TYPE_INT_LIST, -  DLOCALE(13, "The priority of the module. 9 is highest and 0 is lowest." -  " Modules with the same priority can be assumed to be " -  "called in random order."), -  ({0, 1, 2, 3, 4, 5, 6, 7, 8, 9})); +  me->defvar("_priority", Variable. +  Priority(5, 0, DLOCALE(12, "Priority"), +  DLOCALE(13, "<p>The priority of the module.</p>\n" +  "<p>Modules with the same priority " +  "can be assumed to be " +  "called in random order.</p>\n")))-> +  set_range(0, query("max_priority"));    }) {    throw(err);    }    }      #ifdef MODULE_LEVEL_SECURITY    if( (module_type & ~(MODULE_LOGGER|MODULE_PROVIDER|MODULE_USERDB)) != 0 )    {   // me->defvar("_sec_group", "user", DLOCALE(14, "Security: Realm"),   // TYPE_STRING,
Roxen.git/server/base_server/configuration.pike:4121: Inside #if defined(MODULE_DEBUG)
   {   #ifdef MODULE_DEBUG    if (enable_module_batch_msgs) report_debug("\bERROR\n");   #endif    string bt=describe_backtrace(err);    report_error(LOC_M(41, "Error while initiating module copy of %s%s"),    moduleinfo->get_name(), (bt ? ":\n"+bt : "\n"));    pr = 3;    }    +  sorted_modules += ({ me }); +  sorted_module_types += ({ module_type }); +     api_module_cache |= me->api_functions();       if(module_type & MODULE_EXTENSION)    {    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());    }    -  if(module_type & MODULE_FILE_EXTENSION) -  if (err = catch { -  array arr = me->query_file_extensions(); -  if (arrayp(arr)) -  { -  string foo; -  foreach( me->query_file_extensions(), foo ) -  if(pri[pr]->file_extension_modules[foo = lower_case(foo)] ) -  pri[pr]->file_extension_modules[foo] += ({me}); -  else -  pri[pr]->file_extension_modules[foo] = ({me}); -  } -  }) { - #ifdef MODULE_DEBUG -  if (enable_module_batch_msgs) report_debug("\bERROR\n"); - #endif -  string bt=describe_backtrace(err); -  report_error(LOC_M(41, "Error while initiating module copy of %s%s"), -  moduleinfo->get_name(), (bt ? ":\n"+bt : "\n")); -  got_no_delayed_load = -1; -  } -  -  if(module_type & MODULE_PROVIDER) -  if (err = catch -  { -  mixed provs = me->query_provides ? me->query_provides() : ({}); -  if(stringp(provs)) -  provs = (< provs >); -  if(arrayp(provs)) -  provs = mkmultiset(provs); -  if (multisetp(provs)) { -  pri[pr]->provider_modules [ me ] = provs; -  } -  }) { - #ifdef MODULE_DEBUG -  if (enable_module_batch_msgs) report_debug("\bERROR\n"); - #endif -  string bt=describe_backtrace(err); -  report_error(LOC_M(41, "Error while initiating module copy of %s%s"), -  moduleinfo->get_name(), (bt ? ":\n"+bt : "\n")); -  got_no_delayed_load = -1; -  } -  +     if(module_type & MODULE_TYPES)    {    types_module = me;    types_fun = me->type_from_extension;    }       if(module_type & MODULE_TAG)    add_parse_module( me );       if(module_type & MODULE_DIRECTORIES)    if (me->parse_directory)    dir_module = me;    -  if(module_type & MODULE_LOCATION) -  pri[pr]->location_modules += ({ me }); +  sort_modules();    -  if(module_type & MODULE_LOGGER) -  pri[pr]->logger_modules += ({ me }); -  -  if(module_type & MODULE_URL) -  pri[pr]->url_modules += ({ me }); -  -  if(module_type & MODULE_LAST) -  pri[pr]->last_modules += ({ me }); -  -  if(module_type & MODULE_FILTER) -  pri[pr]->filter_modules += ({ me }); -  -  if(module_type & MODULE_FIRST) -  pri[pr]->first_modules += ({ me }); -  +     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, "/")) {
Roxen.git/server/base_server/configuration.pike:4239:    if (me->query_snmp_mib) {    array(int) segment = generate_module_oid_segment(me);    sub_mib = me->query_snmp_mib(query_oid() + ({ 8, 2 }) +    segment[..sizeof(segment)-2],    oid_suffix + ({ segment[-1] }));    SNMP.set_owner(sub_mib, this_object(), me);    prot->mib->merge(sub_mib);    }    }    } -  -  invalidate_cache(); +    }      void call_high_start_callbacks (RoxenModule me, ModuleInfo moduleinfo,    void|int newly_added)   {    // This is icky, but I don't know if it's safe to remove. /mast    if(!me) return;    if(!moduleinfo) return;       mixed err;
Roxen.git/server/base_server/configuration.pike:4322:   {    clean_up_for_module( moduleinfo, me );    call_low_start_callbacks( me,    moduleinfo,    modules[ moduleinfo->sname ] );   }      void clean_up_for_module( ModuleInfo moduleinfo,    RoxenModule me )   { -  int pr; -  if(moduleinfo->type & MODULE_FILE_EXTENSION) -  { -  string foo; -  for(pr=0; pr<10; pr++) -  foreach( indices (pri[pr]->file_extension_modules), foo ) -  pri[pr]->file_extension_modules[foo]-=({me}); +  int i = 0; +  // Loop for paranoia reasons. +  while((i < sizeof(sorted_modules)) && +  (i = search(sorted_modules, me, i)) >= 0) { +  sorted_modules = sorted_modules[..i-1] + sorted_modules[i+1..]; +  sorted_module_types = sorted_module_types[..i-1] + +  sorted_module_types[i+1..];    }    -  if(moduleinfo->type & MODULE_PROVIDER) { -  for(pr=0; pr<10; pr++) -  m_delete(pri[pr]->provider_modules, me); -  } -  +     if(moduleinfo->type & MODULE_TYPES)    {    types_module = 0;    types_fun = 0;    }       if(moduleinfo->type & MODULE_TAG)    remove_parse_module( me );       if( moduleinfo->type & MODULE_DIRECTORIES )    dir_module = 0;    -  if( moduleinfo->type & MODULE_LOCATION ) -  for(pr=0; pr<10; pr++) -  pri[pr]->location_modules -= ({ me }); +  api_module_cache -= me->api_functions();    -  if( moduleinfo->type & MODULE_URL ) -  for(pr=0; pr<10; pr++) -  pri[pr]->url_modules -= ({ me }); -  -  if( moduleinfo->type & MODULE_LAST ) -  for(pr=0; pr<10; pr++) -  pri[pr]->last_modules -= ({ me }); -  -  if( moduleinfo->type & MODULE_FILTER ) -  for(pr=0; pr<10; pr++) -  pri[pr]->filter_modules -= ({ me }); -  -  if( moduleinfo->type & MODULE_FIRST ) { -  for(pr=0; pr<10; pr++) -  pri[pr]->first_modules -= ({ me }); -  } -  -  if( moduleinfo->type & MODULE_LOGGER ) -  for(pr=0; pr<10; pr++) -  pri[pr]->logger_modules -= ({ me }); -  +     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;    }       SNMP.remove_owned(prot->mib, this_object(), me);    }    } -  +  +  invalidate_cache();   }      int disable_module( string modname, void|RoxenModule new_instance )   {    MODULE_LOCK (2);    RoxenModule me;    int id;    sscanf(modname, "%s#%d", modname, id );       if( datacache ) datacache->flush();
Roxen.git/server/base_server/configuration.pike:4659: Inside #if defined(SNMP_AGENT)
  #ifdef SNMP_AGENT    // Start trap after real virt.serv. loading    if(query("snmp_process") && objectp(roxen->snmpagent))    roxen->snmpagent->vs_start_trap(get_config_id());   #endif      }      DataCache datacache;    + // Handle changes in js logger endpoints + void json_log_endpoint_cb(object v) { +  if (!json_logger) return; +  +  array new_endpoints = v->query(); +  array old_endpoints = json_logger->get_bound_ports(); +  +  multiset obsolete = (multiset)old_endpoints; +  +  // Bind any new ports +  foreach(new_endpoints, string ep) { +  if (!ep || (ep == "")) continue; +  ep = roxen_path(ep); +  string json_log_dir = combine_path(getcwd(), roxen.query_configuration_dir(), "_jsonlog"); +  if (!Stdio.exist(json_log_dir)) { +  mkdir(json_log_dir, 0700); +  } +  ep = replace(ep, "$JSONLOGDIR", json_log_dir); +  +  if (!obsolete[ep]) { +  json_logger->bind(ep); +  } +  obsolete[ep] = 0; +  } +  +  foreach((array)obsolete, string ep) { +  if (!ep || (ep == "")) continue; +  json_logger->unbind(ep); +  } + } +  + protected void set_module_max_priority(Variable.Variable var) + { +  int new_max_priority = var->query(); +  foreach(sorted_modules, RoxenModule me) { +  Variable.Variable pri = me->getvar("_priority"); +  if (!pri) continue; +  pri->set_range(0, new_max_priority); +  } + } +    protected void create()   {    if (!name) error ("Configuration name not set through bootstrap_info.\n"); -  +  +  json_logger = ConfigurationLogger(combine_path_unix("conf", name), ([ ]), UNDEFINED); +    // int st = gethrtime();    roxen.add_permission( "Site:"+name, LOC_C(306,"Site")+": "+name );       // for now only these two. In the future there might be more variables.    defvar( "data_cache_size", 131072, DLOCALE(274, "Cache:Cache size"),    TYPE_INT| VAR_PUBLIC,    DLOCALE(275, "The size of the data cache used to speed up requests "    "for commonly requested files, in KBytes"));       defvar( "data_cache_file_max_size", 256, DLOCALE(276, "Cache:Max file size"),
Roxen.git/server/base_server/configuration.pike:4736:    set ("compat_level", roxen.roxen_ver);    // Note to developers: This setting can be accessed through    // id->conf->query("compat_level") or similar, but observe that that    // call is not entirely cheap. It's therefore advisable to put it in    // a local variable if the compatibility level is to be tested    // frequently. It's perfectly all right to do that in e.g. the    // module start function, since the documentation explicitly states    // that a reload of all modules is necessary to propagate a change    // of the setting.    +  defvar("max_priority", +  Variable.IntChoice(9, ({ 9, 99, 999, 9999 }), 0, +  DLOCALE(1074, "Maximum priority"), +  DLOCALE(1075, "<p>The maximum priority value " +  "for modules.</p>\n" +  "<p>In most cases the default (9) " +  "is fine, but in some configurations " +  "there may be more than 10 modules " +  "of the same type that would otherwise " +  "conflict with each other.</p>\n" +  "<p>Note that existing module priorities " +  "will be scaled accordingly when this " +  "value is changed.</p>")))-> +  set_changed_callback(set_module_max_priority); +     defvar("Log", 1, DLOCALE(28, "Logging: Enabled"),    TYPE_FLAG, DLOCALE(29, "Log requests"));       defvar("LogFormat", #"\   # The default format follows the Combined Log Format, a slight   # extension of the Common Log Format - see   # http://httpd.apache.org/docs/1.3/logs.html#combined   *: $ip-number - $user [$cern-date] \"$method $full-resource $protocol\" $response $length \"$referrer\" \"$user-agent-raw\"      # The following line is an extension of the above that adds useful
Roxen.git/server/base_server/configuration.pike:4885:      <table class='hilite-1stcol'><tbody valign='top'>   <tr><td>$host</td>    <td>The remote host name, or ip number.</td></tr>   <tr><td>$vhost</td>    <td>The Host request-header sent by the client, or '-' if none.</td></tr>   <tr><td>$ip-number</td>    <td>The remote ip number.</td></tr>   <tr><td>$bin-ip-number</td>    <td>The remote host ip as a binary integer number.</td></tr> + <tr><td>$link-layer</td> +  <td>The link layer protocol used for the request if known.</td></tr> + <tr><td>$cipher-suite</td> +  <td>The TLS/SSL cipher suite used for the request if applicable.</td></tr> + <tr><td>$forwarded</td> +  <td>The Forwarded (RFC 7239) headers or X-Forwarded-* headers +  for the request, or '-' if none were provided.</td></tr>   <tr><td>$xff</td>    <td>The remote host name/ip taken from the X-Forwarded-For header, or -  '-' if none is provided. If multiple headers or multiple values are +  '-' if none were provided. If multiple headers or multiple values are    given the first value is logged; this should correspond to the    originating computer.</td></tr>   <tr><td>$method</td>    <td>Request method.</td></tr>   <tr><td>$full-resource</td>    <td>Full requested resource, including any query fields.</td></tr>   <tr><td>$protocol</td>    <td>The protocol used (normally HTTP/1.1).</td></tr> -  + <tr><td>$scheme</td> +  <td>The URL scheme (e.g. http or https) derived from the port handler +  module.</td></tr>   <tr><td>$response</td>    <td>The response code sent.</td></tr>   <tr><td>$bin-response</td>    <td>The response code sent as a binary short number.</td></tr>   <tr><td>$length</td>    <td>The length of the data section of the reply.</td></tr>   <tr><td>$bin-length</td>    <td>Same, but as a 32 bit integer in network byte order.</td></tr>   <tr><td>$queue-length</td>    <td>Number of jobs waiting to be processed by the handler threads
Roxen.git/server/base_server/configuration.pike:4954:   <tr><td>$user</td>    <td>The name of the user, if any is given using the HTTP basic    authentication method.</td></tr>   <tr><td>$user-id</td>    <td>A unique user ID, if cookies are supported, by the client,    otherwise '0'.</td></tr>   <tr><td>$content-type</td>    <td>Resource MIME type.</td></tr>   <tr><td>$cookies</td>    <td>All cookies sent by the browser, separated by ';'.</td></tr> + <tr><td>$set-cookies</td> +  <td>All cookies set by the response, separated by ';'.</td></tr>      <tr><td>$cache-status</td>    <td>A comma separated list of words (containing no whitespace)    that describes how the request got handled by various caches:       <table class='hilite-1stcol'><tbody valign='top'>    <tr><td>protcache</td>    <td>The page is served from the HTTP protocol cache.</td></tr>    <tr><td>protstore</td>    <td>The page is stored in the HTTP protocol cache.</td></tr>
Roxen.git/server/base_server/configuration.pike:5004:    <tr><td>nocache</td>    <td>No hit in any known cache, and not added to the HTTP    protocol cache.</td></tr>    </tbody></table></td></tr>      <tr><td>$eval-status</td>    <td>A comma separated list of words (containing no whitespace)    that describes how the page has been evaluated:       <table class='hilite-1stcol'><tbody valign='top'> +  <tr><td>bad-charset</td> +  <td>Detected invalid charset in declared content-type.</td></tr>    <tr><td>xslt</td>    <td>XSL transform.</td></tr>    <tr><td>rxmlsrc</td>    <td>RXML evaluated from source.</td></tr>    <tr><td>rxmlpcode</td>    <td>RXML evaluated from compiled p-code.</td></tr>    </tbody></table></td></tr>      <tr><td>$protcache-cost</td>    <td>The lookup depth in the HTTP protocol module low-level cache.</td></tr>
Roxen.git/server/base_server/configuration.pike:5087:    all crawled variants and the saving of the entry to the    database.</td></tr>    </tbody></table></dd>   </dl>"), 0, lambda(){ return !query("Log");});       // Make the widget above a bit larger.    getvar ("LogFormat")->rows = 20;    getvar ("LogFormat")->cols = 80;       // FIXME: Mention it is relative to getcwd(). Can not be localized in pike 7.0. -  defvar("LogFile", "$LOGDIR/"+Roxen.short_name(name)+"/Log", +  string log_suffix = ".%y-%m-%d"; +  if (name == "Administration Interface") log_suffix = ".%y-%m"; +  defvar("LogFile", "$LOGDIR/"+Roxen.short_name(name)+"/Log" + log_suffix,    DLOCALE(30, "Logging: Log file"), TYPE_FILE,    DLOCALE(31, "The log file. "    "A file name. Some substitutions will be done:"    "<pre>"    "%y Year (e.g. '1997')\n"    "%m Month (e.g. '08')\n"    "%d Date (e.g. '10' for the tenth)\n"    "%h Hour (e.g. '00')\n"    "%H Hostname\n"    "</pre>")    ,0, lambda(){ return !query("Log");});    -  defvar("LogFileCompressor", "", +  string default_compressor = ""; +  foreach(({ "/bin/bzip2", "/usr/bin/bzip2", "/bin/gzip", "/usr/bin/gzip", }), +  string bin) { +  if (Stdio.is_file(bin)) { +  default_compressor = bin; +  break; +  } +  } +  defvar("LogFileCompressor", default_compressor,    DLOCALE(258, "Logging: Compress log file"), TYPE_STRING,    DLOCALE(259, "Path to a program to compress log files, "    "e.g. <tt>/usr/bin/bzip2</tt> or <tt>/usr/bin/gzip</tt>. "    "<b>Note&nbsp;1:</b> The active log file is never compressed. "    "Log rotation needs to be used using the \"Log file\" "    "filename substitutions "    "(e.g. <tt>$LOGDIR/mysite/Log.%y-%m-%d</tt>). "    "<b>Note&nbsp;2:</b> Compression is limited to scanning files "    "with filename substitutions within a fixed directory (e.g. "    "<tt>$LOGDIR/mysite/Log.%y-%m-%d</tt>, "    "not <tt>$LOGDIR/mysite/%y/Log.%m-%d</tt>)."),    0, lambda(){ return !query("Log");});       defvar("NoLog", ({ }),    DLOCALE(32, "Logging: No Logging for"), TYPE_STRING_LIST|VAR_MORE,    DLOCALE(33, "Don't log requests from hosts with an IP number which "    "matches any of the patterns in this list. This also affects "    "the access counter log."),    0, lambda(){ return !query("Log");});    -  +  defvar("JSONLogEndpoints", ({ "$JSONLOGDIR/" + Roxen.short_name(name) + ".jsonlog" }), +  DLOCALE(1076, "Logging: JSON Logging endpoints"), TYPE_STRING_LIST, +  DLOCALE(1077, "Socket paths and/or IP:ports to bind for log output from this configuration. " +  "$JSONLOGDIR will expand to &lt;configuration directory&gt;/_jsonlog where sockets should be reasonably secure.")) +  ->add_changed_callback(json_log_endpoint_cb); +     defvar("Domain", roxen.get_domain(), DLOCALE(34, "Domain"),    TYPE_STRING|VAR_PUBLIC|VAR_NO_DEFAULT,    DLOCALE(35, "The domain name of the server. The domain name is used "    "to generate default URLs, and to generate email addresses."));       defvar("MyWorldLocation", "",    DLOCALE(36, "Ports: Primary Server URL"), TYPE_URL|VAR_PUBLIC,    DLOCALE(37, #"\   This is the main server URL, where your start page is located. This   setting is for instance used as fallback to generate absolute URLs to
Roxen.git/server/base_server/configuration.pike:5301:    http_compr_exact_mimes = mkmapping(exact_mimes,    ({ 1 }) * sizeof(exact_mimes));    http_compr_main_mimes = mkmapping(main_mimes,    ({ 1 }) * sizeof(main_mimes));    };    defvar("http_compression_mimetypes",    ({ "text/*",    "application/javascript",    "application/x-javascript",    "application/json", -  "application/xhtml+xml" }), +  "application/xhtml+xml", +  "image/x-icon", +  "image/svg+xml" }),    DLOCALE(1002, "Compression: Enabled MIME-types"),    TYPE_STRING_LIST,    DLOCALE(1003, "The MIME types for which to enable compression. The "    "forms \"maintype/*\" and \"maintype/subtype\" are allowed, "    "but globbing on the general form (such as " -  "\"maintype/*subtype\") is not allowed and such globs will " -  "be silently ignored.")) +  "\"maintype/*subtype\" or \"maintype/sub*\") is not allowed " +  "and such globs will be silently ignored."))    ->add_changed_callback(lambda(object v)    { set_mimetypes(v->query());    });    set_mimetypes(query("http_compression_mimetypes"));       defvar("http_compression_min_size", 1024,    DLOCALE(1004, "Compression: Minimum content size"),    TYPE_INT,    DLOCALE(1005, "The minimum file size for which to enable compression. "    "(It might not be worth it to compress a request if it can "
Roxen.git/server/base_server/configuration.pike:5400:    </if>    </emit>    </if>   </emit>   </nooutput><if variable='var.errfile'><eval><insert file='&var.errfile;?orig-url=&page.url:url;&amp;orig-file=&page.virtfile:url;'/></eval></if><else><eval>&modvar.site.404-message:none;</eval></else>", 0, 0, 0 );    }    };       defvar("ZNoSuchFile", NoSuchFileOverride() );    -  defvar("404-message", #"<html> +  defvar("404-message", #"<!DOCTYPE html> + <html>    <head>    <title>404 - Page Not Found</title> -  +  <meta name='viewport' content='width=device-width, initial-scale=1'>    <style> -  .msg { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 12px; -  line-height: 160% } -  .url { font-family: georgia, times, serif; -  font-size: 18px; -  padding-top: 6px; -  padding-bottom: 20px } -  .info { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 10px; -  color: #999999 } +  body { +  background: #f2f1eb; +  color: #000; +  margin: 0 0 49px; +  padding: 30px 30px 0; +  font-family: Verdana, Helvetica, Arial, sans-serif; +  font-size: 12px; +  line-height: 160%; +  } +  html { position: relative; min-height: 100%; } +  a { color: #2331d1; } +  .number { float: left; margin-right: 30px; } +  .header { display: block; margin: 34px 0 30px; max-width: 100%; height: auto; } +  .main { float: left; width: 387px; max-width: 100%; } +  .url { display: block; font-family: Georgia, Times, serif; font-size: 18px; font-weight: normal; margin: 6px 0 20px; } +  .separator { color: #ffbe00; } +  .footer { position: absolute; bottom: 0; height: 49px; } +  .footer img { vertical-align: middle; margin-right: 3px; } +  .info { font-size: 10px; color: #999; }    </style>    </head> - <body bgcolor='#f2f1eb' vlink='#2331d1' alink='#f6f6ff' -  leftmargin='0' rightmargin='0' topmargin='0' bottommargin='0' -  style='margin: 0; padding: 0'> -  - <table border='0' cellspacing='0' cellpadding='0' height='99%'> -  <colgroup> -  <col span='3' /> -  <col width='356' /> -  <col width='0*' /> -  </colgroup> -  <tr> -  <td><img src='/internal-roxen-unit' height='30' /></td> -  </tr><tr> -  <td></td> -  <td><img src='/internal-roxen-404' /></td> -  <td><img src='/internal-roxen-unit' width='30' /></td> -  <td valign='bottom'><img src='/internal-roxen-page-not-found-2' /></td> -  <td></td> -  </tr><tr> -  <td><img src='/internal-roxen-unit' height='30' /></td> -  </tr><tr> -  <td colspan='3'></td> -  <td colspan='2'> -  <div class='msg'>Unable to retrieve</div> -  <div class='url'>&page.virtfile;</div> -  </td> -  </tr><tr> -  <td colspan='3'></td> -  <td width='356'> -  <div class='msg'> -  If you feel this is a configuration error, please contact +  <body> +  <img src='/internal-roxen-404' class='number' alt='404' width='142' height='57'/> +  <div class='main'> +  <img src='/internal-roxen-page-not-found-2' class='header' alt='Page Not Found' width='356' height='23'/> +  <p>Unable to retrieve <b class='url'>&page.virtfile;</b></p> +  <p>If you feel this is a configuration error, please contact    the administrators of this server or the author of the -  <if referrer=''> -  <a href='&client.referrer;'>referring page</a>. -  </if><else> -  referring page. -  </else> -  </div> -  </td> -  <td>&nbsp;</td> -  </tr><tr valign='bottom' height='100%'> -  <td colspan='3'></td> -  <td> -  <img src='/internal-roxen-unit' height='20' /> -  <table border='0' cellspacing='0' cellpadding='0'> -  <tr> -  <td><img src='/internal-roxen-roxen-mini.gif' /></td> -  <td class='info'> -  &nbsp;&nbsp;<b>&roxen.product-name;</b> <font color='#ffbe00'>|</font> +  <if referrer=''><a href='&client.referrer;'>referring page</a>.</if> +  <else>referring page.</else> +  </p> +  <div class='footer'> +  <img src='/internal-roxen-roxen-mini' width='19' height='17'/> +  <span class='info'> +  <strong>&roxen.product-name;</strong> <span class='separator'>|</span>    version &roxen.dist-version; -  </td> -  </tr> -  </table> -  <img src='/internal-roxen-unit' height='15' /> -  </td> -  <td></td> -  </tr> - </table> -  +  </span> +  </div> +  </div> +  <br clear='all'/>    </body> - </html>", + </html> + ",    DLOCALE(58, "No such file message"),    TYPE_TEXT_FIELD|VAR_PUBLIC,    DLOCALE(59, "What to return when there is no resource or file "    "available at a certain location."));          class AuthFailedOverride    {    // compatibility with old config-files.    inherit Variable.Variable;
Roxen.git/server/base_server/configuration.pike:5522:    </if>    </emit>    </if>   </emit>   </nooutput><if variable='var.errfile'><eval><insert file='&var.errfile;?orig-url=&page.url:url;&amp;orig-file=&page.virtfile:url;'/></eval></if><else><eval>&modvar.site.401-message:none;</eval></else>", 0, 0, 0 );    }    };       defvar("ZAuthFailed", AuthFailedOverride() );    -  defvar("401-message", #"<html> +  defvar("401-message", #"<!DOCTYPE html> + <html>    <head>    <title>401 - Authentication Failed</title> -  +  <meta name='viewport' content='width=device-width, initial-scale=1'>    <style> -  .msg { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 12px; -  line-height: 160% } -  .url { font-family: georgia, times, serif; -  font-size: 18px; -  padding-top: 6px; -  padding-bottom: 20px } -  .info { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 10px; -  color: #999999 } +  body { +  background: #f2f1eb; +  color: #000; +  margin: 0 0 49px; +  padding: 30px 30px 0; +  font-family: Verdana, Helvetica, Arial, sans-serif; +  font-size: 12px; +  line-height: 160%; +  } +  html { position: relative; min-height: 100%; } +  a { color: #2331d1; } +  .number { float: left; margin-right: 30px; } +  .header { display: block; margin: 34px 0 30px; max-width: 100%; height: auto; } +  .main { float: left; width: 387px; max-width: 100%; } +  .url { display: block; font-family: Georgia, Times, serif; font-size: 18px; font-weight: normal; margin: 6px 0 20px; } +  .separator { color: #ffbe00; } +  .footer { position: absolute; bottom: 0; height: 49px; } +  .footer img { vertical-align: middle; margin-right: 3px; } +  .info { font-size: 10px; color: #999; }    </style>    </head> - <body bgcolor='#f2f1eb' vlink='#2331d1' alink='#f6f6ff' -  leftmargin='0' rightmargin='0' topmargin='0' bottommargin='0' -  style='margin: 0; padding: 0'> -  - <table border='0' cellspacing='0' cellpadding='0' height='99%'> -  <colgroup> -  <col span='3' /> -  <col width='356' /> -  <col width='0*' /> -  </colgroup> -  <tr> -  <td><img src='/internal-roxen-unit' height='30' /></td> -  </tr><tr> -  <td></td> -  <td><img src='/internal-roxen-401' /></td> -  <td><img src='/internal-roxen-unit' width='30' /></td> -  <td valign='bottom'><img src='/internal-roxen-authentication-failed' /></td> -  <td></td> -  </tr><tr> -  <td><img src='/internal-roxen-unit' height='30' /></td> -  </tr><tr> -  <td colspan='3'></td> -  <td colspan='2'> -  <div class='msg'>Unable to retrieve</div> -  <div class='url'>&page.virtfile;</div> -  </td> -  </tr><tr> -  <td colspan='3'></td> -  <td width='356'> -  <div class='msg'> -  If you feel this is a configuration error, please contact +  <body> +  <img src='/internal-roxen-401' class='number' alt='401' width='142' height='57'/> +  <div class='main'> +  <img src='/internal-roxen-authentication-failed' class='header' alt='Authentication Failed' width='387' height='23'/> +  <p>Unable to retrieve <b class='url'>&page.virtfile;</b></p> +  <p>If you feel this is a configuration error, please contact    the administrators of this server or the author of the -  <if referrer=''> -  <a href='&client.referrer;'>referring page</a>. -  </if><else> -  referring page. -  </else> -  </div> -  </td> -  <td>&nbsp;</td> -  </tr><tr valign='bottom' height='100%'> -  <td colspan='3'></td> -  <td> -  <img src='/internal-roxen-unit' height='20' /> -  <table border='0' cellspacing='0' cellpadding='0'> -  <tr> -  <td><img src='/internal-roxen-roxen-mini.gif' /></td> -  <td class='info'> -  &nbsp;&nbsp;<b>&roxen.product-name;</b> <font color='#ffbe00'>|</font> +  <if referrer=''><a href='&client.referrer;'>referring page</a>.</if> +  <else>referring page.</else> +  </p> +  <div class='footer'> +  <img src='/internal-roxen-roxen-mini' width='19' height='17'/> +  <span class='info'> +  <strong>&roxen.product-name;</strong> <span class='separator'>|</span>    version &roxen.dist-version; -  </td> -  </tr> -  </table> -  <img src='/internal-roxen-unit' height='15' /> -  </td> -  <td></td> -  </tr> - </table> -  +  </span> +  </div> +  </div> +  <br clear='all'/>    </body> - </html>", + </html> + ",    DLOCALE(413, "Authentication failed message"),    TYPE_TEXT_FIELD|VAR_PUBLIC,    DLOCALE(420, "What to return when an authentication attempt failed."));       if (!retrieve ("EnabledModules", this)["config_filesystem#0"]) {    // Do not use a handler queue timeout of the administration    // interface. You most probably don't want to get a 503 in your    // face when you're trying to reconfigure an overloaded server... -  defvar("503-message", #"<html> +  defvar("503-message", #"<!DOCTYPE html> + <html>    <head>    <title>503 - Server Too Busy</title> -  +  <meta name='viewport' content='width=device-width, initial-scale=1'>    <style> -  .header { font-family: arial; -  font-size: 20px; -  line-height: 160% } -  .msg { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 12px; -  line-height: 160% } -  .url { font-family: georgia, times, serif; -  font-size: 18px; -  padding-top: 6px; -  padding-bottom: 20px } -  .info { font-family: verdana, helvetica, arial, sans-serif; -  font-size: 10px; -  color: #999999 } +  body { +  background: #f2f1eb; +  color: #000; +  margin: 0 0 49px; +  padding: 30px 30px 0; +  font-family: Verdana, Helvetica, Arial, sans-serif; +  font-size: 12px; +  line-height: 160%; +  } +  html { position: relative; min-height: 100%; } +  .header { font-size: 20px } +  .main { width: 387px; max-width: 100%; } +  .url { display: block; font-family: Georgia, Times, serif; font-size: 18px; font-weight: normal; margin: 6px 0 20px; } +  .separator { color: #ffbe00; } +  .footer { position: absolute; bottom: 0; height: 49px; } +  .info { font-size: 10px; color: #999; }    </style>    </head> - <body bgcolor='#f2f1eb' vlink='#2331d1' alink='#f6f6ff' -  leftmargin='50' rightmargin='0' topmargin='50' bottommargin='0' -  style='margin: 0; padding: 0'> -  - <table border='0' cellspacing='0' cellpadding='0' height='99%'> -  <colgroup> -  <col span='3' /> -  <col width='356' /> -  <col width='0*' /> -  </colgroup> -  <tr><td height='50'></td></tr> -  <tr> -  <td width='100'></td> -  <td> +  <body> +  <div class='main'>    <div class='header'>503 &mdash; Server Too Busy</div> -  </td> -  </tr> -  <tr> -  <td></td> -  <td> -  <div class='msg'>Unable to retrieve</div> -  <div class='url'>&page.virtfile;</div> -  </td> -  </tr> -  <tr> -  <td></td> -  <td> -  <div class='msg'> -  The server is currently too busy to serve your request. Please try again in a few moments. -  </div> -  </td> -  <td>&nbsp;</td> -  </tr> -  <tr valign='bottom' height='100%'> -  <td></td> -  <td> -  <table border='0' cellspacing='0' cellpadding='0'> -  <tr> -  <td class='info'> -  &nbsp;&nbsp;<b>&roxen.product-name;</b> <font color='#ffbe00'>|</font> +  <p>Unable to retrieve <b class='url'>&page.virtfile;</b></p> +  <p>The server is currently too busy to serve your request. +  Please try again in a few moments. +  </p> +  <div class='footer'> +  <span class='info'> +  <strong>&roxen.product-name;</strong> <span class='separator'>|</span>    version &roxen.dist-version; -  </td> -  </tr> -  </table> -  </td> -  <td></td> -  </tr> - </table> -  +  </span> +  </div> +  </div> +  <br clear='all'/>    </body> - </html>", + </html> + ",    DLOCALE(1048, "Server too busy message"),    TYPE_TEXT_FIELD|VAR_PUBLIC,    DLOCALE(1049, "What to return if the server is too busy. See also "    "\"Handler queue timeout\"."));       defvar("handler_queue_timeout", 30,    DLOCALE(1050, "Handler queue timeout"),    TYPE_INT,    DLOCALE(1051, #"Requests that have been waiting this many seconds on   the handler queue will not be processed. Instead, a 503 error code and the