835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer.
f41b982009-05-07Martin Stjernholm // Copyright © 2001 - 2009, Roxen IS.
835c6c2001-06-17Martin Nilsson 
a896932001-01-03Per Hedbor #include <stat.h> #include <config.h>
f7ea0c2002-02-06Martin Stjernholm #include <module.h>
3e3bab2001-01-19Per Hedbor #include <module_constants.h>
0917d32013-03-04Anders Johansson constant cvs_version="$Id$";
c2d7aa2004-04-13Martin Stjernholm  #ifdef DAV_DEBUG #define DAV_WERROR(X...) werror(X) #else /* !DAV_DEBUG */ #define DAV_WERROR(X...) #endif /* DAV_DEBUG */
08e9b22006-08-16Henrik Grubbström (Grubba) #ifdef VARY_DEBUG #define VARY_WERROR(X...) werror("VARY: " + X) #else #define VARY_WERROR(X...) #endif /* VARY_DEBUG */
c2d7aa2004-04-13Martin Stjernholm // To avoid reference cycles. Set to the Roxen module object by // roxenloader.pike. object Roxen;
a896932001-01-03Per Hedbor 
119a552004-05-07Martin Stjernholm // Externally visible identifiers in this file that shouldn't be added // as global constants by roxenloader.pike.
8cedaa2004-05-07Martin Stjernholm constant ignore_identifiers = (<
119a552004-05-07Martin Stjernholm  "cvs_version", "Roxen", "ignore_identifiers"
8cedaa2004-05-07Martin Stjernholm >);
fc40392008-08-15Martin Stjernholm protected class Variable
a896932001-01-03Per Hedbor { constant is_variable = 1; constant type = "Basic"; string get_warnings(); int get_flags(); void set_flags( int flags ); int check_visibility( RequestID id, int more_mode, int expert_mode, int devel_mode, int initial, int|void variable_in_cfif );
7b1a9a2018-01-29Karl Gustav Sterneberg  this_program set_invisibility_check_callback( function(RequestID,Variable:int) cb );
a896932001-01-03Per Hedbor  function(Variable:void) get_changed_callback( );
7b1a9a2018-01-29Karl Gustav Sterneberg  this_program set_changed_callback( function(Variable:void) cb ); this_program add_changed_callback( function(Variable:void) cb );
a896932001-01-03Per Hedbor  function(RequestID,Variable:int) get_invisibility_check_callback() ; string doc( ); string name( ); string type_hint( ); mixed default_value(); void set_warning( string to ); int set( mixed to );
d5123a2017-10-09Karl Gustav Sterneberg  int low_set( mixed to );
a896932001-01-03Per Hedbor  mixed query(); int is_defaulted(); array(string|mixed) verify_set( mixed new_value ); mapping(string:string) get_form_vars( RequestID id ); mixed transform_from_form( string what ); void set_from_form( RequestID id ); string path(); void set_path( string to ); string render_form( RequestID id, void|mapping additional_args ); string render_view( RequestID id ); } class BasicDefvar { mapping(string:Variable) variables=([]); Variable getvar( string name ); int deflocaledoc( string locale, string variable, string name, string doc, mapping|void translate ); void set(string var, mixed value); int killvar(string var); void setvars( mapping (string:mixed) vars );
d5123a2017-10-09Karl Gustav Sterneberg  Variable defvar(string var, mixed value,
a896932001-01-03Per Hedbor  mapping|string|void|object name,
d5123a2017-10-09Karl Gustav Sterneberg  int|void type, mapping|string|void|object doc_str,
a896932001-01-03Per Hedbor  mixed|void misc, int|function|void not_in_config, mapping|void option_translations); mixed query(string|void var, int|void ok); void definvisvar(string name, mixed value, int type, array|void misc); } class StringFile( string data, mixed|void _st ) { int offset; string _sprintf() { return "StringFile("+strlen(data)+","+offset+")"; } string read(int nbytes) { if(!nbytes) { offset = strlen(data); return data; } string d = data[offset..offset+nbytes-1]; offset += strlen(d); return d; } array stat() { if( _st ) return (array)_st; return ({ 0, strlen(data), time(), time(), time(), 0, 0, 0 }); } void write(mixed ... args) { throw( ({ "File not open for write\n", backtrace() }) ); } void seek(int to) { offset = to; }
c814362009-01-07Stephen R. van den Berg  void set_blocking() { }
a896932001-01-03Per Hedbor } class ModuleInfo { string sname; string filename; int last_checked; int type, multiple_copies;
4fb7672008-05-07Martin Stjernholm  array(string) locked;
ea262c2002-04-17Marcus Wellhardh  mapping(Configuration:int) config_locked;
d5123a2017-10-09Karl Gustav Sterneberg 
a896932001-01-03Per Hedbor  string get_name(); string get_description(); RoxenModule instance( object conf, void|int silent ); void save();
fc7f632005-09-02Martin Stjernholm  void update_with( RoxenModule mod, string what ); // NOTE: Throws strings.
a896932001-01-03Per Hedbor  int init_module( string what ); int find_module( string sn ); int check (void|int force); } class ModuleCopies { mapping copies = ([]); mixed `[](mixed q ) { return copies[q]; } mixed `[]=(mixed q,mixed w ) { return copies[q]=w; } array _indices() { return indices(copies); } array _values() { return values(copies); }
d584002009-10-31Martin Stjernholm  string _sprintf (int flag) { return flag == 'O' && ("ModuleCopies("+sizeof(copies)+")"); }
a896932001-01-03Per Hedbor }
b0c9b82004-04-30Henrik Grubbström (Grubba) // Simulate an import of useful stuff from Parser.XML.Tree.
fc40392008-08-15Martin Stjernholm protected constant SimpleNode = Parser.XML.Tree.SimpleNode; protected constant SimpleRootNode = Parser.XML.Tree.SimpleRootNode; protected constant SimpleHeaderNode = Parser.XML.Tree.SimpleHeaderNode; protected constant SimpleTextNode = Parser.XML.Tree.SimpleTextNode; protected constant SimpleElementNode = Parser.XML.Tree.SimpleElementNode;
b0c9b82004-04-30Henrik Grubbström (Grubba) 
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears DAVLock //!
0ddcae2004-05-04Martin Stjernholm //! Container for information about an outstanding DAV lock. No field //! except @[owner] may change after the object has been created since //! filesystem modules might store this info persistently. //! //! @note //! @[DAVLock] objects might be shared between filesystems but not //! between configurations.
fcfa7b2004-05-14Henrik Grubbström (Grubba) class DAVLock { string locktoken;
8265772004-04-29Martin Stjernholm  //! The lock token given to the client. It may be zero in case there //! only is knowledge that a lock exists but the lock instance has //! been forgotten. This can happen in a filesystem that has some //! way of recording locks but doesn't store the DAV lock tokens.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  string path;
8265772004-04-29Martin Stjernholm  //! Canonical absolute path to the locked resource. Always ends with //! a @expr{"/"@}.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  int(0..1) recursive;
8265772004-04-29Martin Stjernholm  //! @expr{1@} if the lock applies to all resources under @[path], //! @expr{0@} if it applies to @[path] only.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  string|SimpleNode lockscope;
8265772004-04-29Martin Stjernholm  //! The lock scope (RFC 2518 12.7). As a special case, if it only is //! an empty element without attributes then the element name is //! stored as a string. //! //! @note //! RFC 2518 specifies the lock scopes @expr{"DAV:exclusive"@} and //! @expr{"DAV:shared"@}.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  string|SimpleNode locktype;
8265772004-04-29Martin Stjernholm  //! The lock type (RFC 2518 12.8). As a special case, if it only is //! an empty element without attributes then the element name is //! stored as a string. //! //! @note //! RFC 2518 only specifies the lock type @expr{"DAV:write"@}.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  int(0..) expiry_delta; //! Idle time before this lock expires. //! //! As a special case, if the value is @expr{0@} (zero), the lock //! has infinite duration. array(SimpleNode) owner;
8265772004-04-29Martin Stjernholm  //! The owner identification (RFC 2518 12.10), or zero if unknown.
071d472004-05-06Martin Stjernholm  //! More precisely, it's the children of the @expr{"DAV:owner"@} //! element.
4908322004-04-29Martin Stjernholm  //! //! @[RoxenModule.lock_file] may set this if it's zero, otherwise //! it shouldn't change.
fcfa7b2004-05-14Henrik Grubbström (Grubba)  int(0..) expiry_time; //! Absolute time when this lock expires. //! //! As a special case, if the value is @expr{0@} (zero), the lock //! has infinite duration.
ddb1b62018-03-27Henrik Grubbström (Grubba)  int(0..1) is_file; //! @expr{1@} if @[path] refers to a file.
fc40392008-08-15Martin Stjernholm  protected void create(string locktoken, string path, int(0..1) recursive, string|SimpleNode lockscope, string|SimpleNode locktype, int(0..) expiry_delta, array(SimpleNode) owner)
fcfa7b2004-05-14Henrik Grubbström (Grubba)  { DAVLock::locktoken = locktoken; DAVLock::path = path; DAVLock::recursive = recursive; DAVLock::lockscope = lockscope; DAVLock::locktype = locktype; DAVLock::expiry_delta = expiry_delta; DAVLock::owner = owner; if (expiry_delta) { if (expiry_delta < 0) error("Negative expiry delta!\n");
1bb72e2006-01-11Martin Stjernholm  expiry_time = time() + expiry_delta;
fcfa7b2004-05-14Henrik Grubbström (Grubba)  } }
b0c9b82004-04-30Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  //! Returns a DAV:activelock @[Parser.XML.Tree.SimpleNode] structure
b0c9b82004-04-30Henrik Grubbström (Grubba)  //! describing the lock.
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode get_xml()
b0c9b82004-04-30Henrik Grubbström (Grubba)  {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode res = SimpleElementNode("DAV:activelock", ([]))-> add_child(SimpleElementNode("DAV:locktype", ([]))-> add_child(stringp(locktype)? SimpleElementNode(locktype, ([])):locktype))-> add_child(SimpleElementNode("DAV:lockscope", ([]))-> add_child(stringp(lockscope)? SimpleElementNode(lockscope, ([])):lockscope))-> add_child(SimpleElementNode("DAV:depth", ([]))-> add_child(recursive? SimpleTextNode("Infinity"):SimpleTextNode("0")));
b0c9b82004-04-30Henrik Grubbström (Grubba)  if (owner) {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode node; res->add_child(node = SimpleElementNode("DAV:owner", ([]))); node->replace_children(owner);
b0c9b82004-04-30Henrik Grubbström (Grubba)  }
fcfa7b2004-05-14Henrik Grubbström (Grubba)  if (expiry_delta) { res->add_child(SimpleElementNode("DAV:timeout", ([]))-> add_child(SimpleTextNode(sprintf("Second-%d", expiry_delta)))); } else { res->add_child(SimpleElementNode("DAV:timeout", ([]))-> add_child(SimpleTextNode("Infinite"))); }
b0c9b82004-04-30Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  res->add_child(SimpleElementNode("DAV:locktoken", ([]))-> add_child(SimpleElementNode("DAV:href", ([]))-> add_child(SimpleTextNode(locktoken))));
b0c9b82004-04-30Henrik Grubbström (Grubba)  return res; }
a720412004-05-14Martin Stjernholm 
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
d584002009-10-31Martin Stjernholm 
fc40392008-08-15Martin Stjernholm  protected string _sprintf (int flag)
a720412004-05-14Martin Stjernholm  { return flag == 'O' &&
d584002009-10-31Martin Stjernholm  sprintf ("DAVLock(%O on %O, %s, %s%s)" + OBJ_COUNT, locktoken, path,
a720412004-05-14Martin Stjernholm  recursive ? "rec" : "norec", lockscope == "DAV:exclusive" ? "excl" : lockscope == "DAV:shared" ? "shared" : sprintf ("%O", lockscope), locktype == "DAV:write" ? "" : sprintf (", %O", locktype)); }
4c87da2004-04-28Henrik Grubbström (Grubba) }
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears Configuration
091e702004-05-19Henrik Grubbström (Grubba) //! Configuration information for a site.
7a67792006-07-05Henrik Grubbström (Grubba) //! @seealso //! @[configuration]
7f66d82001-08-24Martin Nilsson class Configuration
a896932001-01-03Per Hedbor { inherit BasicDefvar; constant is_configuration = 1;
715d5e2009-10-13Martin Stjernholm  mapping(string:int(1..1)) enabled_modules = ([]);
a896932001-01-03Per Hedbor  mapping(string:array(int)) error_log=([]);
8cf9f92015-08-12Marcus Agehall  // The Configration logger class - allows overrides of the SocketLogger if needed. class ConfigurationLogger { inherit Logger.SocketLogger; } // Logger instance for this configuration.
ef196a2015-10-16Marcus Agehall  object json_logger;
8cf9f92015-08-12Marcus Agehall 
a896932001-01-03Per Hedbor #ifdef PROFILE
091e702004-05-19Henrik Grubbström (Grubba)  mapping(string:array(int)) profile_map = ([]);
a896932001-01-03Per Hedbor #endif class DataCache { void flush();
ac55dd2005-12-05Henrik Grubbström (Grubba)  void expire_entry(string url, RequestID id);
0f06502005-12-02Henrik Grubbström (Grubba)  void set(string url, string data, mapping meta, int expire, RequestID id); array(string|mapping(string:mixed)) get(string url, RequestID id);
a896932001-01-03Per Hedbor  void init_from_variables( ); }; object throttler; RoxenModule types_module; RoxenModule dir_module; function types_fun; string name; int inited; // Protocol specific statistics. int requests, sent, hsent, received;
9e03692010-05-19Fredrik Noring  int request_num_runs_001s; int request_num_runs_005s; int request_num_runs_015s; int request_num_runs_05s; int request_num_runs_1s; int request_num_runs_5s; int request_num_runs_15s; int request_acc_time; int handle_num_runs_001s; int handle_num_runs_005s; int handle_num_runs_015s; int handle_num_runs_05s; int handle_num_runs_1s; int handle_num_runs_5s; int handle_num_runs_15s; int handle_acc_time; int queue_num_runs_001s; int queue_num_runs_005s; int queue_num_runs_015s; int queue_num_runs_05s; int queue_num_runs_1s; int queue_num_runs_5s; int queue_num_runs_15s; int queue_acc_time;
4586f52009-10-31Martin Stjernholm  void add_module_pre_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb); void delete_module_pre_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb); void add_module_post_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb); void delete_module_post_callback (string mod_name, string func, function(RoxenModule,mixed...:void) cb); //! These functions add or delete callbacks which are called before //! or after specific functions in @[RoxenModule] instances. In //! particular this can be used to add callbacks to call before or //! after @[RoxenModule.start] in specific modules. //! //! A callback is never added multiple times on the same callback //! list. Callbacks in destructed objects are automatically pruned //! from the callback lists. //! //! @param mod_name //! The base identifier (i.e. without the @expr{"#n"@} part) of the //! module that the callback should be registered for. If it is zero //! then the callback is called for any module in the configuration. //! This is a string to be able to register callbacks for modules //! that are not yet loaded. //! //! @param func //! The function in the module to register the callback for. The //! only supported functions are currently @expr{"start"@} and //! @expr{"stop"@}. //! //! @param cb //! The callback to add or delete from the callback list. If a //! callback is deleted then @[mod_name] and @[func] must be the //! same as when it was registered. //! //! The callback get the @[RoxenModule] instance as the first //! argument. The remaining arguments are the same as the //! corresponding function in @[RoxenModule] gets called with. //! //! Note that post callbacks won't get called if the real //! @[RoxenModule] function failed with an exception.
a896932001-01-03Per Hedbor  function(string:int) log_function; DataCache datacache;
d5123a2017-10-09Karl Gustav Sterneberg 
a896932001-01-03Per Hedbor  int get_config_id(); string get_doc_for( string region, string variable ); string query_internal_location(RoxenModule|void mod); string query_name(); string comment();
0e15572001-02-23Martin Stjernholm  void unregister_urls(); void stop(void|int asynch);
c8bfed2003-06-02Henrik Grubbström (Grubba)  string|array(string) type_from_filename( string file, int|void to, string|void myext );
a896932001-01-03Per Hedbor 
c109fc2001-07-21Martin Stjernholm  string get_url();
1b52c42010-07-12Martin Stjernholm  string get_host();
c109fc2001-07-21Martin Stjernholm 
a896932001-01-03Per Hedbor  array (RoxenModule) get_providers(string provides); RoxenModule get_provider(string provides); array(mixed) map_providers(string provides, string fun, mixed ... args); mixed call_provider(string provides, string fun, mixed ... args); array(function) file_extension_modules(string ext); array(function) url_modules(); mapping api_functions(void|RequestID id); array(function) logger_modules(); array(function) last_modules(); array(function) first_modules(); array location_modules(); array(function) filter_modules(); void init_log_file(); int|mapping check_security(function|object a, RequestID id, void|int slevel); void invalidate_cache(); void clear_memory_caches(); string examine_return_mapping(mapping m);
da60482018-03-27Henrik Grubbström (Grubba)  mapping(string:DAVLock) find_locks(string path, int(-1..1) recursive, int(0..1) exclude_shared, RequestID id);
7ee0b02018-03-27Henrik Grubbström (Grubba)  mapping(string:mixed)|int(-1..0) check_locks(string path, int(0..1) recursive, RequestID id);
a720412004-05-14Martin Stjernholm  mapping(string:mixed) unlock_file(string path, DAVLock lock, RequestID|int(0..0) id);
1894be2004-05-14Henrik Grubbström (Grubba)  int expire_locks(RequestID id); void refresh_lock(DAVLock lock);
21d18b2004-05-14Henrik Grubbström (Grubba)  mapping(string:mixed)|DAVLock lock_file(string path, int(0..1) recursive, string lockscope, string locktype, int(0..) expiry_delta, array(Parser.XML.Tree.Node) owner, RequestID id);
a896932001-01-03Per Hedbor  mapping|int(-1..0) low_get_file(RequestID id, int|void no_magic); mapping get_file(RequestID id, int|void no_magic, int|void internal_get); array(string) find_dir(string file, RequestID id, void|int(0..1) verbose);
ce64272001-01-14Martin Nilsson  array(int)|object(Stdio.Stat) stat_file(string file, RequestID id);
d720082004-04-19Martin Stjernholm  array open_file(string fname, string mode, RequestID id, void|int ig, void|int rc);
a896932001-01-03Per Hedbor  mapping(string:array(mixed)) find_dir_stat(string file, RequestID id); array access(string file, RequestID id); string real_file(string file, RequestID id); int|string try_get_file(string s, RequestID id, int|void status, int|void nocache,
6a613a2002-06-17Anders Johansson  int|void not_internal, mapping|void result_mapping);
2fbed12001-09-13Martin Nilsson  int(0..1) is_file(string virt_path, RequestID id, int(0..1)|void internal);
b83ae22009-04-21Martin Stjernholm  int start(int num);
a896932001-01-03Per Hedbor  void save_me(); int save_one( RoxenModule o ); RoxenModule reload_module( string modname );
d5123a2017-10-09Karl Gustav Sterneberg  RoxenModule enable_module( string modname, RoxenModule|void me, ModuleInfo|void moduleinfo,
7902be2002-04-09Marcus Wellhardh  int|void nostart, int|void nosave );
d5123a2017-10-09Karl Gustav Sterneberg  void call_start_callbacks( RoxenModule me, ModuleInfo moduleinfo,
4fb7672008-05-07Martin Stjernholm  ModuleCopies module, void|int newly_added);
d5123a2017-10-09Karl Gustav Sterneberg  void call_low_start_callbacks( RoxenModule me, ModuleInfo moduleinfo,
a896932001-01-03Per Hedbor  ModuleCopies module ); int disable_module( string modname, int|void nodest ); int add_modules( array(string) mods, int|void now ); RoxenModule find_module(string name);
850c282001-01-10Per Hedbor #if ROXEN_COMPAT < 2.2 Sql.Sql sql_cache_get(string what);
a31e392006-09-18Martin Stjernholm  Sql.Sql sql_connect(string db, void|string charset);
850c282001-01-10Per Hedbor #endif
a896932001-01-03Per Hedbor  void enable_all_modules(); void low_init(void|int modules_already_enabled); string parse_rxml(string what, RequestID id, void|Stdio.File file, void|mapping defines ); void add_parse_module (RoxenModule mod); void remove_parse_module (RoxenModule mod); string real_file(string a, RequestID b);
6495292001-01-19Per Hedbor  mapping authenticate_throw( RequestID id, string realm, UserDB|void database, AuthModule|void method); User authenticate( RequestID id, UserDB|void database, AuthModule|void method ); array(AuthModule) auth_modules(); array(UserDB) user_databases();
e3f4662001-01-19Per Hedbor  AuthModule find_auth_module( string name ); UserDB find_user_database( string name );
d584002009-10-31Martin Stjernholm 
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
d584002009-10-31Martin Stjernholm  protected string _sprintf (int flag)
a896932001-01-03Per Hedbor  {
d584002009-10-31Martin Stjernholm  return flag == 'O' && ("Configuration("+name+")" + OBJ_COUNT);
a896932001-01-03Per Hedbor  } }
7f66d82001-08-24Martin Nilsson //! @appears Protocol
d5123a2017-10-09Karl Gustav Sterneberg class Protocol
a896932001-01-03Per Hedbor { inherit BasicDefvar;
a9fa0d2001-07-21Martin Stjernholm 
a896932001-01-03Per Hedbor  constant name = "unknown";
a9fa0d2001-07-21Martin Stjernholm  //! Name used for internal identification. constant prot_name = "unknown"; //! Name of the protocol as seen in the protocol part of a url.
a896932001-01-03Per Hedbor  constant supports_ipless = 0; constant requesthandlerfile = ""; constant default_port = 4711; int bound; int refs; program requesthandler; string path; int port; string ip; array(string) sorted_urls = ({}); mapping(string:mapping) urls = ([]);
ea65f72001-07-21Martin Stjernholm  mapping(Configuration:mapping) conf_data = ([]);
a896932001-01-03Per Hedbor  void ref(string url, mapping data); void unref(string url);
d5123a2017-10-09Karl Gustav Sterneberg  Configuration find_configuration_for_url( string url, RequestID id,
a896932001-01-03Per Hedbor  int|void no_default ); string get_key(); void save(); void restore(); };
e0ffe32001-02-05Per Hedbor class FakedVariables( mapping real_variables ) {
e085872005-11-24Henrik Grubbström (Grubba)  // FIXME: _get_iterator()
fc40392008-08-15Martin Stjernholm  protected array _indices()
e0ffe32001-02-05Per Hedbor  { return indices( real_variables ); }
fc40392008-08-15Martin Stjernholm  protected array _values()
e0ffe32001-02-05Per Hedbor  { return map( _indices(), `[] ); }
fc40392008-08-15Martin Stjernholm  protected mixed fix_value( mixed what )
e0ffe32001-02-05Per Hedbor  { if( !what ) return what; if( !arrayp(what) ) return what; // huh if( sizeof( what ) == 1 ) return what[0]; return what*"\0"; }
fc40392008-08-15Martin Stjernholm  protected mixed `[]( string ind ) {
e0ffe32001-02-05Per Hedbor  return fix_value( real_variables[ ind ] ); }
fc40392008-08-15Martin Stjernholm  protected mixed `->(string ind ) {
e0ffe32001-02-05Per Hedbor  return `[]( ind ); }
fc40392008-08-15Martin Stjernholm  protected mixed `[]=( string ind, mixed what ) {
e0ffe32001-02-05Per Hedbor  real_variables[ ind ] = ({ what }); return what; }
fc40392008-08-15Martin Stjernholm  protected mixed `->=(string ind, mixed what ) {
e0ffe32001-02-05Per Hedbor  return `[]=( ind,what ); }
fc40392008-08-15Martin Stjernholm  protected mixed _m_delete( mixed what ) {
91d3c32001-03-12Martin Nilsson // report_debug(" _m_delete( %O )\n", what );
e0ffe32001-02-05Per Hedbor  return fix_value( m_delete( real_variables, what ) ); }
fc40392008-08-15Martin Stjernholm  protected int _equal( mixed what ) {
e0ffe32001-02-05Per Hedbor  return `==(what); }
fc40392008-08-15Martin Stjernholm  protected int `==( mixed what ) {
e0ffe32001-02-05Per Hedbor  if( mappingp( what ) && (real_variables == what) ) return 1; }
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
d584002009-10-31Martin Stjernholm 
fc40392008-08-15Martin Stjernholm  protected string _sprintf( int f )
e0ffe32001-02-05Per Hedbor  { switch( f ) { case 'O':
d584002009-10-31Martin Stjernholm  return sprintf( "FakedVariables(%O)" + OBJ_COUNT, real_variables );
e0ffe32001-02-05Per Hedbor  default: return sprintf( sprintf("%%%c", f ), real_variables ); } }
fc40392008-08-15Martin Stjernholm  protected this_program `|( mapping what )
e0ffe32001-02-05Per Hedbor  { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); }
fc40392008-08-15Martin Stjernholm  protected this_program `+=( mapping what )
e0ffe32001-02-05Per Hedbor  { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); }
fc40392008-08-15Martin Stjernholm  protected this_program `+( mapping what )
e0ffe32001-02-05Per Hedbor  { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); }
e734d52001-02-18Mirar (Pontus Hagland) 
fc40392008-08-15Martin Stjernholm  protected mapping cast(string to)
e734d52001-02-18Mirar (Pontus Hagland)  { if (to[..6]=="mapping")
3ce5922001-02-18Mirar (Pontus Hagland)  { array v=indices(real_variables); return mkmapping(v,map(v,`[]));
d5123a2017-10-09Karl Gustav Sterneberg  }
e734d52001-02-18Mirar (Pontus Hagland)  error("can't cast to %O\n",to); }
e0ffe32001-02-05Per Hedbor }
13b3532002-10-25Martin Stjernholm class PrefLanguages //! @appears PrefLanguages //! Support for language preferences. This object is typically //! accessed through @tt{id->misc->pref_languages@}. { int decoded=0; int sorted=0;
f081a72009-02-16Jonas Wallden  // array(string) subtags=({});
13b3532002-10-25Martin Stjernholm  array(string) languages=({}); array(float) qualities=({});
14ca442009-02-19Jonas Wallden  // Default action for delayed vary handling is to depend on the // Accept-Language header since that is init_pref_languages() uses. // If Preferred Language Analyzer is loaded it will update this array // based on what request properties it bases its language decision on. // Only when the request processing completes will we know all language // forks and can then install a vary callback. array(array(string|array)) delayed_vary_actions = ({ ({ "accept-language", 0 }) }); multiset(string) known_langs = (< >);
d5123a2017-10-09Karl Gustav Sterneberg 
fc40392008-08-15Martin Stjernholm  protected string _sprintf(int c, mapping|void attrs)
8766282004-05-05Marcus Wellhardh  {
d584002009-10-31Martin Stjernholm  return c == 'O' && sprintf("PrefLanguages(%O)", get_languages());
8766282004-05-05Marcus Wellhardh  }
14ca442009-02-19Jonas Wallden  void register_known_language_forks(multiset(string) langs, void|RequestID id) { // Accumulate all known languages for this page. If the entry is // placed in the protocol cache we will be able to construct a vary // callback that can determine a proper cache key. known_langs |= langs; // If caller provides the current RequestID we can also propagate // the language set in the parent request. if (RequestID parent_id = id && id->misc->orig) { PrefLanguages parent_pl = parent_id->misc->pref_languages; parent_pl->register_known_language_forks(langs, parent_id); } } multiset(string) get_known_language_forks() { return known_langs; }
d5123a2017-10-09Karl Gustav Sterneberg 
14ca442009-02-19Jonas Wallden  void finalize_delayed_vary(RequestID id) { // No point in installing protocol cache arbitration when the requested // page isn't multilingual. if (sizeof(known_langs) < 2) return;
d5123a2017-10-09Karl Gustav Sterneberg 
88cca82009-02-19Jonas Wallden  VARY_WERROR("finalize_delayed_vary: actions: %O\n", delayed_vary_actions);
14ca442009-02-19Jonas Wallden  foreach (delayed_vary_actions, array(string|array(string)) item) { function cb; switch (item[0]) { case "accept-language": cb = Roxen->get_lang_vary_cb(known_langs, "accept-language"); id->register_vary_callback("accept-language", cb); break;
d5123a2017-10-09Karl Gustav Sterneberg 
e7a5182009-04-17Jonas Wallden  case "cookie":
14ca442009-02-19Jonas Wallden  string lang_cookie = item[1];
e7a5182009-04-17Jonas Wallden  cb = Roxen->get_lang_vary_cb(known_langs, "cookie", lang_cookie); id->register_vary_callback("cookie", cb);
14ca442009-02-19Jonas Wallden  break;
d5123a2017-10-09Karl Gustav Sterneberg 
14ca442009-02-19Jonas Wallden  case "host": id->register_vary_callback("host"); break;
d5123a2017-10-09Karl Gustav Sterneberg 
14ca442009-02-19Jonas Wallden  case "variable": case "prestate": case "path": // All three are implicitly managed in the protocol cache since // they are part of the page URL. break; } // Don't depend on further properties as soon as one of the known // languages have appeared (since that language will be the one // shown in the page). if (array(string) property_langs = item[-1]) { foreach (property_langs, string lang) if (known_langs[lang]) return; } } }
d5123a2017-10-09Karl Gustav Sterneberg 
13b3532002-10-25Martin Stjernholm  array(string) get_languages() { sort_lang(); return languages; } string get_language() { if(!languages || !sizeof(languages)) return 0; sort_lang(); return languages[0]; } array(float) get_qualities() { sort_lang(); return qualities; } float get_quality() { if(!qualities || !sizeof(qualities)) return 0.0; sort_lang(); return qualities[0]; } void set_sorted(array(string) lang, void|array(float) q) { languages=lang; if(q && sizeof(q)==sizeof(lang)) qualities=q; else qualities=({1.0})*sizeof(lang); sorted=1; decoded=1; }
d5123a2017-10-09Karl Gustav Sterneberg 
13b3532002-10-25Martin Stjernholm  void sort_lang() { if(sorted && decoded) return; array(float) q;
f081a72009-02-16Jonas Wallden  array(string) s=reverse(languages)-({""}) /*, u=({})*/;
d5123a2017-10-09Karl Gustav Sterneberg 
13b3532002-10-25Martin Stjernholm  if(!decoded) { q=({}); s=Array.map(s, lambda(string x) { float n=1.0; string sub=""; sscanf(lower_case(x), "%s;q=%f", x, n); if(n==0.0) return ""; sscanf(x, "%s-%s", x, sub);
2d722b2006-04-19Marcus Wellhardh  if(x == "" ) return "";
13b3532002-10-25Martin Stjernholm  q+=({n});
f081a72009-02-16Jonas Wallden  // u+=({sub});
13b3532002-10-25Martin Stjernholm  return x; }); s-=({""}); decoded=1; } else q=reverse(qualities);
f081a72009-02-16Jonas Wallden  sort(q, s /*, u */);
13b3532002-10-25Martin Stjernholm  languages=reverse(s); qualities=reverse(q);
f081a72009-02-16Jonas Wallden  // subtags=reverse(u); // Remove duplicate entries caused by varying subtags multiset(string) known_langs = (< >); for (int i = 0; i < sizeof(languages); i++) { string l = languages[i]; if (known_langs[l]) { languages[i] = 0; qualities[i] = 0; } else { known_langs[l] = 1; } } languages -= ({ 0 }); qualities -= ({ 0 });
d5123a2017-10-09Karl Gustav Sterneberg 
13b3532002-10-25Martin Stjernholm  sorted=1; } }
48b4112003-02-05Jonas Wallden 
a86aa82009-11-04Martin Stjernholm typedef function(object,mixed...:void) CacheActivationCB;
21e7f52011-01-11Martin Jonsson typedef function(object,mixed...:void) CacheDestructionCB;
48b4112003-02-05Jonas Wallden 
b192532004-05-25Martin Stjernholm class CacheKey //! @appears CacheKey //! //! Used as @expr{id->misc->cachekey@}. Every request that might be
e97d412007-09-20Henrik Grubbström (Grubba) //! cacheable has an instance, and the protocol cache which stores the
b192532004-05-25Martin Stjernholm //! result of the request checks that this object still exists before //! using the cache entry. Thus other subsystems that provide data to
3ef51a2008-01-08Martin Stjernholm //! the result can keep track of this object and use //! @[roxen.invalidate] on it whenever they change state in a way that //! invalidates the previous result.
b192532004-05-25Martin Stjernholm //! //! Those data providers should however not store this object //! directly, but instead call @[add_activation_cb]. That avoids //! unnecessary garbage (see below). //! //! A cache implementation must call @[activate] before storing the //! cache key. At that point the functions registered with //! @[add_activation_cb] are called, and the key gets added in the //! internal structures of the data providers. This avoids registering //! cache keys for results that never get cached, which would //! otherwise produce excessive amounts of garbage objects in those //! internal structures. Note that other code might need to call //! @[activate] to process the callbacks, so a key being active //! doesn't necessarily mean it's used in a cache. //! //! @note
3ef51a2008-01-08Martin Stjernholm //! @[destruct] is traditionally used on cache keys to invalidate //! them. That is no longer recommended since it doesn't allow //! @[TristateCacheKey] to operate correctly. Nowadays //! @[roxen.invalidate] should always be used, regardless whether it's //! a tristate cache key or not. //! //! @note
b192532004-05-25Martin Stjernholm //! These objects can be destructed asynchronously; all accesses for //! things inside them have to rely on the interpreter lock, and code //! can never assume that @expr{id->misc->cachekey@} exists to begin
3ef51a2008-01-08Martin Stjernholm //! with.
b192532004-05-25Martin Stjernholm {
911a332008-01-16Martin Stjernholm #ifdef ID_CACHEKEY_DEBUG
b192532004-05-25Martin Stjernholm  RoxenDebug.ObjectMarker __marker = RoxenDebug.ObjectMarker (this);
d584002009-10-31Martin Stjernholm #else
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
b192532004-05-25Martin Stjernholm #endif
fc40392008-08-15Martin Stjernholm  protected array(array(CacheActivationCB|array)) activation_cbs;
b192532004-05-25Martin Stjernholm  // Functions to call when the cache key is activated, i.e. stored // together with some result in a cache. Zero when the key already // is active.
21e7f52011-01-11Martin Jonsson  protected array(array(CacheDestructionCB|array)) destruction_cbs; // Functions to call when the cache key is destructed.
fc40392008-08-15Martin Stjernholm  protected void create (void|int activate_immediately)
b192532004-05-25Martin Stjernholm  { if (!activate_immediately) activation_cbs = ({});
21e7f52011-01-11Martin Jonsson  destruction_cbs = ({});
b192532004-05-25Martin Stjernholm  }
956b852010-05-07Martin Stjernholm  protected void destroy() {
fc78ec2011-01-21Martin Jonsson  foreach (destruction_cbs, [CacheDestructionCB cb, int remove_at_activation, array args]) {
60f63a2011-01-21Martin Jonsson  if (cb) cb (this, @args);
21e7f52011-01-11Martin Jonsson  } #if 0
956b852010-05-07Martin Stjernholm  if (activation_cbs) werror ("%O: activation list size at destroy: %d\n", this, sizeof (activation_cbs)); #endif
21e7f52011-01-11Martin Jonsson  }
fc78ec2011-01-21Martin Jonsson  void add_destruction_cb (CacheDestructionCB cb, int remove_at_activation, mixed... args)
21e7f52011-01-11Martin Jonsson  //! Register a callback that will be called when this cache key is
fc78ec2011-01-21Martin Jonsson  //! destructed. If @[remove_at_activation] is set, the callback will //! be removed when this key is activated. Also, attempts to add //! callbacks with the @[remove_at_activation] flag set when the key //! is already active will be silently ignored. See also //! @[add_activation_cb].
21e7f52011-01-11Martin Jonsson  {
fc78ec2011-01-21Martin Jonsson  if (activation_cbs || !remove_at_activation) destruction_cbs += ({({ cb, remove_at_activation, args })});
21e7f52011-01-11Martin Jonsson  }
956b852010-05-07Martin Stjernholm 
b192532004-05-25Martin Stjernholm  void add_activation_cb (CacheActivationCB cb, mixed... args) //! Register a callback that will be called if and when this cache //! key is used in a cache, i.e. is activated. The callback gets //! this object followed by @[args] as arguments and should do //! whatever bookkeeping necessary to keep track of the cache key so //! that it can be destructed. //! //! If this cache key already is active then @[cb] is called right //! away. //! //! The registered callbacks will be called in the same order they //! are added. //! //! @note //! Cache keys can be destructed at any time, and @[cb] might get //! called with an already destructed object. //! //! @note
a86aa82009-11-04Martin Stjernholm  //! @[cb] should also be prepared to get a destruct key that isn't a //! @[CacheKey] as first argument - see @[merge_activation_list]. //! //! @note
b192532004-05-25Martin Stjernholm  //! Take care to avoid cyclic refs when the activation callback is //! registered. This object should e.g. not be among @[args], and
5c5d652004-05-25Martin Stjernholm  //! @[cb] should not be a lambda that contains references to this
b192532004-05-25Martin Stjernholm  //! object. {
faa89d2012-05-07Martin Stjernholm  // vvv Relying on the interpreter lock from here. if (this && activation_cbs) {
b192532004-05-25Martin Stjernholm  activation_cbs += ({({cb, args})});
faa89d2012-05-07Martin Stjernholm  // ^^^ Relying on the interpreter lock to here. }
a86aa82009-11-04Martin Stjernholm  else { #if 0 werror ("Key %O already active - calling %O(%{%O, %})\n", this, cb, args); #endif
b192532004-05-25Martin Stjernholm  cb (this, @args);
a86aa82009-11-04Martin Stjernholm  } } void add_activation_list (array(array(CacheActivationCB|array)) cbs) // For internal use by merge_activation_list. {
faa89d2012-05-07Martin Stjernholm  // vvv Relying on the interpreter lock from here. if (this && activation_cbs) {
a86aa82009-11-04Martin Stjernholm  activation_cbs += cbs;
faa89d2012-05-07Martin Stjernholm  // ^^^ Relying on the interpreter lock to here. }
a86aa82009-11-04Martin Stjernholm  else foreach (cbs, [CacheActivationCB cb, array args]) { #if 0 werror ("Key %O already active - calling %O(%{%O, %})\n", this, cb, args); #endif cb (this, @args); } } int merge_activation_list (object merge_target) //! Merges the activation list in this key into @[merge_target]. //! //! If @[merge_target] already is active or if it isn't capable of //! delayed activation then our activation list is executed for it //! right away. If this key already is active then zero is returned //! and nothing is done. {
faa89d2012-05-07Martin Stjernholm  // vvv Relying on the interpreter lock from here. if (array(array(CacheActivationCB|array)) cbs = this && activation_cbs) {
a86aa82009-11-04Martin Stjernholm  if (objectp (merge_target) && merge_target->add_activation_list) { merge_target->add_activation_list (cbs); // ^^^ Relying on the interpreter lock up to this call. } else foreach (cbs, [CacheActivationCB cb, array args]) { #if 0 werror ("Merge from %O - activating key %O: Calling %O(%{%O, %})\n", this, merge_target, cb, args); #endif cb (merge_target, @args); } return 1; } return 0;
b192532004-05-25Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  int activate()
b192532004-05-25Martin Stjernholm  //! Activate the cache key. This must be called when the key is
9750692004-06-01Martin Stjernholm  //! stored in a cache. Return nonzero if any callbacks got called.
b192532004-05-25Martin Stjernholm  {
faa89d2012-05-07Martin Stjernholm  // vvv Relying on the interpreter lock from here. if (array(array(CacheActivationCB|array)) cbs = this && activation_cbs) {
b192532004-05-25Martin Stjernholm  activation_cbs = 0;
faa89d2012-05-07Martin Stjernholm  // ^^^ Relying on the interpreter lock to here.
fc78ec2011-01-21Martin Jonsson  array _destruction_cbs = destruction_cbs; // Remove destruction callbacks set to be removed at activation. _destruction_cbs = filter (_destruction_cbs, lambda (array(CacheDestructionCB|int|array) arg) { return !arg[1]; }); if (this) destruction_cbs = _destruction_cbs;
04c5aa2007-01-17Martin Stjernholm  foreach (cbs, [CacheActivationCB cb, array args]) { #if 0 werror ("Activating key %O: Calling %O(%{%O, %})\n", this, cb, args); #endif
b192532004-05-25Martin Stjernholm  cb (this, @args);
04c5aa2007-01-17Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  return sizeof (cbs);
b192532004-05-25Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  return 0;
b192532004-05-25Martin Stjernholm  } int activated() //! Returns nonzero iff the key is activated. {
51115e2012-05-08Martin Stjernholm  return this && // Relying on the interpreter lock here. !activation_cbs;
b192532004-05-25Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  int activate_if_necessary() // Activate the key only if any activation cbs are installed. This // is a kludge to play safe in situations early in the request path // where we don't want to activate the key and where there aren't // any outstanding callbacks in the common case with a direct // request but might still be in the recursive case. Ignore if you // can.
b192532004-05-25Martin Stjernholm  {
faa89d2012-05-07Martin Stjernholm  // vvv Relying on the interpreter lock from here. if (array(array(CacheActivationCB|array)) cbs = this && activation_cbs) {
9750692004-06-01Martin Stjernholm  if (sizeof (cbs)) {
faa89d2012-05-07Martin Stjernholm  activation_cbs = 0; // ^^^ Relying on the interpreter lock to here.
04c5aa2007-01-17Martin Stjernholm  foreach (cbs, [CacheActivationCB cb, array args]) { #if 0 werror ("Activating key %O: Calling %O(%{%O, %})\n", this, cb, args); #endif
9750692004-06-01Martin Stjernholm  cb (this, @args);
04c5aa2007-01-17Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  return 1; }
b192532004-05-25Martin Stjernholm  }
9750692004-06-01Martin Stjernholm  return 0;
b192532004-05-25Martin Stjernholm  }
fc40392008-08-15Martin Stjernholm  protected string _sprintf (int flag)
b192532004-05-25Martin Stjernholm  {
04c5aa2007-01-17Martin Stjernholm  return flag == 'O' && sprintf ("%s(%s)%s", function_name (object_program (this)) || "CacheKey", activation_cbs ? "inactive" : "active",
b192532004-05-25Martin Stjernholm #ifdef ID_CACHEKEY_DEBUG
04c5aa2007-01-17Martin Stjernholm  __marker ? "[" + __marker->count + "]" : "", #else
d584002009-10-31Martin Stjernholm  OBJ_COUNT
3751a52004-05-18Martin Stjernholm #endif
04c5aa2007-01-17Martin Stjernholm  );
b192532004-05-25Martin Stjernholm  }
3751a52004-05-18Martin Stjernholm }
3ef51a2008-01-08Martin Stjernholm class TristateCacheKey //! @appears TristateCacheKey //! //! This is a variant of @[CacheKey] that adds a third state called //! "invalid" or "stale". The semantics of that is that the cache will //! continue to send the cached result for a stale cache entry until //! it get destructed, but the cache should trig an update to replace //! the cache entry as soon as possible. This is useful in cases where //! response times are more important than correctness. //! //! Subsystems that rely on valid data may use @[roxen.invalidp()] to //! check whether the cache entry is stale or not. //! //! As with the basic @[CacheKey], @[roxen.invalidate()] should be //! used to force a cache key into the stale state. { inherit CacheKey;
fc40392008-08-15Martin Stjernholm  protected int flags;
3ef51a2008-01-08Martin Stjernholm  int invalidp() //! Return nonzero if this cache key has been invalidated. //! //! Don't call directly - use @[roxen.invalidp] instead. That one //! deals properly with destruct races. //! //! @note //! If this function does not exist, the key is assumed //! to be valid for as long as it hasn't been destructed. //! //! @seealso //! @[roxen.invalidp()], @[invalidate()] { // DANGER HIGH HAZARD AREA: Exceptions in this function will go unseen. return flags; } void invalidate() //! Mark this cache key as invalid/stale. //! //! @note //! If this function does not exist, invalidation is done //! by destructing the cache key. //! //! @seealso //! @[roxen.invalidate()], @[invalidp()] { // DANGER HIGH HAZARD AREA: Exceptions in this function will go unseen. flags = 1; } } class ProtocolCacheKey //! @appears ProtocolCacheKey //! //! The cache key used by the protocol cache. { inherit TristateCacheKey; }
b192532004-05-25Martin Stjernholm // Kludge for resolver problems
fc40392008-08-15Martin Stjernholm protected function _charset_decoder_func;
b192532004-05-25Martin Stjernholm 
0c8a5b2008-03-14Martin Stjernholm // This is a somewhat simplistic regexp that doesn't handle // quoted-string parameter values correctly. It's only used on content // types so we know wide strings aren't a problem.
fc40392008-08-15Martin Stjernholm protected Regexp ct_charset_search = Regexp (";[ \t\n\r]*charset=");
0c8a5b2008-03-14Martin Stjernholm 
b49a892016-02-11Martin Karlgren // Logger class for requests. Intended to have a parent logger in // the parent request or its configuration. class RequestJSONLogger { inherit Logger.BaseJSONLogger; string request_uuid; mapping merge_defaults(mapping msg) { // We always want the hrtime from the request as well as the thread id. msg = msg + ([ // We want the current hrtime, not the start of the request... "hrtime" : gethrtime(), ]); msg->rid = request_uuid; return ::merge_defaults(msg); } //! Default parameter mapping and a parent logger object. //! //! The @[parent_logger] object is used to pass any log messages //! injected into this logger up the chain. By default, this logger //! does not log at it's own level if a parent logger is given. Instead, //! it will simply add its defaults and pass the complete log entry up //! to the parent which is then responsible for handling the actual logging. void create(void|string logger_name, void|mapping|function defaults, void|object parent_logger, void|string request_uuid) { ::create (logger_name, defaults, parent_logger); this_program::request_uuid = request_uuid; } }
a896932001-01-03Per Hedbor class RequestID
7f66d82001-08-24Martin Nilsson //! @appears RequestID
a896932001-01-03Per Hedbor //! The request information object contains all request-local information and //! server as the vessel for most forms of intercommunication between modules, //! scripts, RXML and so on. It gets passed round to almost all API callbacks //! worth mentioning. A RequestID object is born when an incoming request is //! encountered, and its life expectancy is short, as it dies again when the
2adb8a2009-06-12Martin Stjernholm //! request has passed through all levels of the module type calling sequence.
a896932001-01-03Per Hedbor {
b192532004-05-25Martin Stjernholm #ifdef ID_OBJ_DEBUG RoxenDebug.ObjectMarker __marker = RoxenDebug.ObjectMarker (this);
d584002009-10-31Martin Stjernholm #else
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
b192532004-05-25Martin Stjernholm #endif
4e99802015-08-12Marcus Agehall  // Generator for unique request UUIDs on the fly. protected string _request_uuid; string `request_uuid() { return _request_uuid || (_request_uuid = Standards.UUID.make_version4()->str()); }; void `request_uuid=(mixed v) { _request_uuid = (string)v; };
b49a892016-02-11Martin Karlgren  protected Configuration _conf; Configuration `conf() { return _conf; }
8cf9f92015-08-12Marcus Agehall 
b49a892016-02-11Martin Karlgren  void `conf=(Configuration c) { if (!json_logger->parent_logger && c) json_logger->parent_logger = c->json_logger; _conf = c;
8cf9f92015-08-12Marcus Agehall  }
b49a892016-02-11Martin Karlgren  RequestJSONLogger json_logger = RequestJSONLogger("server/handler", UNDEFINED, UNDEFINED, request_uuid);
a896932001-01-03Per Hedbor  Protocol port_obj; //! The port object this request came from. int time; //! Time of the request, standard unix time (seconds since the epoch; 1970).
e409342006-09-21Marcus Wellhardh  int hrtime;
271da22006-10-13Martin Stjernholm  //! Time of the start of the request, as returned by @[gethrtime].
e409342006-09-21Marcus Wellhardh 
a896932001-01-03Per Hedbor  string raw_url; //! The nonparsed, nontouched, non-* URL requested by the client.
ba84e12008-02-18Martin Stjernholm  //! Hence, this path is unlike @[not_query] and @[virtfile] not //! relative to the server URL and must be used in conjunction with //! the former to generate absolute paths within the server. Be //! aware that this string will contain any URL variables present in //! the request as well as the file path, and it is not decoded from //! the transport encoding.
a896932001-01-03Per Hedbor  int do_not_disconnect; //! Typically 0, meaning the channel to the client will be disconnected upon //! finishing the request and the RequestID object destroyed with it.
e0ffe32001-02-05Per Hedbor  mapping (string:array) real_variables;
a896932001-01-03Per Hedbor  //! Form variables submitted by the client browser, as found in the
af42042008-02-18Martin Stjernholm  //! @expr{form@} scope in RXML. //! //! Both query (as found in the query part of the URL) and POST //! (submitted in the request body from a form response) variables //! share this scope. If the same variable occurs in both then the //! value(s) from the query part are appended to those from the POST //! response.
a896932001-01-03Per Hedbor  //!
ba84e12008-02-18Martin Stjernholm  //! The indices are the variable names. The values contain the //! variable value(s) for each variable. The variable values are //! stored in arrays to make it possible to hold more than one value //! for a variable. These arrays are never empty.
e0ffe32001-02-05Per Hedbor  //!
ba84e12008-02-18Martin Stjernholm  //! All data (names and values) have been decoded from the transport //! encoding (i.e. @expr{%XX@} style escapes and the charset //! according to @[input_charset] have been decoded). See
6091362009-03-21Martin Stjernholm  //! @[decode_query_charset] for details.
ba84e12008-02-18Martin Stjernholm  //!
12db5a2009-01-10Stephen R. van den Berg  //! @note //! This mapping is often changed internally to produce an updated //! set of variables that is passed back to the client. //!
ba84e12008-02-18Martin Stjernholm  //! @seealso
12db5a2009-01-10Stephen R. van den Berg  //! @[rest_query], @expr{misc->post_variables@}
a896932001-01-03Per Hedbor 
5aca9b2001-09-28Martin Stjernholm  mapping(string:mixed)|FakedVariables variables;
e0ffe32001-02-05Per Hedbor  //! @decl mapping(string:mixed) variables; //! //! The variables mapping is more or less identical to the //! real_variables maping, but each variable can only have one //! value, if the form variable was sent multiple times from the //! client (this happens, as an example, if you have checkbox //! variables with the same name but different values), the values //! will be separated with \0 (the null character) in this mapping.
d5123a2017-10-09Karl Gustav Sterneberg 
c7fc3b2008-06-24Martin Stjernholm  mapping misc; // Note that non-string indices are ok.
a896932001-01-03Per Hedbor  //! This mapping contains miscellaneous non-standardized information, and //! is the typical location to store away your own request-local data for //! passing between modules et cetera. Be sure to use a key unique to your //! own application.
0f06502005-12-02Henrik Grubbström (Grubba)  //!
4d0c662006-09-27Martin Stjernholm  //! If a subrequest (a.k.a. "fake" request) is generated within //! another request then the subrequest receives a copy (created //! with @[copy_value]) of this mapping. I.e. the subrequest sees //! any data put here by the parent request, but changes made in the //! subrequest won't affect the parent. //!
0f06502005-12-02Henrik Grubbström (Grubba)  //! These are some of the defined entries: //! @mapping //! @member int "cacheable"
08a9d22009-02-23Martin Stjernholm  //! Time in seconds that the request is cacheable. Use
c7429a2009-04-17Martin Stjernholm  //! @[get_max_cache()], @[lower_max_cache()], //! @[raise_max_cache()], @[set_max_cache()] or one of the cache
08a9d22009-02-23Martin Stjernholm  //! macros to access. This setting both controls the maximum //! cache time in the protocol cache and the timestamp returned //! in the @expr{Expires@} header (an @expr{"expires"@} entry in //! the result mapping will override it, though).
c7429a2009-04-17Martin Stjernholm  //! @member mapping(mixed:int) "local_cacheable" //! If this mapping exists, each value in it will be modified by //! @[set_max_cache], @[lower_max_cache] and @[raise_max_cache] //! in the same way they modify @expr{misc->cacheable@}. It's //! used to track cache time changes during specific parts of //! the request.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member array(function) "_cachecallbacks" //! Callbacks to verify that the cache entry is valid.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member CacheKey "cachekey" //! @[CacheKey] for the request.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member multiset(string) "client_connection" //! Parsed request header "Connection".
88c2a62008-02-19Martin Stjernholm  //! @member string "content-type" //! Alias for @expr{@[request_header]["content-type"]@}, the //! content type of the request. (Note the dash instead of //! underscore in the field name.) //! @member string "content_type_type" //! The content type itself from the @expr{Content-Type@} header //! of the request, i.e. without any parameters. It has been //! lowercased and whitespace stripped, so it's on a canonical //! form like e.g. @expr{"image/jpeg"@}. //! @member string "content_type_params" //! The parameters from the @expr{Content-Type@} header of the //! request, if any. Whitespace preceding the first parameter //! have been removed, but otherwise it remains unchanged.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member array "cookies" //! Empty array. Obsolete entry.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member string "connection" //! Protocol connection mode. Typically @tt{"keep-alive"@} or //! @tt{"close"@}.
ad8cfd2009-01-10Martin Stjernholm  //! @member int "defaulted_conf" //! If set, then the url-to-configuration resolution failed to //! find a good configuration based on the interface, port, host //! header, and port path prefix. The value is an integer //! showing the "badness" of the fallback configuration chosen: //! @int //! @value 1 //! The host, port and path prefix didn't match among the //! urls registered for the interface the request was //! received on, but a match was found among the urls //! registered on an ANY interface for the same port number. //! @value 2 //! One of the configurations on the receiving port (c.f. //! @[port_obj]) had the "Default site" flag set and was //! therefore chosen. //! @value 3 //! One of the configurations in the server had the "Default //! site" flag set and was therefore chosen, regardless of //! the ports it has open. //! @value 4 //! No configuration with the "Default site" flag set was //! found, so the one with the least specific url on the //! receiving port was chosen. //! @endint
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member mapping(string:mixed) "defines" //! RXML macros.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member int(100..) "error_code" //! Result error code unless specified elsewhere.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member string "etag" //! Entity tag for the request. If the request is cacheable in //! the protocol cache one will be generated if not already present.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member array(string) "files" //! Multipart/form-data variables that have an associated filename. //! @member string "host"
1985942009-01-21Martin Stjernholm  //! The host header, exactly as read from the request. Since it //! isn't normalized, it's useful in e.g. redirects so that the //! client doesn't falsely conclude that the redirect has //! switched server (c.f. @[url_base]).
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member string "hostname"
1985942009-01-21Martin Stjernholm  //! Normalized hostname from the host header. The normalization //! means it's lowercased, and if it's an IPv6 address then it's //! converted to the basic @tt{x:x:x:x:x:x:x:x@} form (using //! @[Protocols.IPv6.normalize_addr_basic]).
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member int "last_modified" //! Time stamp for when the request was last modified.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member int "len" //! Length in bytes of the data section of the request. //! @member int "_log_cheat_addition" //! Value to add to @expr{file->len@} to get the length //! to report in the access log.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member mapping(string:string|array(string)) "moreheads"
1003fd2009-04-03Martin Stjernholm  //! If this exists, it contains headers to send in the response. //! It overrides automatically calculated headers and headers //! given in a response mapping (as returned by e.g. //! @[RoxenModule.find_file]). Although http headers are case //! insensitive, the header names in this mapping are not. All //! names should follow the capitalization forms used in RFC //! 2616 (c.f. @[Roxen.canonicalize_http_header]). See //! @[add_response_header()] for more details.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member int(1..1) "no_proto_cache" //! Flag indicating that the result should not be cached in //! the protocol cache.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member RequestID "orig" //! Originating @[RequestID] for recursive requests.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member int "port" //! Port number from the canonicalized host header.
a642762014-12-15Henrik Grubbström (Grubba)  //! //! Note that this may differ from the actual port number //! (available in @[port_obj->port]) if eg the server is //! found behind a load balancing proxy. cf [bug 7385].
abea6f2016-05-25Henrik Grubbström (Grubba)  //! @member array(array(string|int)) forwarded //! Parsed @expr{"Forwarded"@} header (@rfc{7239@}). //! If the client sent no forwarded headers, any @expr{"x-forwarded-*"@} //! headers that it sent are used instead. //! //! Each entry is on the format returned by @[MIME.tokenize()], and //! corresponds to one @b{Forwarded@} field.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member PrefLanguages "pref_languages" //! Language preferences for the request.
6091362009-03-21Martin Stjernholm  //! @member mapping(string:array(string)) "post_variables"
12db5a2009-01-10Stephen R. van den Berg  //! For POST requests, these are the variables parsed from the //! request body. It is undefined otherwise.
6091362009-03-21Martin Stjernholm  //! @member mapping(string:array(MIME.Message)) "post_parts" //! For multipart/form-data POST requests, this holds the MIME //! message object for each part. It is undefined otherwise.
ac55dd2005-12-05Henrik Grubbström (Grubba)  //! @member array "proxyauth" //! Decoded proxy authentication information. //! @member string "range" //! Byte range information.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member string "site_prefix_path" //! Site path prefix. //! @member Stat "stat" //! File status information for the request. //! @member multiset(string) "vary" //! Contains the active set of vary headers. Please use
74da3d2008-01-10Martin Stjernholm  //! @[register_vary_callback()] to alter. All header names here //! should be lowercase to avoid problems with duplicates.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @member array(string|function(string, RequestID:string)) @ //! "vary_cb_order" //! Contains the cache lookup callback functions relevant to the //! request so far in order. See @[register_vary_callback()] for //! details.
a0f4712008-01-10Martin Stjernholm  //! @member mapping(string:multiset(function(string, RequestID:string))| @ //! int(1..1)) "vary_cb_set"
0f06502005-12-02Henrik Grubbström (Grubba)  //! Same content as @tt{"vary_cb_order"@} above, but only used to
a0f4712008-01-10Martin Stjernholm  //! speed up some lookups. For internal use only.
0f06502005-12-02Henrik Grubbström (Grubba)  //! @endmapping
a896932001-01-03Per Hedbor 
c7fc3b2008-06-24Martin Stjernholm  mapping connection_misc = ([]); // Note that non-string indices are ok.
99e65a2003-06-18Tomas Nilsson  //! This mapping contains miscellaneous non-standardized information, and //! is the typical location to store away your own connection-local data //! for passing between requests on the same connection et cetera. Be sure //! to use a key unique to your own application.
08e9b22006-08-16Henrik Grubbström (Grubba)  //! Contains the current set of cookies. //! //! @note //! DON'T touch! //! //! @seealso //! @[cookies] mapping(string:string) real_cookies;
ad7da42005-12-07Henrik Grubbström (Grubba)  //! Wrapper that calls @[register_vary_callback()] as appropriate when //! cookies are accessed. //!
e1559d2005-12-09Henrik Grubbström (Grubba)  //! @note //! Uses the parent pointer to access @[register_vary_callback()] and //! will thus update the original @[RequestID] object even if copied //! to a cloned @[RequestID]. This is a feature. //!
ad7da42005-12-07Henrik Grubbström (Grubba)  //! @seealso //! @[cookies], @[register_vary_callback()], @[Roxen.get_cookie_callback()]
9911402009-04-28Martin Jonsson  class CookieJar
ad7da42005-12-07Henrik Grubbström (Grubba)  {
f060a02006-04-20Henrik Grubbström (Grubba)  //! Contains the set of cookies that have been zapped in some way.
fc40392008-08-15Martin Stjernholm  protected mapping(string:string) eaten = ([]);
f060a02006-04-20Henrik Grubbström (Grubba) 
3c0e582012-05-10Henrik Grubbström (Grubba) 
32fd592012-05-14Henrik Grubbström (Grubba)  // cf RFC 6265.
3d76f52009-04-28Henrik Grubbström (Grubba)  protected void create(string|array(string)|mapping(string:string)|void contents)
ad7da42005-12-07Henrik Grubbström (Grubba)  {
4b55782006-09-15Marcus Wellhardh  VARY_WERROR("Initiating cookie jar.\n");
08e9b22006-08-16Henrik Grubbström (Grubba)  real_cookies = ([]);
ad7da42005-12-07Henrik Grubbström (Grubba)  if(!contents) return;
3d76f52009-04-28Henrik Grubbström (Grubba)  if (mappingp(contents)) { real_cookies = contents; return; }
ad7da42005-12-07Henrik Grubbström (Grubba)  array tmp = arrayp(contents) ? contents : ({ contents});
d5123a2017-10-09Karl Gustav Sterneberg 
ad7da42005-12-07Henrik Grubbström (Grubba)  foreach(tmp, string cookieheader) {
1748ea2012-05-15Henrik Grubbström (Grubba)  foreach(cookieheader/";", string c)
ad7da42005-12-07Henrik Grubbström (Grubba)  {
1748ea2012-05-15Henrik Grubbström (Grubba)  array(string) pair = c/"=";
3c0e582012-05-10Henrik Grubbström (Grubba)  if (sizeof(pair) < 2) continue;
32fd592012-05-14Henrik Grubbström (Grubba)  string name = String.trim_whites(pair[0]); string value = String.trim_whites(pair[1..]*"="); if (has_prefix(value, "\"") && has_suffix(value, "\"")) value = value[1..sizeof(value)-2];
3c0e582012-05-10Henrik Grubbström (Grubba)  catch {
dbd94b2005-12-07Henrik Grubbström (Grubba)  value=_Roxen.http_decode_string(value);
3c0e582012-05-10Henrik Grubbström (Grubba)  }; catch {
dbd94b2005-12-07Henrik Grubbström (Grubba)  name=_Roxen.http_decode_string(name);
3c0e582012-05-10Henrik Grubbström (Grubba)  }; real_cookies[ name ]=value;
d5123a2017-10-09Karl Gustav Sterneberg 
ad7da42005-12-07Henrik Grubbström (Grubba) #ifdef OLD_RXML_CONFIG
3c0e582012-05-10Henrik Grubbström (Grubba)  // FIXME: Really ought to register this one... if( (name == "RoxenConfig") && strlen(value) ) config = mkmultiset( value/"," );
ad7da42005-12-07Henrik Grubbström (Grubba) #endif } } }
fc40392008-08-15Martin Stjernholm  protected string `->(string cookie)
ad7da42005-12-07Henrik Grubbström (Grubba)  {
6b423e2006-05-22Jonas Wallden  if (supports && zero_type(eaten[cookie])) {
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("Looking at cookie %O from %s\n", cookie, describe_backtrace(({backtrace()[-2]})));
c3310f2014-11-17Henrik Grubbström (Grubba)  register_vary_callback("cookie", Roxen->get_cookie_callback(cookie));
f060a02006-04-20Henrik Grubbström (Grubba)  }
08e9b22006-08-16Henrik Grubbström (Grubba)  return real_cookies[cookie];
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected string `[](mixed cookie)
ad7da42005-12-07Henrik Grubbström (Grubba)  {
dbd94b2005-12-07Henrik Grubbström (Grubba)  if (stringp(cookie)) {
ad7da42005-12-07Henrik Grubbström (Grubba)  return `->(cookie); } return UNDEFINED; }
fc40392008-08-15Martin Stjernholm  protected string `->=(string cookie, string value)
ad7da42005-12-07Henrik Grubbström (Grubba)  {
f060a02006-04-20Henrik Grubbström (Grubba)  if (zero_type(eaten[cookie])) {
08e9b22006-08-16Henrik Grubbström (Grubba)  eaten[cookie] = real_cookies[cookie];
f060a02006-04-20Henrik Grubbström (Grubba)  }
08e9b22006-08-16Henrik Grubbström (Grubba)  return real_cookies[cookie] = value;
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected string `[]=(mixed cookie, string value)
ad7da42005-12-07Henrik Grubbström (Grubba)  { // FIXME: Warn if not string? return `->=(cookie, value); }
fc40392008-08-15Martin Stjernholm  protected string _m_delete(string cookie)
ad7da42005-12-07Henrik Grubbström (Grubba)  { // FIXME: Warn if not string?
f060a02006-04-20Henrik Grubbström (Grubba)  if (zero_type(eaten[cookie])) {
08e9b22006-08-16Henrik Grubbström (Grubba)  eaten[cookie] = real_cookies[cookie];
f060a02006-04-20Henrik Grubbström (Grubba)  }
08e9b22006-08-16Henrik Grubbström (Grubba)  return m_delete(real_cookies, cookie);
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected array(string) _indices()
ad7da42005-12-07Henrik Grubbström (Grubba)  {
e3589d2008-01-10Martin Stjernholm  register_vary_callback("cookie");
08e9b22006-08-16Henrik Grubbström (Grubba)  return indices(real_cookies);
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected array(string) _values()
ad7da42005-12-07Henrik Grubbström (Grubba)  {
e3589d2008-01-10Martin Stjernholm  register_vary_callback("cookie");
08e9b22006-08-16Henrik Grubbström (Grubba)  return values(real_cookies);
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected int _sizeof()
ad7da42005-12-07Henrik Grubbström (Grubba)  {
e3589d2008-01-10Martin Stjernholm  register_vary_callback("cookie");
08e9b22006-08-16Henrik Grubbström (Grubba)  return sizeof(real_cookies);
ad7da42005-12-07Henrik Grubbström (Grubba)  }
fc40392008-08-15Martin Stjernholm  protected mapping(string:string) `+(mapping(string:string) other)
45004c2005-12-09Martin Stjernholm  {
e3589d2008-01-10Martin Stjernholm  register_vary_callback("cookie");
08e9b22006-08-16Henrik Grubbström (Grubba)  return real_cookies + other;
45004c2005-12-09Martin Stjernholm  }
fc40392008-08-15Martin Stjernholm  protected mapping(string:string) ``+(mapping(string:string) other)
45004c2005-12-09Martin Stjernholm  {
e3589d2008-01-10Martin Stjernholm  register_vary_callback("cookie");
08e9b22006-08-16Henrik Grubbström (Grubba)  return other + real_cookies;
45004c2005-12-09Martin Stjernholm  }
f060a02006-04-20Henrik Grubbström (Grubba)  //! Used to retrieve the original set of cookies at //! protocol cache store time.
fc40392008-08-15Martin Stjernholm  protected mapping(string:string) `~()
f060a02006-04-20Henrik Grubbström (Grubba)  {
3f994f2006-09-14Marcus Wellhardh  VARY_WERROR("Disconnecting cookie jar.\n");
08e9b22006-08-16Henrik Grubbström (Grubba)  return real_cookies + eaten;
f060a02006-04-20Henrik Grubbström (Grubba)  }
2ca4282009-11-24Stefan Wallström  //! @ignore
d584002009-10-31Martin Stjernholm  DECLARE_OBJ_COUNT;
2ca4282009-11-24Stefan Wallström  //! @endignore
d584002009-10-31Martin Stjernholm 
fc40392008-08-15Martin Stjernholm  protected string _sprintf(int fmt)
3583aa2005-12-07Henrik Grubbström (Grubba)  {
d584002009-10-31Martin Stjernholm  return fmt == 'O' && sprintf("CookieJar(%O)" + OBJ_COUNT,
86658e2009-08-19Martin Stjernholm  RequestID::this && real_cookies);
3583aa2005-12-07Henrik Grubbström (Grubba)  }
8cf9f92015-08-12Marcus Agehall  string encode_json(int flags) { return Standards.JSON.encode(real_cookies); }
ad7da42005-12-07Henrik Grubbström (Grubba)  }
f060a02006-04-20Henrik Grubbström (Grubba)  CookieJar|mapping(string:string) cookies;
a896932001-01-03Per Hedbor  //! The indices and values map to the names and values of the cookies sent //! by the client for the requested page. All data (names and values) are //! decoded from their possible transport encoding.
ad7da42005-12-07Henrik Grubbström (Grubba)  //! //! @note //! Used to be a plain mapping in Roxen 4.0 and earlier. It now //! has a wrapper that registers dependencies on the various cookies.
f060a02006-04-20Henrik Grubbström (Grubba)  //! @note //! The wrapper is removed, and the contents partially restored at //! request send time.
ad7da42005-12-07Henrik Grubbström (Grubba)  //! Call to initialize the cookies. //! //! Typically called from callbacks installed with
a432ad2008-01-10Martin Stjernholm  //! @[register_vary_callback()] to ensure @[cookies] is initialized.
4b55782006-09-15Marcus Wellhardh  void init_cookies(int|void no_cookie_jar)
ad7da42005-12-07Henrik Grubbström (Grubba)  { if (!cookies) { cookies = CookieJar(request_headers["cookie"]);
4b55782006-09-15Marcus Wellhardh  if (no_cookie_jar) { // Disable the cookie jar -- Called from log()? real_cookies = cookies = ~cookies; }
ad7da42005-12-07Henrik Grubbström (Grubba)  } }
a896932001-01-03Per Hedbor 
a432ad2008-01-10Martin Stjernholm  void init_pref_languages()
a253282009-04-03Henrik Grubbström (Grubba)  //! Call to initialize @expr{@[misc]->pref_languages@}.
a432ad2008-01-10Martin Stjernholm  //! //! Typically called from callbacks installed with //! @[register_vary_callback()] to ensure
a253282009-04-03Henrik Grubbström (Grubba)  //! @expr{@[misc]->pref_languages@} is initialized.
a432ad2008-01-10Martin Stjernholm  { if (!misc->pref_languages) { misc->pref_languages=PrefLanguages(); if (string|array(string) contents = request_headers[ "accept-language" ]) { if( !arrayp( contents ) ) contents = (contents-" ")/","; else contents = Array.flatten( map( map( contents, `-, " " ), `/, "," ))-({""}); misc->pref_languages->languages=contents; misc["accept-language"] = contents; } } }
5588692001-04-22Per Hedbor  mapping (string:array(string)|string) request_headers;
88c2a62008-02-19Martin Stjernholm  //! Indices and values map to the names and values of all HTTP //! headers sent with the request, with lowercased header names. //! Here is where you look for the "user-agent" header, the //! "referer" [sic!] header and similar interesting data provided by //! the client.
a896932001-01-03Per Hedbor  mapping (string:mixed) throttle; // ? mapping (string:mixed) client_var; //! The client scope; a mapping of various client-related variables, indices //! being the entity names and the values being their values respectively. multiset(string) prestate; //! A multiset of all prestates harvested from the URL. Prestates are boolean //! flags, who are introduced in an extra leading path segment of the URL //! path put within parentheses, as in <a //! href="http://docs.roxen.com/(tables)/">docs://www.roxen.com/(tables)/</a>, //! this rendering a prestate multiset <pi>(&lt; "tables" &gt;)</pi>. //! //! Prestates are mostly useful for debugging purposes, since prestates //! generally lead to multiple URLs for identical documents resulting in //! poor usage of browser/proxy caches and the like. See <ref>config</ref>.
ba84e12008-02-18Martin Stjernholm  //! //! The transport encoding has been decoded (i.e. any @expr{%XX@} //! escapes and the charset according to @[input_charset]).
a896932001-01-03Per Hedbor  multiset(string) config; //! Much like prestates, the id->config multiset is typically used for //! boolean information of state supplied by the client. The config state, //! however, is hidden in a client-side cookie treated specially by roxen, //! namely the <tt>RoxenConfig</tt> cookie.
ba84e12008-02-18Martin Stjernholm  //! //! The transport encoding has been decoded (i.e. any @expr{%XX@} //! escapes and the charset according to @[input_charset]).
a896932001-01-03Per Hedbor  multiset(string) supports; //! All flags set by the supports system. multiset(string) pragma; //! All pragmas (lower-cased for canonization) sent with the request. For //! real-world applications typically only <pi>pragma["no-cache"]</pi> is of //! any particular interest, this being sent when the user does a forced //! reload of the page. array(string) client; array(string) referer; Stdio.File my_fd; // Don't touch; use the returned file descriptor from connection() instead. string prot; //! The protocol used for the request, e g "FTP", "HTTP/1.0", "HTTP/1.1". //! (Se also <ref>clientprot</ref>.) string clientprot; //! The protocol the client wanted to use in the request. This may //! not be the same as <ref>prot</ref>, if the client wanted to talk //! a higher protocol version than the server supports to date. string method; //! The method used by the client in this request, e g "GET", "POST". string realfile; //! When the the requested resource is an actual file in the real //! filesystem, this is its path. string virtfile; //! The mountpoint of the location module that provided the requested file. //! Note that this is not accessable from location modules; you need to keep //! track of your mountpoint on your own using <ref>defvar()</ref> and //! <ref>query()</ref>. This mountpoint is relative to the server URL. string rest_query; //! The scraps and leftovers of the requested URL's query part after //! removing all variables (that is, all key=value pairs) from it.
ba84e12008-02-18Martin Stjernholm  //! //! The transport encoding has been decoded (i.e. any @expr{%XX@} //! escapes and the charset according to @[input_charset]). The //! pieces have then been concatenated together again in the same //! order, separated by @expr{"&"@}.
a896932001-01-03Per Hedbor  string raw; //! The raw, untouched request in its entirety.
ba84e12008-02-18Martin Stjernholm  //! //! @seealso //! @[raw_url]
a896932001-01-03Per Hedbor  string query;
ba84e12008-02-18Martin Stjernholm  //! The query part (i.e. all characters after the first question //! mark) of the requested URL. //! //! The transport encoding has been decoded (i.e. any @expr{%XX@} //! escapes and the charset according to @[input_charset]).
a896932001-01-03Per Hedbor  string not_query;
ba84e12008-02-18Martin Stjernholm  //! The path to the requested resource that is below the virtual //! server's mountpoint. I.e. for a typical server registering a URL //! with no ending path component, @[not_query] will contain the //! full path. //! //! @[not_query] usually begins with @expr{"/"@} but that is not //! guaranteed since it's taken from the request line. It has //! however been simplified so it is guaranteed to not contain any //! @expr{"."@} or @expr{".."@} segments. //! //! The transport encoding has been decoded (i.e. any @expr{%XX@} //! escapes and the charset according to @[input_charset]). string input_charset; //! The charset that was used to decode @[prestate], @[config], //! @[query], @[not_query], @[rest_query], and the variable bindings //! in @[real_variables]. //! //! It is zero if they were not successfully charset decoded. That //! is effectively the same as if the charset was ISO-8859-1. //! //! @seealso
6091362009-03-21Martin Stjernholm  //! @[decode_query_charset]
a896932001-01-03Per Hedbor  string extra_extension;
e885f72010-06-29Henrik Grubbström (Grubba)  string data = "";
a896932001-01-03Per Hedbor  //! The raw request body, containing non-decoded post variables et cetera.
e885f72010-06-29Henrik Grubbström (Grubba)  //! //! @note //! In versions of Roxen prior to 5.0.505 this variable could sometimes //! contain @expr{0@}, which should be regarded as equvivalent to //! @expr{""@}. //! //! @note //! If the amount of request data is unknown (ie @expr{misc->len@} //! is @expr{0x7fffffff@}), then additional data can be retrieved //! from @[connection()]. This is typically the case with eg STOR //! (aka PUT) and the ftp protocol. string leftovers = ""; //! Raw data belonging to the next request. //! //! @note //! In versions of Roxen prior to 5.0.505 this variable could sometimes //! contain @expr{0@}, which should be regarded as equvivalent to //! @expr{""@}.
a896932001-01-03Per Hedbor 
9689f12002-09-03Martin Stjernholm  string rawauth, realauth; // Used by many modules, so let's keep this.
a896932001-01-03Per Hedbor  string since; string remoteaddr; //! The client's IP address. string host; //! The client's hostname, if resolved.
6322612004-05-06Martin Stjernholm  multiset(string) cache_status = (<>);
b5f3de2006-04-20Henrik Grubbström (Grubba)  //! Contains the caches that were hit when the request was served.
46d4cb2001-08-22Martin Stjernholm  //! See the docstring for @tt{$cache-status@} in the @tt{LogFormat@} //! global variable for known values, but note that the multiset
b5f3de2006-04-20Henrik Grubbström (Grubba)  //! never actually contains the value @expr{"nocache"@}; it's only written
46d4cb2001-08-22Martin Stjernholm  //! when the multiset is empty.
be70552006-09-07Fredrik Noring  multiset(string) eval_status = (<>); //! Contains the content evaluators that were hit when the request //! was served. See the docstring for @tt{$eval-status@} in the //! @tt{LogFormat@} global variable for known values.
7fafb82001-07-25Johan Sundström  object root_id; //! @decl RequestID root_id; //! The root id object directly associated with the request - remains //! the same for all id objects generated by <insert href> tags and
b82d472010-09-28Martin Stjernholm  //! similar conditions that invoke @[clone_me()]. Points to itself //! in the root id object.
7fafb82001-07-25Johan Sundström 
e824002009-06-24Martin Stjernholm  // Internal but not protected. The time variables have double use: // At the start of the timed period they are set to the starting // point in time, at the end they are changed to the elapsed time. int queue_length, queue_time, handle_time, handle_vtime;
24a43a2009-06-10Martin Stjernholm 
fc40392008-08-15Martin Stjernholm  protected void create(Stdio.File fd, Protocol port, Configuration conf){}
a896932001-01-03Per Hedbor  void send(string|object what, int|void len){}
fc40392008-08-15Martin Stjernholm  protected SimpleNode xml_data; // XML data for the request.
24e9142003-04-22Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode get_xml_data()
24e9142003-04-22Henrik Grubbström (Grubba)  {
b6067d2010-06-22Martin Jonsson  if (!data || !sizeof(data)) return 0;
24e9142003-04-22Henrik Grubbström (Grubba)  if (xml_data) return xml_data; // FIXME: Probably ought to check that the content-type for // the request is text/xml.
c2d7aa2004-04-13Martin Stjernholm  DAV_WERROR("Parsing XML data: %O\n", data);
a8e2b32004-05-07Henrik Grubbström (Grubba)  return xml_data = Parser.XML.Tree.simple_parse_input(data, 0, Parser.XML.Tree.PARSE_ENABLE_NAMESPACES);
24e9142003-04-22Henrik Grubbström (Grubba)  }
76d1192004-05-05Henrik Grubbström (Grubba) 
7caea32016-02-22Henrik Grubbström (Grubba)  //! Get a string describing the link layer protocol for the request. //! //! @returns //! Currently returns one of //! @string //! @value "-" //! Unknown. Typically an internal request or the connection has //! been closed. //! @value "TCP/IP" //! Standard TCP/IP connection. //! @value "SSL/3.0" //! Secure Socket Layer version 3.0. //! @value "TLS/1.0" //! Transport Layer Security version 1.0. //! @value "TLS/1.1" //! Transport Layer Security version 1.1. //! @value "TLS/1.2" //! Transport Layer Security version 1.2. //! @endstring //! //! @note //! This is the value logged by the log-format @tt{$link-layer@}. //! //! @note //! More versions of TLS may become supported, in which case they //! will get corresponding results from this function.
8162682016-09-16Henrik Grubbström (Grubba)  //! //! @seealso //! @[query_cipher_suite()]
7caea32016-02-22Henrik Grubbström (Grubba)  string query_link_layer() { if (!my_fd) return "-"; if (!my_fd->query_version) return "TCP/IP"; return replace(SSL.Constants.fmt_version(my_fd->query_version()), " ", "/"); }
8162682016-09-16Henrik Grubbström (Grubba)  //! Get a string describing the TLS cipher suite used for the request. //! //! @returns //! Returns either //! @string //! @value "-" //! None. Either the connection doesn't use SSL/TLS, or the //! handshaking hasn't completed yet. //! @value "TLS_*" //! @value "SSL_*" //! A symbol from the table of cipher suites in @[SSL.Constants]. //! These are typically the same as the symbols in the corresponding //! RFCs, but with only the "TLS_"/"SSL_" prefix being upper case. //! @endstring //! //! @note //! This is the value logged by the log-format @tt{$cipher-suite@}. //! //! @seealso //! @[query_link_layer()] string query_cipher_suite() { if (!my_fd || !my_fd->query_suite) return "-"; int suite = my_fd->query_suite(); if (!suite) return "-"; return SSL.Constants.fmt_cipher_suite(suite); }
76d1192004-05-05Henrik Grubbström (Grubba)  // Parsed if-header for the request.
fc40392008-08-15Martin Stjernholm  protected mapping(string:array(array(array(string)))) if_data;
76d1192004-05-05Henrik Grubbström (Grubba) 
2550a42004-05-17Martin Stjernholm #ifdef IF_HEADER_DEBUG #define IF_HDR_MSG(X...) werror (X) #else #define IF_HDR_MSG(X...) #endif
76d1192004-05-05Henrik Grubbström (Grubba)  //! Parse an RFC 2518 9.4 "If Header". //! //! @note //! For speed reasons the parsing is rather forgiving. //! //! @returns
1093e42004-05-05Henrik Grubbström (Grubba)  //! Returns @expr{0@} (zero) if there was no if header, or //! if parsing of the if header failed. //! Returns a mapping from resource name to condition on success.
76d1192004-05-05Henrik Grubbström (Grubba)  //! //! A condition is represented as an array of sub-conditions //! (@tt{List@} in RFC 2518), where each sub-condition is an array //! of tokens, and each token is an array of two elements, where //! the first is one of the strings @expr{"not"@}, @expr{"etag"@}, //! or @expr{"key"@}, and the second is the value.
1093e42004-05-05Henrik Grubbström (Grubba)  //!
59e6582018-03-27Henrik Grubbström (Grubba)  //! There is an implicit @b{OR@} between sub-conditions //! (cf @rfc{4918:10.4.6@}), and an implicit @b{AND@} //! between the elements in a sub-condition. //! //! The default resource is mapped to @[not_query]. //! //! As @rfc{4918:10.4.1@} states that the mere fact that a state //! token appears in an If header means that it has been submitted, //! we as a convenience add all non-negated lock tokens to the //! @expr{0@} resource. //! //! @seealso //! @rfc{4918:10.4.2@}
76d1192004-05-05Henrik Grubbström (Grubba)  mapping(string:array(array(array(string)))) get_if_data() {
2550a42004-05-17Martin Stjernholm  if (if_data) { IF_HDR_MSG ("get_if_data(): Returning cached result\n"); return sizeof(if_data) && if_data; }
76d1192004-05-05Henrik Grubbström (Grubba) 
53cb8b2004-05-06Henrik Grubbström (Grubba)  if_data = ([]); // Negative caching.
76d1192004-05-05Henrik Grubbström (Grubba)  string raw_header;
2550a42004-05-17Martin Stjernholm  if (!(raw_header = request_headers->if)) { IF_HDR_MSG ("get_if_data(): No if header\n"); return 0; }
76d1192004-05-05Henrik Grubbström (Grubba)  array(array(string|int|array(array(string)))) decoded_if = MIME.decode_words_tokenized_labled(raw_header);
2550a42004-05-17Martin Stjernholm #if 0 IF_HDR_MSG("get_if_data(): decoded_if: %O\n", decoded_if); #endif if (!sizeof(decoded_if)) { IF_HDR_MSG("Got only whitespace.\n"); return 0; }
76d1192004-05-05Henrik Grubbström (Grubba) 
59e6582018-03-27Henrik Grubbström (Grubba)  mapping(string:array(array(array(string)))) res = ([ 0:({}), ]);
76d1192004-05-05Henrik Grubbström (Grubba) 
59e6582018-03-27Henrik Grubbström (Grubba)  array(string) keys = ({});
2a86b62004-05-08Henrik Grubbström (Grubba)  string tmp_resource;
59e6582018-03-27Henrik Grubbström (Grubba)  string resource = not_query;
76d1192004-05-05Henrik Grubbström (Grubba)  foreach(decoded_if, array(string|int|array(array(string))) symbol) { switch (symbol[0]) { case "special":
2a86b62004-05-08Henrik Grubbström (Grubba)  switch(symbol[1]) { case '<': tmp_resource = ""; break;
d5123a2017-10-09Karl Gustav Sterneberg  case '>':
2a86b62004-05-08Henrik Grubbström (Grubba)  resource = tmp_resource; tmp_resource = 0; // Normalize. // FIXME: Check that the protocol and server parts refer // to this server.
59e6582018-03-27Henrik Grubbström (Grubba)  // NB: Above invalid according to rfc 4918 8.3. // // NB: RFC 4918 8.3 adds support for path-absolute resources. // NB: The resource reference may have a query section. //
2a86b62004-05-08Henrik Grubbström (Grubba)  // FIXME: Support for servers mounted on subpaths. catch { resource = Standards.URI(resource)->path; };
59e6582018-03-27Henrik Grubbström (Grubba)  catch { resource = Protocols.HTTP.percent_decode(resource); }; catch { resource = utf8_to_string(resource); }; resource = Unicode.normalize(resource, "NFC"); if (!sizeof(resource)) resource = "/";
2a86b62004-05-08Henrik Grubbström (Grubba)  if (!res[resource]) res[resource] = ({}); break; default: if (tmp_resource) tmp_resource += sprintf("%c", symbol[1]); break; }
76d1192004-05-05Henrik Grubbström (Grubba)  break; case "word":
2a86b62004-05-08Henrik Grubbström (Grubba)  case "domain-literal":
76d1192004-05-05Henrik Grubbström (Grubba)  // Resource
2a86b62004-05-08Henrik Grubbström (Grubba)  if (!tmp_resource) return 0; tmp_resource += symbol[1];
76d1192004-05-05Henrik Grubbström (Grubba)  break; case "comment": // Parenthesis expression.
2a86b62004-05-08Henrik Grubbström (Grubba)  if (tmp_resource) { // Inside a resource. tmp_resource += "(" + symbol[1][0][0] + ")"; break; }
76d1192004-05-05Henrik Grubbström (Grubba)  array(array(string|int|array(array(string)))) sub_expr = MIME.decode_words_tokenized_labled(symbol[1][0][0]); int i; array(array(string)) expr = ({});
2a86b62004-05-08Henrik Grubbström (Grubba)  string tmp_key;
76d1192004-05-05Henrik Grubbström (Grubba)  for (i = 0; i < sizeof(sub_expr); i++) { switch(sub_expr[i][0]) { case "special":
2a86b62004-05-08Henrik Grubbström (Grubba)  switch(sub_expr[i][1]) { case '<': tmp_key = ""; break; case '>':
2550a42004-05-17Martin Stjernholm  if (!tmp_key) { IF_HDR_MSG("No tmp_key.\n"); return 0; }
59e6582018-03-27Henrik Grubbström (Grubba)  if (!sizeof(expr) || (expr[-1][0] != "not")) { keys += ({ tmp_key }); }
2a86b62004-05-08Henrik Grubbström (Grubba)  expr += ({ ({ "key", tmp_key }) }); tmp_key = 0; break; default: if (tmp_key) tmp_key += sprintf("%c", sub_expr[i][1]); break; }
76d1192004-05-05Henrik Grubbström (Grubba)  break; case "domain-literal":
2a86b62004-05-08Henrik Grubbström (Grubba)  if (tmp_key) { tmp_key += sub_expr[i][1]; break; }
76d1192004-05-05Henrik Grubbström (Grubba)  // entity-tag. string etag = sub_expr[i][1]; // etag is usually something like "[\"some etag\"]" here.
e589e52004-05-10Henrik Grubbström (Grubba)  sscanf(etag, "[%s]", etag); // Remove brackets
76d1192004-05-05Henrik Grubbström (Grubba)  expr += ({ ({ "etag", etag }) }); break; case "word": // State-token or Not.
2a86b62004-05-08Henrik Grubbström (Grubba)  if (tmp_key) { tmp_key += sub_expr[i][1]; break; } if (lower_case(sub_expr[i][1]) == "not") {
76d1192004-05-05Henrik Grubbström (Grubba)  // Not
59e6582018-03-27Henrik Grubbström (Grubba)  if (sizeof(expr) && (expr[-1][0] == "not")) { IF_HDR_MSG("Double negation."); report_debug("Syntax error in if-header: %O\n", raw_header); return 0; }
76d1192004-05-05Henrik Grubbström (Grubba)  expr += ({ ({ "not", 0 }) });
2a86b62004-05-08Henrik Grubbström (Grubba)  break;
76d1192004-05-05Henrik Grubbström (Grubba)  }
2550a42004-05-17Martin Stjernholm  IF_HDR_MSG("Word outside key: %O\n", sub_expr[i][1]); report_debug("Syntax error in if-header: %O\n", raw_header);
2a86b62004-05-08Henrik Grubbström (Grubba)  return 0;
76d1192004-05-05Henrik Grubbström (Grubba)  } }
2550a42004-05-17Martin Stjernholm  if (tmp_key) { IF_HDR_MSG("Active tmp_key: %O\n", tmp_key); report_debug("Syntax error in if-header: %O\n", raw_header); return 0; }
76d1192004-05-05Henrik Grubbström (Grubba)  res[resource] += ({ expr }); break; default: report_debug("Syntax error in if-header: %O\n", raw_header); return 0; } }
59e6582018-03-27Henrik Grubbström (Grubba)  if (sizeof(keys)) { res[0] = ({ map(keys, lambda(string key) { return ({ "key", key }); }), }); }
2550a42004-05-17Martin Stjernholm  if (tmp_resource) { IF_HDR_MSG("Active tmp_resource: %O\n", tmp_resource); report_debug("Syntax error in if-header: %O\n", raw_header); return 0; } IF_HDR_MSG("get_if_data(): Parsed if header: %s:\n" "%O\n", raw_header, res);
76d1192004-05-05Henrik Grubbström (Grubba)  return if_data = res; }
24e9142003-04-22Henrik Grubbström (Grubba) 
c7429a2009-04-17Martin Stjernholm  int get_max_cache() //! Returns the maximum cacheable time in seconds. See //! @expr{@[misc]->cacheable@}. { return misc->cacheable; } int lower_max_cache (int seconds) //! Lowers the maximum cacheable time to @[seconds] if it currently //! has a higher value. Returns the old value. See //! @expr{@[misc]->cacheable@}. { if (mapping(mixed:int) lc = misc->local_cacheable) foreach (lc; mixed ind; int old) if (seconds < old) lc[ind] = seconds; int old = misc->cacheable; if (seconds < old) { object/*(RXML.Context)*/ ctx = RXML_CONTEXT; if (ctx && ctx->id == this) ctx->set_id_misc ("cacheable", seconds); else misc->cacheable = seconds; #ifdef DEBUG_CACHEABLE object frame = backtrace()[-2]; report_debug ("%s:%d: Lower cacheable to %d (was %d)\n", frame[0], frame[1], seconds, old); #endif } #ifdef DEBUG_CACHEABLE else { object frame = backtrace()[-2]; report_debug ("%s:%d: Not lowering cacheable to %d (is %d)\n", frame[0], frame[1], seconds, old); } #endif return old; } int raise_max_cache (int seconds) //! Raises the maximum cacheable time to @[seconds] if it currently //! has a lower value. Returns the old value. See //! @expr{@[misc]->cacheable@}. { if (mapping(mixed:int) lc = misc->local_cacheable) foreach (lc; mixed ind; int old) if (seconds > old) lc[ind] = seconds; int old = misc->cacheable; if (seconds > old) { object/*(RXML.Context)*/ ctx = RXML_CONTEXT; if (ctx && ctx->id == this) ctx->set_id_misc ("cacheable", seconds); else misc->cacheable = seconds; #ifdef DEBUG_CACHEABLE object frame = backtrace()[-2]; report_debug ("%s:%d: Raise cacheable to %d (was %d)\n", frame[0], frame[1], seconds, old); #endif } #ifdef DEBUG_CACHEABLE else { object frame = backtrace()[-2]; report_debug ("%s:%d: Not raising cacheable to %d (is %d)\n", frame[0], frame[1], seconds, old); } #endif return old; } int set_max_cache( int seconds ) //! Sets the maximum cacheable time to @[seconds]. Returns the old //! value. See @expr{@[misc]->cacheable@}. { if (mapping(mixed:int) lc = misc->local_cacheable) foreach (lc; mixed ind;) lc[ind] = seconds; int old = misc->cacheable; if (seconds != old) { object/*(RXML.Context)*/ ctx = RXML_CONTEXT; if (ctx && ctx->id == this) ctx->set_id_misc ("cacheable", seconds); else misc->cacheable = seconds; } #ifdef DEBUG_CACHEABLE object frame = backtrace()[-2]; report_debug ("%s:%d: Set cacheable to %d (was %d)\n", frame[0], frame[1], seconds, old); #endif return old; }
0f06502005-12-02Henrik Grubbström (Grubba)  //! Register that the result was dependant on the request header //! specified by @[vary], and/or a callback @[cb] that generates //! a key that can be used as a decision variable. //! //! The headers registred in @[vary] during the requests parsing //! will be used to generate the vary header (RFC 2068 14.43) for //! the result. //! //! @param vary //! Either a (lower-case) string that specifies the name of //! a request header, or @tt{0@} (zero) which specifies that //! it doesn't depend on a header, but something else (eg //! the IP-number of the client (and thus will generate a //! @tt{"*"@} vary header. //! //! @param cb //! This function will be called at request time with the //! path of the request, and the @[RequestID]. It should
4120f32007-01-03Henrik Grubbström (Grubba)  //! return a key fragment (either a string or an integer). If //! @[cb] is not specified, it will default to a function that //! returns the value of the request header specified by @[vary].
0f06502005-12-02Henrik Grubbström (Grubba)  //! //! @note //! The order of calls to @[register_vary_callback()] is significant //! since it will be used to create a decision tree. //! //! Note also that the function @[cb] should be fast, and avoid //! excessive lengths in the returned key, to keep down on //! perfomance issues. //! //! Caveat! The callback function gets called very early in //! the request processing, so not all fields in the @[RequestID] //! object may be valid yet. //! //! @seealso
3d76f52009-04-28Henrik Grubbström (Grubba)  //! @[NOCACHE()], @[propagate_vary_callbacks()]
0f06502005-12-02Henrik Grubbström (Grubba)  void register_vary_callback(string|void vary,
4120f32007-01-03Henrik Grubbström (Grubba)  function(string, RequestID: string|int)|void cb)
0f06502005-12-02Henrik Grubbström (Grubba)  {
6e6a772011-02-07Henrik Grubbström (Grubba)  if (!(vary || cb)) { error("Vary: At least one of the arguments MUST be specified.\n"); }
baf7672006-08-15Marcus Wellhardh  // Don't generate a vary header for the Host header.
e3589d2008-01-10Martin Stjernholm  if (vary != "host") {
baf7672006-08-15Marcus Wellhardh  if (!misc->vary) { misc->vary = (< vary || "*" >); } else { misc->vary[vary || "*"] = 1; }
0f06502005-12-02Henrik Grubbström (Grubba)  } if (!misc->vary_cb_set) {
a0f4712008-01-10Martin Stjernholm  misc->vary_cb_set = cb ? ([vary: (<cb>)]) : ([vary: 1]);
0f06502005-12-02Henrik Grubbström (Grubba)  misc->vary_cb_order = ({ cb || vary });
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("register_vary_callback(%O, %O)\n", vary, cb);
0f06502005-12-02Henrik Grubbström (Grubba)  return; }
a0f4712008-01-10Martin Stjernholm  if (multiset(function(string,RequestID:string|int))|int(1..1) old = misc->vary_cb_set[vary]) { if (old == 1) { // The full header has already been registred. VARY_WERROR("register_vary_callback(%O, %O) Full header\n", vary, cb); return; } else if (old[cb]) { // Already registred. VARY_WERROR("register_vary_callback(%O, %O) Duplicate\n", vary, cb); return; } else if (!cb) { // Registering full header now - remove all callbacks. misc->vary_cb_order = misc->vary_cb_order - (array) old + ({vary}); misc->vary_cb_set[vary] = 1; VARY_WERROR("register_vary_callback(%O, 0) Removed old cbs\n", vary); return; } old[cb] = 1;
0f06502005-12-02Henrik Grubbström (Grubba)  }
a0f4712008-01-10Martin Stjernholm  else misc->vary_cb_set[vary] = cb ? (<cb>) : 1;
0f06502005-12-02Henrik Grubbström (Grubba)  misc->vary_cb_order += ({ cb || vary });
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("register_vary_callback(%O, %O)\n", vary, cb);
0f06502005-12-02Henrik Grubbström (Grubba)  }
5473bf2008-01-10Martin Stjernholm  void unregister_vary_callback (string vary, void|function(string,RequestID:string|int) cb) //! Unregisters a dependency on a request header or a specific vary //! callback. @[vary] and @[cb] should be the same arguments that //! were previously passed to @[register_vary_callback]. If @[cb] is //! zero then all callbacks registered for the @[vary] header are //! unregistered. //! //! @note
3d76f52009-04-28Henrik Grubbström (Grubba)  //! Try to avoid this function. It's ugly practice. //! //! @seealso //! @[register_vary_callback()]
5473bf2008-01-10Martin Stjernholm  { if (misc->vary) misc->vary[vary || "*"] = 0; if (!misc->vary_cb_set) { VARY_WERROR ("unregister_vary_callback (%O, %O) " "Got no vary registrations\n", vary, cb); return; } if (multiset(function(string,RequestID:string|int))|int(1..1) old = misc->vary_cb_set[vary]) { if (multisetp (old)) { if (cb) { if (old[cb]) { misc->vary_cb_order -= ({cb}); old[cb] = 0;
a0a0b12008-01-11Martin Stjernholm  if (!sizeof (old)) m_delete (misc->vary_cb_set, vary);
5473bf2008-01-10Martin Stjernholm  VARY_WERROR ("unregister_vary_callback (%O, %O) " "Removed callback\n", vary, cb); } else { VARY_WERROR ("unregister_vary_callback (%O, %O) " "Callback wasn't registered\n", vary, cb); } } else { misc->vary_cb_order -= (array) old; m_delete (misc->vary_cb_set, vary); VARY_WERROR ("unregister_vary_callback (%O, 0) " "Removed %d callbacks\n", vary, sizeof (old)); } } else { if (cb) { VARY_WERROR ("unregister_vary_callback (%O, %O) " "Callback wasn't registered\n", vary, cb); } else { misc->vary_cb_order -= ({vary}); m_delete (misc->vary_cb_set, vary); VARY_WERROR ("unregister_vary_callback (%O, 0) " "Removed header\n", vary); } } } }
3d76f52009-04-28Henrik Grubbström (Grubba)  //! Propagate vary callbacks from another @[RequestID] object. //! //! This function is typically used when a subrequest has //! been performed to propagate vary callbacks from the //! subrequest to the present request. //! //! @seealso //! @[register_vary_callback()] void propagate_vary_callbacks(RequestID id) { VARY_WERROR("Propagating vary information from %O to %O...\n", id, this_object()); if (id->misc->vary) { mapping(function(string,RequestID:string|int):multiset(string)) reverse_cb_set; foreach(id->misc->vary_cb_order || ({}), string|function(string, RequestID: string|int)|object vary_cb) { if (stringp(vary_cb)) { VARY_WERROR("Propagating vary header %O.\n", vary_cb); register_vary_callback(vary_cb); } else if (objectp(vary_cb) && vary_cb->cookie) { // Update indirectly via the CookieJar. VARY_WERROR("Propagating cookie %O.\n", vary_cb->cookie); mixed ignored = cookies[vary_cb->cookie]; } else { if (!reverse_cb_set) { // This gets complicated... // Build a reverse lookup from callback to the headers.
4baee22009-05-04Martin Jonsson  VARY_WERROR("Building reverse vary lookup.\n");
3d76f52009-04-28Henrik Grubbström (Grubba)  reverse_cb_set = ([]); foreach(id->misc->vary_cb_set || ([]); string vary; multiset(function(string,RequestID:string|int))| int(1..1) cb_info) { if (multisetp(cb_info)) { foreach(cb_info; function(string,RequestID:string|int) cb;) { if (reverse_cb_set[cb]) { reverse_cb_set[cb][vary] = 1; } else { reverse_cb_set[cb] = (< vary >); } } } } } VARY_WERROR("Propagating generic vary callback: %O (headers: %O)\n", vary_cb, reverse_cb_set[vary_cb]); foreach(reverse_cb_set[vary_cb] || (< 0 >); string vary;) { register_vary_callback(vary, vary_cb); } } } } VARY_WERROR("Propagation of vary information from %O to %O complete.\n", id, this_object()); }
2adb8a2009-06-12Martin Stjernholm  protected array(object) threadbound_session_objects; void add_threadbound_session_object (object obj) //! This can be used to register some kind of session object (e.g. a //! mutex lock) which will have the same lifetime as this request in //! the current thread. I.e. @[obj] will be destructed when a //! handler thread is done with this request, or when this object is //! destructed, whichever comes first. //! //! @[obj] is destructed even if a handler thread is aborted due to //! an exception. It is not an error if @[obj] already is destructed //! when the lifetime ends. //! //! @note //! If you want to be able to retrieve @[obj] later, put it into //! @[misc] too. { if (threadbound_session_objects) threadbound_session_objects += ({obj}); else threadbound_session_objects = ({obj}); } int remove_threadbound_session_object (object obj) //! Removes an object which has earlier been passed to //! @[add_threadbound_session_object]. //! //! @returns //! 1 if @[obj] was among the threadbound session objects, zero //! otherwise. { if (!threadbound_session_objects) return 0; int s = sizeof (threadbound_session_objects); threadbound_session_objects -= ({obj}); return sizeof (threadbound_session_objects) != s; } void destruct_threadbound_session_objects() //! Explicitly destructs all objects registered with //! @[add_threadbound_session_object]. Should normally only be used //! internally. {
fdb9102010-08-19Martin Stjernholm  if (array(object) objs = this && threadbound_session_objects) {
2adb8a2009-06-12Martin Stjernholm  threadbound_session_objects = 0; foreach (objs, object obj) { if (mixed err = catch { if (obj) // Relying on the interpreter lock here. destruct (obj); }) master()->handle_error (err); } } }
63b8b92017-10-13Karl Gustav Sterneberg  protected variant string find_in_misc_forwarded(string key, bool|void case_insensitive) //! Wrapper for //! find_in_misc_forwarded(multiset(string) keys, bool|void case_insensitive) { return find_in_misc_forwarded((<key>), case_insensitive)[key]; } protected variant mapping(string:string) find_in_misc_forwarded(multiset(string) keys, bool|void case_insensitive) //! If @[case_insensitive] is true, indeces in arg @[keys] will be converted //! to lower case. Indeces in returned mapping will also be only in lower //! case. { keys += (<>); // Shallow copy is ok since keys are strings. if (case_insensitive) { for (keys; string key;) { keys[key] = 0; keys[lower_case(key)] = 1; } } mapping(string:string) map_ = ([]);
33c6ef2017-10-09Karl Gustav Sterneberg  foreach(misc->forwarded || ({}), array(int|string) entry) { foreach(entry/ ({ ';' }), array(int|string) forwarded_pair) { if ((sizeof(forwarded_pair) != 3) || (forwarded_pair[1] != '=') || !stringp(forwarded_pair[0]) || !stringp(forwarded_pair[2])) { continue; } string key = lower_case(forwarded_pair[0]);
63b8b92017-10-13Karl Gustav Sterneberg  if (case_insensitive) { key = lower_case(key); } if (keys[key]) { // If keys contained key add key and value to map_. map_[key] = forwarded_pair[2]; // Do not try to find another value for this key. keys[key] = 0;
33c6ef2017-10-09Karl Gustav Sterneberg  }
63b8b92017-10-13Karl Gustav Sterneberg  if (!sizeof(keys)) { return map_;
33c6ef2017-10-09Karl Gustav Sterneberg  } } }
63b8b92017-10-13Karl Gustav Sterneberg  return map_;
33c6ef2017-10-09Karl Gustav Sterneberg  }
b24b392017-10-10Karl Gustav Sterneberg  string client_scheme() //! The scheme used by the client. Typically http or https. If the client //! talks https with a proxy and the proxy talkes http with the server, this //! function returns the string "https".
e5cbd72017-10-16Karl Gustav Sterneberg  //! Returns 0 if client scheme cannot be found.
33c6ef2017-10-09Karl Gustav Sterneberg  {
63b8b92017-10-13Karl Gustav Sterneberg  string scheme = find_in_misc_forwarded("proto", true);
b24b392017-10-10Karl Gustav Sterneberg  if (!scheme) {
e5cbd72017-10-16Karl Gustav Sterneberg  scheme = port_obj?->prot_name;
33c6ef2017-10-09Karl Gustav Sterneberg  }
e5cbd72017-10-16Karl Gustav Sterneberg  if (scheme) { scheme = lower_case(scheme); } return scheme;
33c6ef2017-10-09Karl Gustav Sterneberg  }
fc40392008-08-15Martin Stjernholm  protected string cached_url_base;
5430602001-07-21Martin Stjernholm  string url_base() //! Returns the base part of the URL, i.e. what should be added in //! front of a path in the virtual filesystem to get the absolute
3c49612001-07-21Martin Stjernholm  //! URL to the page. The returned string ends with a "/", or is "" //! if no server base could be found.
5430602001-07-21Martin Stjernholm  //! //! This function gets the correct host for protocols that handles //! IP-less hosts. { if (!cached_url_base) {
c5a2bd2016-05-25Henrik Grubbström (Grubba)  string host; string scheme; // We're looking at the forwarded header... register_vary_callback("forwarded"); // First look at the forwarded header. if (misc->forwarded) {
63b8b92017-10-13Karl Gustav Sterneberg  mapping(string:string) forwarded = find_in_misc_forwarded((<"proto", "host">), true);
33c6ef2017-10-09Karl Gustav Sterneberg  scheme = forwarded->proto ? lower_case(forwarded->proto) : 0; host = forwarded->host ? forwarded->host : 0;
c5a2bd2016-05-25Henrik Grubbström (Grubba)  }
5430602001-07-21Martin Stjernholm 
c5a2bd2016-05-25Henrik Grubbström (Grubba)  // Second look at the host header in the request. if (!host) { // We're looking at the host header... register_vary_callback("host"); host = misc->host; }
52ab412006-09-25Henrik Grubbström (Grubba) 
c5a2bd2016-05-25Henrik Grubbström (Grubba)  // Then try the port object. if (!scheme) { scheme = port_obj->prot_name; } if (!host) { mapping(string:mixed) conf_data = port_obj->conf_data[conf]; if (conf_data) { host = conf_data->hostname; if (host == "*") // Use the hostname in the configuration url. // Fall back to the numeric ip. host = conf->get_host() || port_obj->ip; if (port_obj->port != port_obj->default_port) { host += ":" + port_obj->port; } } } else { string host_no_port; int port;
52ab412006-09-25Henrik Grubbström (Grubba) 
c5a2bd2016-05-25Henrik Grubbström (Grubba)  if (has_prefix(host, "[")) {
dc89282008-12-11Jonas Wallden  // IPv6
c5a2bd2016-05-25Henrik Grubbström (Grubba)  sscanf(host, "[%s]:%d", host_no_port, port);
52ab412006-09-25Henrik Grubbström (Grubba)  } else {
c5a2bd2016-05-25Henrik Grubbström (Grubba)  sscanf(host, "%[^:]:%d", host_no_port, port); } if (port == ([ "http":80, "https":443 ])[scheme]) { // Default port. port = 0; host = host_no_port;
52ab412006-09-25Henrik Grubbström (Grubba)  } }
a153e72009-01-22Martin Stjernholm 
c5a2bd2016-05-25Henrik Grubbström (Grubba)  if (host) { cached_url_base = scheme + "://" + host;
5430602001-07-21Martin Stjernholm  }
3c49612001-07-21Martin Stjernholm  // Then try the configuration url.
c5a2bd2016-05-25Henrik Grubbström (Grubba)  else if (conf && sizeof (host = conf->get_url())) cached_url_base = host[..sizeof(host) - 2]; // Remove trailing '/'.
5430602001-07-21Martin Stjernholm 
3c49612001-07-21Martin Stjernholm  // Lastly use a pathetic fallback. With this the produced urls // will still be relative, which has some chance of working. else return cached_url_base = "";
5430602001-07-21Martin Stjernholm  if (string p = misc->site_prefix_path) cached_url_base += p; cached_url_base += "/"; } return cached_url_base; }
f7ea0c2002-02-06Martin Stjernholm  void add_response_header (string name, string value)
b421932002-01-29Martin Stjernholm  //! Adds a header @[name] with the value @[value] to be sent in the //! http response. An existing header with the same name will not be
80f24a2008-06-12Martin Stjernholm  //! overridden, instead another (duplicate) header line with //! @[value] will be sent in the response, after the old one(s). //! However, if a header with the same name and value already exists //! then another one isn't added.
b421932002-01-29Martin Stjernholm  //! //! @note //! If used from within an RXML parse session, this function will //! ensure that the new header is registered properly in the RXML //! p-code cache. That's the primary reason to used it instead of
dfb4862008-06-18Martin Stjernholm  //! adding the header directly to
562fb32012-05-10Henrik Grubbström (Grubba)  //! @tt{misc->defines[" _extra_heads"]@} and/or @tt{misc->moreheads@}.
dfb4862008-06-18Martin Stjernholm  //! //! @note //! Although case is insignificant in http header names, it is //! significant here. @[name] should always follow the
1003fd2009-04-03Martin Stjernholm  //! capitalization used in RFC 2616. Use //! @[Roxen.canonicalize_http_header] if necessary.
dfb4862008-06-18Martin Stjernholm  //! //! @seealso
1003fd2009-04-03Martin Stjernholm  //! @[set_response_header], @[add_or_set_response_header], //! @[get_response_headers], @[remove_response_headers]
f7ea0c2002-02-06Martin Stjernholm  {
562fb32012-05-10Henrik Grubbström (Grubba)  mapping(string:string|array(string)) hdrs = misc->moreheads;
f7ea0c2002-02-06Martin Stjernholm  if (!hdrs) hdrs = misc->moreheads = ([]); // Essentially Roxen.add_http_header inlined. Can't refer to it // from here due to the recursive resolver problems in Pike. array|string cur_val = hdrs[name]; if(cur_val) { if(arrayp(cur_val)) { if (!has_value(cur_val, value)) cur_val += ({ value }); } else { if (cur_val != value) cur_val = ({ cur_val, value }); } } else cur_val = value;
b166152012-05-10Henrik Grubbström (Grubba)  object /*(RXML.Context)*/ ctx;
562fb32012-05-10Henrik Grubbström (Grubba)  if (misc->defines && misc->defines[" _extra_heads"] &&
b166152012-05-10Henrik Grubbström (Grubba)  (ctx = RXML_CONTEXT))
f7ea0c2002-02-06Martin Stjernholm  ctx->set_var (name, cur_val, "header"); else hdrs[name] = cur_val; }
b421932002-01-29Martin Stjernholm 
f7ea0c2002-02-06Martin Stjernholm  void set_response_header (string name, string value)
b421932002-01-29Martin Stjernholm  //! Sets the header @[name] to the value @[value] to be sent in the //! http response. If an existing header with the same name exists, //! its value(s) will be overridden. This is useful for headers like
dfb4862008-06-18Martin Stjernholm  //! @expr{"Expire-Time"@}, otherwise @[add_response_header] is //! typically a better choice.
b421932002-01-29Martin Stjernholm  //! //! @note //! If used from within an RXML parse session, this function will //! ensure that the new header is registered properly in the RXML //! p-code cache. That's the primary reason to used it instead of
dfb4862008-06-18Martin Stjernholm  //! adding the header directly to //! @tt{misc->defines[" _extra_heads"]@} or @tt{misc->moreheads@}. //! //! @note //! Although case is insignificant in http header names, it is //! significant here. @[name] should always follow the
2f4b7f2009-07-14Martin Stjernholm  //! capitalization used in RFC 2616. Use //! @[Roxen.canonicalize_http_header] if necessary.
dfb4862008-06-18Martin Stjernholm  //! //! @seealso
1003fd2009-04-03Martin Stjernholm  //! @[add_or_set_response_header], @[get_response_headers], //! @[remove_response_headers]
f7ea0c2002-02-06Martin Stjernholm  {
562fb32012-05-10Henrik Grubbström (Grubba)  if (!misc->moreheads) misc->moreheads = ([]); misc->moreheads[name] = value;
b166152012-05-10Henrik Grubbström (Grubba)  object/*(RXML.Context)*/ ctx;
562fb32012-05-10Henrik Grubbström (Grubba)  if (misc->defines && misc->defines[" _extra_heads"] &&
b166152012-05-10Henrik Grubbström (Grubba)  (ctx = RXML_CONTEXT))
562fb32012-05-10Henrik Grubbström (Grubba)  ctx->signal_var_change (name, "header", value);
f7ea0c2002-02-06Martin Stjernholm  }
b421932002-01-29Martin Stjernholm 
1003fd2009-04-03Martin Stjernholm  void add_or_set_response_header (string name, string value) //! Calls either @[add_response_header] or @[set_response_header] as //! appropriate for the specific header: If a header is known to //! allow multiple values (from RFC 2616) then //! @[add_response_header] is called to add another value to it, //! otherwise @[set_response_header] is called to override the old //! value if there is any. { if ((["Accept-Ranges": 1, "Allow": 1, "Cache-Control": 1, "Connection": 1, "Content-Encoding": 1, "Content-Language": 1, "Pragma": 1, "Proxy-Authenticate": 1,
562fb32012-05-10Henrik Grubbström (Grubba)  "Set-Cookie": 1,
1003fd2009-04-03Martin Stjernholm  "Trailer": 1, "Transfer-Encoding": 1, "Upgrade": 1, "Vary": 1, "Via": 1, "Warning": 1, "WWW-Authenticate": 1, ])[name]) add_response_header (name, value); else set_response_header (name, value); }
fc40392008-08-15Martin Stjernholm  protected constant http_nontoken_chars = ([
dfb4862008-06-18Martin Stjernholm  0:1, 1:1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:1, 8:1, 9:1, 10:1, 11:1, 12:1, 13:1, 14:1, 15:1, 16:1, 17:1, 18:1, 19:1, 20:1, 21:1, 22:1, 23:1, 24:1, 25:1, 26:1, 27:1, 28:1, 29:1, 30:1, 31:1, '(':1, ')':1, '<':1, '>':1, '@':1, ',':1, ';':1, ':':1, '\\':1, '"':1, '/':1, '[':1, ']':1, '?':1, '=':1, '{':1, '}':1, ' ':1, '\t':1]); #define MATCH_TOKEN_PREFIX(VAL, PREFIX) \ (VAL == PREFIX || \ has_prefix (VAL, PREFIX) && http_nontoken_chars[VAL[sizeof (PREFIX)]]) array(string) get_response_headers (string name, void|string value_prefix) //! Returns the values of all headers with the given @[name] in the //! set of headers that are to be sent in the response. //! //! If @[value_prefix] is given then it must match a prefix of the //! header value up to a token boundary for the value to be //! returned. //! //! @returns //! Returns an array containing the matching values (zero or more). //! //! @note //! This function only searches through headers in //! @tt{misc->defines[" _extra_heads"]@} and @tt{misc->moreheads@}, //! i.e. headers that have been set using @[add_response_header] or //! @[set_response_header]. {
562fb32012-05-10Henrik Grubbström (Grubba)  mapping(string:string|array(string)) hdrs = misc->moreheads; if (hdrs) { if (array(string)|string cur_val = hdrs[name]) { if (!value_prefix) return arrayp (cur_val) ? cur_val : ({cur_val}); else { if (arrayp (cur_val)) { array(string) res = ({}); foreach (cur_val, string val) if (MATCH_TOKEN_PREFIX (val, value_prefix)) res += ({val}); return res;
dfb4862008-06-18Martin Stjernholm  }
562fb32012-05-10Henrik Grubbström (Grubba)  else if (MATCH_TOKEN_PREFIX (cur_val, value_prefix)) return ({cur_val});
dfb4862008-06-18Martin Stjernholm  } }
562fb32012-05-10Henrik Grubbström (Grubba)  }
dfb4862008-06-18Martin Stjernholm 
562fb32012-05-10Henrik Grubbström (Grubba)  return ({});
dfb4862008-06-18Martin Stjernholm  } int remove_response_headers (string name, void|string value_prefix) //! Removes a header with the given @[name] from the set of headers //! that are to be sent in the response. //! //! If @[value_prefix] is given then it must match a prefix of the //! header value up to a token boundary for the header to be //! removed, otherwise all headers with the given name are removed. //! //! @returns //! Returns nonzero if at least one header was removed. //! //! @note //! This function only removes headers in
562fb32012-05-10Henrik Grubbström (Grubba)  //! @tt{misc->defines[" _extra_heads"]@} and/or @tt{misc->moreheads@},
dfb4862008-06-18Martin Stjernholm  //! i.e. headers that have been set using @[add_response_header] or //! @[set_response_header]. It's possible that a matching header //! gets sent anyway if it gets added later or through other means //! (e.g. through @tt{"extra_heads"@} in a response mapping). { int removed;
562fb32012-05-10Henrik Grubbström (Grubba)  mapping(string:string|array(string)) hdrs = misc->moreheads; if (hdrs) { if (array(string)|string cur_val = hdrs[name]) { if (!value_prefix) { m_delete (hdrs, name); removed = 1; } else { if (arrayp (cur_val)) { foreach (cur_val; int i; string val) if (MATCH_TOKEN_PREFIX (val, value_prefix)) { cur_val[i] = 0;
dfb4862008-06-18Martin Stjernholm  removed = 1; }
562fb32012-05-10Henrik Grubbström (Grubba)  cur_val -= ({0}); if (sizeof (cur_val)) hdrs[name] = cur_val; else m_delete (hdrs, name);
dfb4862008-06-18Martin Stjernholm  }
562fb32012-05-10Henrik Grubbström (Grubba)  else if (MATCH_TOKEN_PREFIX (cur_val, value_prefix)) { m_delete (hdrs, name); removed = 1; }
dfb4862008-06-18Martin Stjernholm  } }
562fb32012-05-10Henrik Grubbström (Grubba)  }
dfb4862008-06-18Martin Stjernholm  return removed; }
fc40392008-08-15Martin Stjernholm  protected MultiStatus multi_status;
ae77c42004-05-05Martin Stjernholm  MultiStatus get_multi_status()
8cedaa2004-05-07Martin Stjernholm  //! Returns a @[MultiStatus] object that will be used to produce a
a5303e2004-06-28Martin Stjernholm  //! 207 Multi-Status response (RFC 2518 10.2). It's only consulted
8cedaa2004-05-07Martin Stjernholm  //! if the result returned from @[RoxenModule.find_file] et al is an //! empty mapping.
ae77c42004-05-05Martin Stjernholm  //! //! @note //! It's not necessarily safe to assume that there aren't any //! multi-status responses stored here already on entry to
8cedaa2004-05-07Martin Stjernholm  //! @[find_file] et al. C.f. @[multi_status_size].
ae77c42004-05-05Martin Stjernholm  //! //! @seealso //! @[set_status_for_path] { if (!multi_status) multi_status = MultiStatus(); return multi_status; } int multi_status_size() //! Returns the number responses that have been added to the //! @[MultiStatus] object returned by @[get_multi_status]. Useful to //! see whether a (part of a) recursive operation added any errors //! or other results. { return multi_status && multi_status->num_responses(); } void set_status_for_path (string path, int status_code, string|void message, mixed... args) //! Register a status to be included in the response that applies //! only for the given path. This is used for recursive operations //! that can yield different results for different encountered files //! or directories. //! //! The status is stored in the @[MultiStatus] object returned by //! @[get_multi_status]. //! //! @param path //! Absolute path in the configuration to which the status //! applies. Note that filesystem modules need to prepend their //! location to their internal paths. //! //! @param status_code //! The HTTP status code. //! //! @param message //! If given, it's a message to include in the response. The //! message may contain line feeds ('\n') and ISO-8859-1
197e812004-05-06Martin Stjernholm  //! characters in the ranges 32..126 and 128..255. Line feeds are //! converted to spaces if the response format doesn't allow them.
ae77c42004-05-05Martin Stjernholm  //! //! @param args //! If there are more arguments after @[message] then @[message] //! is taken as an @[sprintf] style format string which is used to //! format @[args]. //! //! @seealso //! @[Roxen.http_status] { if (sizeof (args)) message = sprintf (message, @args); ASSERT_IF_DEBUG (has_prefix (path, "/")); get_multi_status()->add_status (url_base() + path[1..], status_code, message); }
55c6082018-03-27Henrik Grubbström (Grubba)  variant void set_status_for_path (string path, mapping(string:mixed) ret) //! Register a status to be included in the response that applies //! only for the given path. This is used for recursive operations //! that can yield different results for different encountered files //! or directories. //! //! The status is stored in the @[MultiStatus] object returned by //! @[get_multi_status]. //! //! @param path //! Absolute path in the configuration to which the status //! applies. Note that filesystem modules need to prepend their //! location to their internal paths. //! //! @param ret //! Result-mapping for the path. //! //! @seealso //! @[Roxen.http_status] { ASSERT_IF_DEBUG (has_prefix (path, "/")); get_multi_status()->add_status (url_base() + path[1..], ret); }
ed2fa42018-03-28Henrik Grubbström (Grubba)  // NB: object below to avoid forward reference with variants... variant void set_status_for_path (string path, object status) //! @decl void set_status_for_path (string path, MultiStatusNode status) //! Register a status to be included in the response that applies //! only for the given path. This is used for recursive operations //! that can yield different results for different encountered files //! or directories. //! //! The @[status] is stored in the @[MultiStatus] object returned by //! @[get_multi_status]. //! //! @param path //! Absolute path in the configuration to which the status //! applies. Note that filesystem modules need to prepend their //! location to their internal paths. //! //! @param status //! Status object for the @[path]. //! //! @seealso //! @[Roxen.http_status] { ASSERT_IF_DEBUG (has_prefix (path, "/")); get_multi_status()->add_status (url_base() + path[1..], status); }
ae77c42004-05-05Martin Stjernholm  void set_status_for_url (string url, int status_code, string|void message, mixed... args) //! Register a status to be included in the response that applies //! only for the given URL. Similar to @[set_status_for_path], but //! takes a complete URL instead of an absolute path within the //! configuration. { if (sizeof (args)) message = sprintf (message, @args); get_multi_status()->add_status (url, status_code, message); }
55c6082018-03-27Henrik Grubbström (Grubba)  variant void set_status_for_url (string url, mapping(string:mixed) ret) //! Register a status to be included in the response that applies //! only for the given URL. Similar to @[set_status_for_path], but //! takes a complete URL instead of an absolute path within the //! configuration. { get_multi_status()->add_status (url, ret); }
ed2fa42018-03-28Henrik Grubbström (Grubba)  // NB: object below to avoid forward reference with variants... variant void set_status_for_url (string url, object status) //! @decl void set_status_for_url (string url, MultiStatusNode status) //! Register a status to be included in the response that applies //! only for the given URL. Similar to @[set_status_for_path], but //! takes a complete URL instead of an absolute path within the //! configuration. { get_multi_status()->add_status (url, status); }
48b4112003-02-05Jonas Wallden  // Charset handling
d5123a2017-10-09Karl Gustav Sterneberg 
6322612004-05-06Martin Stjernholm  array(string) output_charset = ({});
9689f12002-09-03Martin Stjernholm  void set_output_charset( string|function to, int|void mode ) {
d5aab82007-08-15Martin Stjernholm #ifdef DEBUG if (stringp (to)) // This will throw an error if the charset is invalid.
50aa162015-04-28Jonas Walldén  Charset.encoder (to);
d5aab82007-08-15Martin Stjernholm #endif
80f43b2003-03-24Martin Stjernholm  if (object/*(RXML.Context)*/ ctx = RXML_CONTEXT) ctx->add_p_code_callback ("set_output_charset", to, mode);
ba84e12008-02-18Martin Stjernholm  if( has_value (output_charset, to) ) // Already done.
9689f12002-09-03Martin Stjernholm  return;
6322612004-05-06Martin Stjernholm  switch( mode )
9689f12002-09-03Martin Stjernholm  { case 0: // Really set. output_charset = ({ to }); break; case 1: // Only set if not already set. if( !sizeof( output_charset ) ) output_charset = ({ to }); break; case 2: // Join.
66d3632005-04-12Anders Johansson  if (sizeof (output_charset) && (!stringp (to) || !stringp (output_charset[0]))) error ("Can't join charsets with functions (%O with %O).\n", to, output_charset[0]);
ba84e12008-02-18Martin Stjernholm  output_charset += ({ to });
9689f12002-09-03Martin Stjernholm  break; } }
66d3632005-04-12Anders Johansson  string|function(string:string) get_output_charset() { string charset; function(string:string) encoder; foreach( output_charset, string|function f ) [charset,encoder] = join_charset(charset, f, encoder, 0); return charset || encoder; }
d5123a2017-10-09Karl Gustav Sterneberg 
fc40392008-08-15Martin Stjernholm  protected string charset_name(function|string what)
48b4112003-02-05Jonas Wallden  {
e4ee812013-11-14Henrik Grubbström (Grubba)  if (what == string_to_unicode) return "ISO10646-1"; if (what == string_to_utf8) return "UTF-8"; return upper_case((string) what);
48b4112003-02-05Jonas Wallden  }
fc40392008-08-15Martin Stjernholm  protected function charset_function(function|string what, int allow_entities)
48b4112003-02-05Jonas Wallden  {
e4ee812013-11-14Henrik Grubbström (Grubba)  if (functionp(what)) return what;
7e9ff72018-02-23Martin Karlgren  switch (upper_case(what)) {
48b4112003-02-05Jonas Wallden  case "ISO-10646-1": case "ISO10646-1":
7e9ff72018-02-23Martin Karlgren  case "ISO-10646": case "ISO10646":
48b4112003-02-05Jonas Wallden  return string_to_unicode;
d5123a2017-10-09Karl Gustav Sterneberg 
48b4112003-02-05Jonas Wallden  case "UTF-8":
7e9ff72018-02-23Martin Karlgren  case "UTF8":
48b4112003-02-05Jonas Wallden  return string_to_utf8;
d5123a2017-10-09Karl Gustav Sterneberg 
48b4112003-02-05Jonas Wallden  default:
4f47ab2007-08-15Martin Stjernholm  // Use entity fallback if content type allows it function fallback_func = allow_entities && lambda(string char) { return sprintf("&#x%x;", char[0]); };
d5123a2017-10-09Karl Gustav Sterneberg 
4f47ab2007-08-15Martin Stjernholm  _charset_decoder_func = _charset_decoder_func || Roxen->_charset_decoder; return
50aa162015-04-28Jonas Walldén  _charset_decoder_func(Charset.encoder((string) what, "", fallback_func))
4f47ab2007-08-15Martin Stjernholm  ->decode;
48b4112003-02-05Jonas Wallden  } }
d5123a2017-10-09Karl Gustav Sterneberg 
fc40392008-08-15Martin Stjernholm  protected array(string) join_charset(string old, function|string add, function oldcodec, int allow_entities)
48b4112003-02-05Jonas Wallden  { switch (old && upper_case(old)) { case 0:
ba84e12008-02-18Martin Stjernholm  case "ISO8859-1": case "ISO-8859-1":
48b4112003-02-05Jonas Wallden  return ({ charset_name(add), charset_function(add, allow_entities) }); case "ISO10646-1": case "UTF-8": return ({ old, oldcodec }); // Everything goes here. :-) case "ISO-2022": return ({ old, oldcodec }); // Not really true, but how to know this? default: // Not true, but there is no easy way to add charsets yet...
66d3632005-04-12Anders Johansson #if 1 // The safe choice. return ({"UTF-8", string_to_utf8}); #else
48b4112003-02-05Jonas Wallden  return ({ charset_name(add), charset_function(add, allow_entities) });
66d3632005-04-12Anders Johansson #endif
48b4112003-02-05Jonas Wallden  } }
d5123a2017-10-09Karl Gustav Sterneberg 
360c1b2004-06-30Stefan Wallström  array replace_charset_placeholder(string charset, string what, int allow_entities) { // If we allow entities we also replace the automatic charset placeholder with the charset in use if(allow_entities && charset)
7e9fec2004-06-30Stefan Wallström  what = replace(what, Roxen->magic_charset_variable_placeholder, charset);
360c1b2004-06-30Stefan Wallström  return ({ charset, what });
d5123a2017-10-09Karl Gustav Sterneberg  }
48b4112003-02-05Jonas Wallden  array(string) output_encode(string what, int|void allow_entities, string|void force_charset) {
b038b12003-02-19Jonas Wallden  // Performance optimization for unneeded ISO-8859-1 recoding of // strings which already are narrow. if (String.width(what) == 8) { if (force_charset) { if (upper_case(force_charset) == "ISO-8859-1")
360c1b2004-06-30Stefan Wallström  return replace_charset_placeholder("ISO-8859-1", what, allow_entities);
b038b12003-02-19Jonas Wallden  } else {
6322612004-05-06Martin Stjernholm  if (sizeof(output_charset) == 1 &&
b038b12003-02-19Jonas Wallden  upper_case(output_charset[0]) == "ISO-8859-1")
360c1b2004-06-30Stefan Wallström  return replace_charset_placeholder("ISO-8859-1", what, allow_entities);
b038b12003-02-19Jonas Wallden  } }
d5123a2017-10-09Karl Gustav Sterneberg 
48b4112003-02-05Jonas Wallden  if (!force_charset) { string charset; function encoder;
d5123a2017-10-09Karl Gustav Sterneberg 
6322612004-05-06Martin Stjernholm  foreach( output_charset, string|function f )
48b4112003-02-05Jonas Wallden  [charset,encoder] = join_charset(charset, f, encoder, allow_entities); if (!encoder) if (String.width(what) > 8) { charset = "UTF-8"; encoder = string_to_utf8; } if (encoder) what = encoder(what);
360c1b2004-06-30Stefan Wallström  return replace_charset_placeholder( charset, what, allow_entities );
48b4112003-02-05Jonas Wallden  } else return ({ 0,
50aa162015-04-28Jonas Walldén  Charset.encoder((force_charset / "=")[-1])->feed(what)->drain()
48b4112003-02-05Jonas Wallden  }); }
6091362009-03-21Martin Stjernholm  void split_query_vars (string query_vars, mapping(string:array(string)) vars) { array(string) rests = ({}); foreach (query_vars / "&", string v) { if(sscanf(v, "%s=%s", string a, string b) == 2) {
4bda032016-10-28Henrik Grubbström (Grubba)  // NB: Assume invalidly %-encoded values are unencoded. // Fixes [bug 7818]. catch { a = replace(a, "+", " "); a = _Roxen.http_decode_string(a); }; catch { b = replace(b, "+", " "); b = _Roxen.http_decode_string(b); };
6091362009-03-21Martin Stjernholm  vars[ a ] += ({ b });
4bda032016-10-28Henrik Grubbström (Grubba)  } else { catch { v = _Roxen.http_decode_string( v ); }; rests += ({ v }); }
6091362009-03-21Martin Stjernholm  } if (sizeof (rests)) { string rest = rests * "&"; #ifdef ENABLE_IDIOTIC_STUPID_STANDARD // This comes from http.pike with the illustrious comment "IDIOTIC // STUPID STANDARD". I fail to find any standard that says // anything about this, so nowadays it's disabled by default. // /mast rest=replace(rest, "+", "\000"); #endif vars[""] = ({rest}); } } string decode_query_charset (string path, mapping(string:array(string)) vars, string decode_charset) //! Decodes the charset encoding of a query.
ba84e12008-02-18Martin Stjernholm  //!
6091362009-03-21Martin Stjernholm  //! @[path] is the path part of the query. It is returned after //! decoding. @[vars] contains the query variables, and they are //! assigned to @[real_variables] after decoding. Both @[path] and //! @[vars] are assumed to be transport decoded already.
ba84e12008-02-18Martin Stjernholm  //! //! If @[decode_charset] is a string, it defines a charset to decode //! with after the transport encoding has been removed. The string //! can be on the combined form understood by //! @[Roxen.get_decoder_for_client_charset]. //! //! If @[decode_charset] has the special value @expr{"magic"@} then //! the function will look for a variable tuple with //! @expr{magic_roxen_automatic_charset_variable@} and use //! heuristics on that to figure out the character set that //! @[query_string] has been encoded with. See the RXML tag //! @expr{<roxen-automatic-charset-variable>@} and //! @[Roxen.get_client_charset]. //! //! If @[decode_charset] has the special value //! @expr{"roxen-http-default"@} then the function will first try //! the @expr{"magic"@} method above, and if the special variable is //! not found it tries to UTF-8 decode. If there is a decode failure //! then it is ignored and no charset decoding is done (meaning that //! the original charset is effectively assumed to be ISO-8859-1). //! As the name suggests, this is the default method used on //! incoming requests by the http protocol module. //! //! The variable @[input_charset] is set to the charset successfully //! used if @[decode_charset] was given. The charset decoding uses //! an all-or-nothing approach - if there is an error decoding any //! string then no string is decoded. If the function throws an
84ad8b2009-03-21Martin Stjernholm  //! error due to a decode failure then @[real_variables] remain //! unchanged.
ba84e12008-02-18Martin Stjernholm  //! //! @note //! Using @expr{"utf-8"@} as @[decode_charset] reverses the URI //! encoding produced by @[Roxen.http_encode_url] for eight bit and //! wider chars. That is compliant with the IRI standard (RFC 3987) //! and HTML 4.01 (appendix B.2.1).
a896932001-01-03Per Hedbor  {
ba84e12008-02-18Martin Stjernholm  do_decode_charset: if (decode_charset == "roxen-http-default") { array(string) magic = vars->magic_roxen_automatic_charset_variable; decode_charset = magic && Roxen->get_client_charset (magic[0]); function(string:string) decoder = decode_charset ? Roxen->get_decoder_for_client_charset (decode_charset) : utf8_to_string; mapping(string:array(string)) decoded_vars = ([]); if (mixed err = catch {
e868c12008-09-26Martin Stjernholm  path = decoder (path);
6380f42009-03-31Martin Stjernholm  foreach (vars; string var; array(string) vals) { if (vars[var + ".mimetype"]) // Don't decode the value if it has a mime type (which // we assume comes from a multipart/form-data POST). decoded_vars[decoder (var)] = vals; else decoded_vars[decoder (var)] = map (vals, decoder); }
ba84e12008-02-18Martin Stjernholm  }) { #ifdef DEBUG if (decode_charset) report_debug ("Failed to decode query %O using charset %O derived " "from magic_roxen_automatic_charset_variable %O: %s",
6091362009-03-21Martin Stjernholm  raw_url, decode_charset, magic[0], describe_error (err));
ba84e12008-02-18Martin Stjernholm #if 0 else report_debug ("Failed to decode query %O using UTF-8: %s",
6091362009-03-21Martin Stjernholm  raw_url, describe_error (err));
ba84e12008-02-18Martin Stjernholm #endif #endif } else { vars = decoded_vars; input_charset = decode_charset || "utf-8"; // Set this after we're done. } } else if (decode_charset) { if (decode_charset == "magic") { array(string) magic = vars->magic_roxen_automatic_charset_variable; decode_charset = magic && Roxen->get_client_charset (magic[0]); if (!decode_charset) break do_decode_charset; } function(string:string) decoder = Roxen->get_decoder_for_client_charset (decode_charset);
e868c12008-09-26Martin Stjernholm  path = decoder (path);
6091362009-03-21Martin Stjernholm  mapping(string:array(string)) decoded_vars = ([]);
6380f42009-03-31Martin Stjernholm  foreach (vars; string var; array(string) vals) { if (vars[var + ".mimetype"]) // Don't decode the value if it has a mime type (which we // assume comes from a multipart/form-data POST). decoded_vars[decoder (var)] = vals; else decoded_vars[decoder (var)] = map (vals, decoder); }
6091362009-03-21Martin Stjernholm  vars = decoded_vars;
ba84e12008-02-18Martin Stjernholm  input_charset = decode_charset; // Set this after we're done.
a896932001-01-03Per Hedbor  }
ba84e12008-02-18Martin Stjernholm 
6236432008-02-18Martin Stjernholm  if (sizeof (real_variables))
ba84e12008-02-18Martin Stjernholm  foreach (vars; string var; array(string) vals) real_variables[var] += vals;
baf8952008-02-18Martin Stjernholm  else { real_variables = vars; // Must fix the mapping in FakedVariables too. variables = FakedVariables (vars); }
e868c12008-09-26Martin Stjernholm  return path;
ba84e12008-02-18Martin Stjernholm  } string scan_for_query( string f ) {
6091362009-03-21Martin Stjernholm  if(sscanf(f,"%s?%s", f, query) == 2) { mapping(string:array(string)) vars = ([]); split_query_vars (query, vars); f = decode_query_charset (_Roxen.http_decode_string (f), vars, 0); }
a896932001-01-03Per Hedbor  return f; }
4b3d022010-05-25Martin Jonsson  string get_response_content_type (mapping file, void|int(1..1) destructive) { string|array(string) type = file->type; if (mappingp (file->extra_heads) && file->extra_heads["Content-Type"]) { type = file->extra_heads["Content-Type"]; if (destructive) m_delete (file->extra_heads, "Content-Type"); } if (mappingp (misc->moreheads) && misc->moreheads["Content-Type"]) { type = misc->moreheads["Content-Type"]; if (destructive) m_delete (misc->moreheads, "Content-Type"); } if (arrayp (type)) type = type[0]; return type || "text/plain"; }
80f24a2008-06-12Martin Stjernholm  mapping(string:string|array(string)) make_response_headers ( mapping(string:mixed) file)
2e9a312004-04-13Martin Stjernholm  //! Make the response headers from a response mapping for this //! request. The headers associated with transfer modifications of //! the response, e.g. 206 Partial Content and 304 Not Modified, are //! not calculated here. //! //! @note //! Is destructive on @[file] and on various data in the request; //! should only be called once for a @[RequestID] instance.
c2d7aa2004-04-13Martin Stjernholm  {
a7332e2004-04-20Martin Stjernholm  if (!file->stat) file->stat = misc->stat; if(objectp(file->file)) {
50b5a12004-06-16Henrik Grubbström (Grubba)  if(!file->stat && file->file->stat)
a7332e2004-04-20Martin Stjernholm  file->stat = file->file->stat(); if (zero_type(misc->cacheable) && file->file->is_file) { // Assume a cacheablity on the order of the age of the file. misc->cacheable = (predef::time(1) - file->stat[ST_MTIME])/4; } } if( Stat fstat = file->stat ) {
2a86b62004-05-08Henrik Grubbström (Grubba)  if( !file->len && (fstat[1] >= 0) && file->file )
a7332e2004-04-20Martin Stjernholm  file->len = fstat[1]; if ( fstat[ST_MTIME] > misc->last_modified ) misc->last_modified = fstat[ST_MTIME];
d5123a2017-10-09Karl Gustav Sterneberg  }
a7332e2004-04-20Martin Stjernholm  if (!file->error) file->error = Protocols.HTTP.HTTP_OK;
c2d7aa2004-04-13Martin Stjernholm 
4b3d022010-05-25Martin Jonsson  string type = get_response_content_type (file, 1);
de29ac2004-04-14Martin Stjernholm 
a7332e2004-04-20Martin Stjernholm  mapping(string:string) heads = ([]);
14ca442009-02-19Jonas Wallden  // Collect info about language forks and their protocol cache callbacks // and Vary header effects. if (PrefLanguages pl = misc->pref_languages) pl->finalize_delayed_vary(this);
d5123a2017-10-09Karl Gustav Sterneberg 
c2d7aa2004-04-13Martin Stjernholm  if( !zero_type(misc->cacheable) && (misc->cacheable != INITIAL_CACHEABLE) ) { if (!misc->cacheable) { // It expired a year ago. heads["Expires"] = Roxen->http_date( predef::time(1)-31557600 );
0f06502005-12-02Henrik Grubbström (Grubba)  // Force the vary header generated below to be "*".
c077ca2009-07-10Stefan Wallström  misc->vary = (< "*" >);
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("Not cacheable. Force vary *.\n");
c2d7aa2004-04-13Martin Stjernholm  } else heads["Expires"] = Roxen->http_date( predef::time(1)+misc->cacheable ); if (misc->cacheable < INITIAL_CACHEABLE) { // Data with expiry is assumed to have been generated at the // same instant. misc->last_modified = predef::time(1); } } if (misc->last_modified) heads["Last-Modified"] = Roxen->http_date(misc->last_modified);
8382322009-03-26Jonas Wallden  // Only process output encoding if no "; charset=" suffix is available. if (!ct_charset_search->match (type)) {
3b866b2008-03-14Martin Stjernholm  string charset; if( stringp(file->data) ) { if (file->charset) // Join to be on the safe side. set_output_charset (file->charset, 2); if (sizeof (output_charset) || (String.width(file->data) > 8)) {
0a1c8f2009-03-30Martin Stjernholm  sscanf (type, "%[-!#$%&´*+./0-9A-Z^_`a-z{|}~]", string ct);
3b866b2008-03-14Martin Stjernholm  int allow_entities =
0a1c8f2009-03-30Martin Stjernholm  (ct == "text/xml") || (ct == "text/html") || (ct == "application/xml") || sscanf(ct, "application/%*s+xml%*c") == 1;
3b866b2008-03-14Martin Stjernholm  [charset, file->data] = output_encode( file->data, allow_entities ); }
c2d7aa2004-04-13Martin Stjernholm  }
d5123a2017-10-09Karl Gustav Sterneberg 
ae6bf62009-03-27Jonas Wallden  // Only declare charset if we have exact knowledge of it. We cannot // provide a default for other requests since e.g. Firefox will // complain if it receives a charset=ISO-8859-1 header for text data // that starts with a UTF-8 BOM. if (charset) type += "; charset=" + charset;
0c8a5b2008-03-14Martin Stjernholm  }
0f06502005-12-02Henrik Grubbström (Grubba) 
5e803f2014-01-23Henrik Grubbström (Grubba)  if (stringp (file->data)) { if (String.width(file->data) > 8) { // Invalid charset header! // DWIM! eval_status["bad-charset"] = 1; file->data = string_to_utf8(file->data); type = (type/";")[0] + "; charset=utf-8"; }
3b866b2008-03-14Martin Stjernholm  file->len = sizeof (file->data);
5e803f2014-01-23Henrik Grubbström (Grubba)  } heads["Content-Type"] = type;
3b866b2008-03-14Martin Stjernholm 
0d0aca2008-01-16Stefan Wallström #ifndef DISABLE_BYTE_RANGES
0c8a5b2008-03-14Martin Stjernholm  heads["Accept-Ranges"] = "bytes";
0d0aca2008-01-16Stefan Wallström #endif
349ed52005-08-16Martin Stjernholm  heads["Server"] = replace(roxenp()->version(), " ", "_");
2a86b62004-05-08Henrik Grubbström (Grubba)  if (file->error == 500) { // Internal server error. // Make sure the connection is closed to resync. heads["Connection"] = "close"; misc->connection = "close"; } else if( misc->connection )
c2d7aa2004-04-13Martin Stjernholm  heads["Connection"] = misc->connection; if(file->encoding) heads["Content-Encoding"] = file->encoding; heads->Date = Roxen->http_date(predef::time(1)); if(file->expires) heads->Expires = Roxen->http_date(file->expires); //if( file->len > 0 || (file->error != 200) ) heads["Content-Length"] = (string)file->len;
56b5852004-04-13Martin Stjernholm  if (misc->etag) heads->ETag = misc->etag;
c2d7aa2004-04-13Martin Stjernholm #ifdef RAM_CACHE
56b5852004-04-13Martin Stjernholm  if (!misc->etag && file->len &&
c2d7aa2004-04-13Martin Stjernholm  (file->data || file->file) &&
2e9a312004-04-13Martin Stjernholm  file->error == 200 && (<"HEAD", "GET">)[method] &&
711d592016-04-07Jonas Walldén  (file->len < conf->datacache->get_cache_stats()->max_file_size)) {
c2d7aa2004-04-13Martin Stjernholm  string data = ""; if (file->file) { data = file->file->read(file->len); if (file->data && (sizeof(data) < file->len)) { data += file->data[..file->len - (sizeof(data)+1)]; }
2e9a312004-04-13Martin Stjernholm  m_delete(file, "file");
c2d7aa2004-04-13Martin Stjernholm  } else if (file->data) { data = file->data[..file->len - 1]; } file->data = data; heads->ETag = misc->etag =
4e32072009-01-09Stephen R. van den Berg  sprintf("\"%s\"", String.string2hex(Crypto.MD5.hash(data)));
c2d7aa2004-04-13Martin Stjernholm  } #endif /* RAM_CACHE */
3308de2006-08-25Jonas Wallden  if (misc->vary && sizeof(misc->vary)) {
0f06502005-12-02Henrik Grubbström (Grubba)  // Generate a vary header.
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("Full set: %s\n", ((array)misc->vary) * ", ");
7a67792006-07-05Henrik Grubbström (Grubba)  if (!supports->vary) { // Broken support for vary. heads->Vary = "User-Agent";
4120f32007-01-03Henrik Grubbström (Grubba) #ifndef DISABLE_VARY_EXPIRES_FALLBACK
7a67792006-07-05Henrik Grubbström (Grubba)  // It expired a year ago.
3c8ad82006-07-10Henrik Grubbström (Grubba)  heads->Expires = Roxen->http_date(predef::time(1)-31557600);
4120f32007-01-03Henrik Grubbström (Grubba) #endif /* !DISABLE_VARY_EXPIRES_FALLBACK */
08e9b22006-08-16Henrik Grubbström (Grubba)  VARY_WERROR("Vary not supported by the browser.\n");
c077ca2009-07-10Stefan Wallström  } else if (misc->vary["*"]) {
0f06502005-12-02Henrik Grubbström (Grubba)  // Depends on non-headers. heads->Vary = "*"; } else { heads->Vary = ((array)misc->vary) * ", "; }
bb55552004-05-18Henrik Grubbström (Grubba)  }
80f24a2008-06-12Martin Stjernholm  // Note: The following will cause headers with the same names to // be replaced rather than added to. That's not always what one // wants, but otoh it's not clear in which order to merge them // either.
2e9a312004-04-13Martin Stjernholm  if(mappingp(file->extra_heads)) heads |= file->extra_heads; if(mappingp(misc->moreheads)) heads |= misc->moreheads;
c2d7aa2004-04-13Martin Stjernholm  return heads; }
9689f12002-09-03Martin Stjernholm  void adjust_for_config_path( string p ) { if( not_query ) not_query = not_query[ strlen(p).. ]; raw_url = raw_url[ strlen(p).. ]; misc->site_prefix_path = p; }
a896932001-01-03Per Hedbor  void end(string|void s, int|void keepit){} void ready_to_receive(){}
12bbbd2004-05-10Martin Stjernholm  void send_result(mapping|void result) { #ifdef DEBUG error ("send_result not overridden.\n"); #endif }
a896932001-01-03Per Hedbor  RequestID clone_me() { object c,t; c=object_program(t=this_object())(0, port_obj, conf); c->port_obj = port_obj; c->conf = conf;
7fafb82001-07-25Johan Sundström  c->root_id = root_id;
a896932001-01-03Per Hedbor  c->time = time; c->raw_url = raw_url;
ccf8312001-08-15Per Hedbor  c->real_variables = copy_value( real_variables ); c->variables = FakedVariables( c->real_variables );
a896932001-01-03Per Hedbor  c->misc = copy_value( misc ); c->misc->orig = t;
99e65a2003-06-18Tomas Nilsson  c->connection_misc = connection_misc;
a896932001-01-03Per Hedbor  c->prestate = prestate; c->supports = supports; c->config = config; c->client_var = client_var; c->remoteaddr = remoteaddr; c->host = host; c->client = client; c->referer = referer; c->pragma = pragma;
3d76f52009-04-28Henrik Grubbström (Grubba)  if (objectp(cookies)) { c->cookies = c->CookieJar(real_cookies + ([]));
2a74052009-04-28Henrik Grubbström (Grubba)  } else if (cookies) {
a786a02010-01-18Anders Johansson  c->cookies = c->real_cookies = (real_cookies||([])) + ([]);
3d76f52009-04-28Henrik Grubbström (Grubba)  } else {
e70c752009-04-28Henrik Grubbström (Grubba)  c->cookies = c->real_cookies = 0;
3d76f52009-04-28Henrik Grubbström (Grubba)  }
fcfa7b2004-05-14Henrik Grubbström (Grubba)  c->request_headers = request_headers + ([]);
a896932001-01-03Per Hedbor  c->my_fd = 0; c->prot = prot; c->clientprot = clientprot; c->method = method; c->rest_query = rest_query; c->raw = raw; c->query = query; c->not_query = not_query; c->data = data; c->extra_extension = extra_extension; c->realauth = realauth; c->rawauth = rawauth; c->since = since;
b49a892016-02-11Martin Karlgren  c->json_logger = json_logger->child();
a896932001-01-03Per Hedbor  return c; } Stdio.File connection( ) //! Returns the file descriptor used for the connection to the client. { return my_fd; } Configuration configuration() //! Returns the <ref>Configuration</ref> object of the virtual server that //! is handling the request. { return conf; }
b192532004-05-25Martin Stjernholm 
2adb8a2009-06-12Martin Stjernholm  protected void destroy() { destruct_threadbound_session_objects(); }
b192532004-05-25Martin Stjernholm  string _sprintf (int flag) { return flag == 'O' && ("RequestID(" + (raw_url||"") + ")" #ifdef ID_OBJ_DEBUG + (__marker ? "[" + __marker->count + "]" : "")
d584002009-10-31Martin Stjernholm #else + OBJ_COUNT
b192532004-05-25Martin Stjernholm #endif ); }
a896932001-01-03Per Hedbor }
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears MultiStatusStatus
2927812018-03-19Henrik Grubbström (Grubba) class MultiStatusStatus
c8bfed2003-06-02Henrik Grubbström (Grubba) {
197e812004-05-06Martin Stjernholm  constant is_status = 1;
2927812018-03-19Henrik Grubbström (Grubba)  int http_code; SimpleNode message; //! @param http_code //! HTTP return code. //! //! @param message //! Description of @[http_code]. protected void create(int http_code, void|string message) { this::http_code = http_code; if (message) { SimpleNode node = SimpleElementNode("DAV:responsedescription", ([])); node->add_child(SimpleTextNode(message||"")); this::message = node; } } //! @param ret //! Result mapping to convert into a @[MultiStatusStatus]. //! //! The mapping has often been generated by @[Roxen.http_xml_status()]. protected variant void create(mapping(string:mixed) ret) { if (ret->xml) { message = ret->xml; http_code = ret->error; return; } create(ret->error, ret->rettext); }
a8e2b32004-05-07Henrik Grubbström (Grubba)  void build_response (SimpleElementNode response_node)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode node = SimpleElementNode("DAV:status", ([]));
197e812004-05-06Martin Stjernholm  response_node->add_child (node); // No use wasting space on a good message in the status node since // we have it in the responsedescription instead.
a8e2b32004-05-07Henrik Grubbström (Grubba)  node->add_child(SimpleTextNode(sprintf("HTTP/1.1 %d ", http_code)));
197e812004-05-06Martin Stjernholm  if (message) {
2927812018-03-19Henrik Grubbström (Grubba)  response_node->add_child(message);
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
197e812004-05-06Martin Stjernholm  } int `== (mixed other) { return objectp (other) && object_program (other) == this_program && other->http_code == http_code && other->message == message; } int __hash() {
2927812018-03-19Henrik Grubbström (Grubba)  return http_code + (message && hash (message->render_xml()));
197e812004-05-06Martin Stjernholm  } string _sprintf (int flag) { return flag == 'O' && sprintf ("MultiStatusStatus(%d,%O)", http_code, message);
c8bfed2003-06-02Henrik Grubbström (Grubba)  } }
a8e2b32004-05-07Henrik Grubbström (Grubba) private SimpleElementNode ok_status_node =
0662752004-05-13Martin Stjernholm  SimpleElementNode("DAV:status", ([]))->add_child(SimpleTextNode("HTTP/1.1 200 OK"));
c8bfed2003-06-02Henrik Grubbström (Grubba) 
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears MultiStatusPropStat
197e812004-05-06Martin Stjernholm class MultiStatusPropStat { constant is_prop_stat = 1;
c8bfed2003-06-02Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  mapping(string:string|SimpleNode|array(SimpleNode)|MultiStatusStatus) properties = ([]);
071d472004-05-06Martin Stjernholm  //! The property settings. Indexed on property name (with complete //! XML namespace). Values are: //! //! @mixed
a8e2b32004-05-07Henrik Grubbström (Grubba)  //! @type array(SimpleNode)
071d472004-05-06Martin Stjernholm  //! The property value as a sequence of XML nodes. These nodes //! are used as children to the property nodes in the DAV //! protocol.
a8e2b32004-05-07Henrik Grubbström (Grubba)  //! @type SimpleNode
071d472004-05-06Martin Stjernholm  //! Same as an array containing only this node. //! @type string
a8e2b32004-05-07Henrik Grubbström (Grubba)  //! Same as a single @[Parser.XML.Tree.SimpleTextNode] with this value.
071d472004-05-06Martin Stjernholm  //! @type int(0..0) //! The property exists but has no value. //! @type MultiStatusStatus //! There was an error querying the property and this is the //! status it generated instead of a value. //! @endmixed
1904282003-06-17Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  void build_response (SimpleElementNode response_node)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode ok_prop_node = SimpleElementNode("DAV:prop", ([])); mapping(MultiStatusStatus:SimpleNode) prop_nodes = ([]);
197e812004-05-06Martin Stjernholm  foreach (properties;
a8e2b32004-05-07Henrik Grubbström (Grubba)  string prop_name; string|SimpleNode|array(SimpleNode)|MultiStatusStatus value) {
197e812004-05-06Martin Stjernholm  if (objectp (value) && value->is_status) { // Group together failed properties according to status codes.
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode prop_node = prop_nodes[value];
197e812004-05-06Martin Stjernholm  if (!prop_node)
a8e2b32004-05-07Henrik Grubbström (Grubba)  prop_nodes[value] = prop_node = SimpleElementNode("DAV:prop", ([])); prop_node->add_child(SimpleElementNode(prop_name, ([])));
a7332e2004-04-20Martin Stjernholm  }
197e812004-05-06Martin Stjernholm  else { // The property is ok and has a value. string ms_type; // The DAV client in Windows XP Pro (at least) requires types // on the date fields to parse them correctly. The type system // is of course some MS goo. switch (prop_name) { case "DAV:creationdate": ms_type = "dateTime.tz"; break; case "DAV:getlastmodified": ms_type = "dateTime.rfc1123"; break; // MS header - format unknown. //case "DAV:lastaccessed": ms_type = "dateTime.tz"; break; }
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode node = SimpleElementNode(prop_name, ms_type ? ([ "urn:schemas-microsoft-com:datatypesdt": ms_type ]) : ([]));
197e812004-05-06Martin Stjernholm  ok_prop_node->add_child (node); if (arrayp (value)) node->replace_children (value); else if (stringp (value))
a8e2b32004-05-07Henrik Grubbström (Grubba)  node->add_child(SimpleTextNode(value));
197e812004-05-06Martin Stjernholm  else if (objectp (value)) node->add_child (value);
1904282003-06-17Henrik Grubbström (Grubba)  }
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
197e812004-05-06Martin Stjernholm  if (ok_prop_node->count_children()) {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode propstat_node = SimpleElementNode("DAV:propstat", ([]));
197e812004-05-06Martin Stjernholm  response_node->add_child (propstat_node); propstat_node->add_child (ok_prop_node); propstat_node->add_child (ok_status_node);
1904282003-06-17Henrik Grubbström (Grubba)  }
c8bfed2003-06-02Henrik Grubbström (Grubba) 
a8e2b32004-05-07Henrik Grubbström (Grubba)  foreach (prop_nodes; MultiStatusStatus status; SimpleNode prop_node) { SimpleElementNode propstat_node = SimpleElementNode("DAV:propstat", ([]));
197e812004-05-06Martin Stjernholm  response_node->add_child (propstat_node); propstat_node->add_child (prop_node); status->build_response (propstat_node); } }
c8bfed2003-06-02Henrik Grubbström (Grubba) 
197e812004-05-06Martin Stjernholm  string _sprintf (int flag)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
197e812004-05-06Martin Stjernholm  return flag == 'O' && sprintf ("MultiStatusPropStat(%O)", properties);
c8bfed2003-06-02Henrik Grubbström (Grubba)  } }
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears MultiStatusNode
197e812004-05-06Martin Stjernholm typedef MultiStatusStatus|MultiStatusPropStat MultiStatusNode;
7a67792006-07-05Henrik Grubbström (Grubba) //! @appears MultiStatus
c8bfed2003-06-02Henrik Grubbström (Grubba) class MultiStatus {
fc40392008-08-15Martin Stjernholm  protected mapping(string:MultiStatusNode) status_set = ([]);
c8bfed2003-06-02Henrik Grubbström (Grubba) 
fc40392008-08-15Martin Stjernholm  protected mapping(string:string) args = ([
f8874d2004-04-29Martin Stjernholm  "xmlns:DAV": "DAV:", // MS namespace for data types; see comment in // XMLPropStatNode.add_property. Note: The XML parser in the // MS DAV client is broken and requires the break of the last // word "datatypesdt" to be exactly at this point. "xmlns:MS": "urn:schemas-microsoft-com:datatypes", ]);
ae77c42004-05-05Martin Stjernholm  int num_responses() //! Returns the number of responses that have been added to this //! object.
b303da2004-03-03Henrik Grubbström (Grubba)  {
197e812004-05-06Martin Stjernholm  return sizeof (status_set);
b303da2004-03-03Henrik Grubbström (Grubba)  }
197e812004-05-06Martin Stjernholm  MultiStatusNode get_response (string href) //! Returns the response stored for @[href] if there is any. //! //! @returns //! @mixed //! @type MultiStatusStatus //! There was some kind of error on @[href] and this is the //! stored status. This type is preferably checked by testing //! for a nonzero @expr{is_status@} member. //! @type MultiStatusPropStat //! A property query was successful on @[href] and the //! returned object contains the results for some or all //! properties. This type is preferably checked by testing for //! a nonzero @expr{is_prop_stat@} member. //! @endmixed
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
197e812004-05-06Martin Stjernholm  return status_set[href]; } mapping(string:MultiStatusNode) get_responses_by_prefix (string href_prefix) //! Returns the stored responses for all URI:s that got //! @[href_prefix] as prefix. See @[get_response] for details about //! the response value. //! //! Prefixes are only matched at @expr{"/"@} boundaries, so //! @expr{"http://x.se/foo"@} is considered a prefix of //! @expr{"http://x.se/foo/bar"@} but not //! @expr{"http://x.se/foobar"@}. As a special case, the empty //! string as @[href_prefix] returns all stored responses. { if (href_prefix == "") return status_set + ([]); mapping(string:MultiStatusNode) result = ([]); if (!has_suffix (href_prefix, "/")) { if (MultiStatusNode stat = status_set[href_prefix]) result[href_prefix] = stat; href_prefix += "/";
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
197e812004-05-06Martin Stjernholm  foreach (status_set; string href; MultiStatusNode stat) if (has_prefix (href, href_prefix)) result[href] = stat; return result;
c8bfed2003-06-02Henrik Grubbström (Grubba)  } //! Add DAV:propstat information about the property @[prop_name] for //! the resource @[href]. //! //! @param prop_value //! Optional property value. It can be one of the following: //! @mixed prop_value //! @type void|int(0..0) //! Operation performed ok, no value.
a8e2b32004-05-07Henrik Grubbström (Grubba)  //! @type string|SimpleNode|array(SimpleNode)
c8bfed2003-06-02Henrik Grubbström (Grubba)  //! Property has value @[prop_value].
197e812004-05-06Martin Stjernholm  //! @type MultiStatusStatus
c8bfed2003-06-02Henrik Grubbström (Grubba)  //! @type mapping(string:mixed) //! Operation failed as described by the mapping. //! @endmixed void add_property(string href, string prop_name,
a8e2b32004-05-07Henrik Grubbström (Grubba)  void|int(0..0)|string|array(SimpleNode)|SimpleNode|
197e812004-05-06Martin Stjernholm  MultiStatusStatus|mapping(string:mixed) prop_value)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
197e812004-05-06Martin Stjernholm  MultiStatusPropStat prop_stat; if (MultiStatusNode stat = status_set[href]) if (stat->is_prop_stat) // This will cause override of an existing MultiStatusStatus. // Presumably it came from another file system that is now // being hidden. Or is it better to keep the status node and // do nothing here instead? prop_stat = stat; if (!prop_stat) prop_stat = status_set[href] = MultiStatusPropStat(); if (mappingp (prop_value)) prop_value = MultiStatusStatus (prop_value->error, prop_value->rettext); prop_stat->properties[prop_name] = prop_value;
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
ae77c42004-05-05Martin Stjernholm  void add_status (string href, int status_code, void|string message, mixed... args) //! Add a status for the specified url. The remaining arguments are //! the same as for @[Roxen.http_status]. {
197e812004-05-06Martin Stjernholm  if (sizeof (args)) message = sprintf (message, @args);
59ec342004-05-09Henrik Grubbström (Grubba)  if (!status_code) error("Bad status code!\n");
197e812004-05-06Martin Stjernholm  status_set[href] = MultiStatusStatus (status_code, message);
ae77c42004-05-05Martin Stjernholm  }
2927812018-03-19Henrik Grubbström (Grubba)  variant void add_status(string href, mapping(string:mixed) ret) //! Add a status for the specified url. //! //! @param href //! URL the status is for. //! //! @param ret //! Result-mapping for the URL. { status_set[href] = MultiStatusStatus(ret); }
ed2fa42018-03-28Henrik Grubbström (Grubba)  variant void add_status(string href, MultiStatusNode status_node) //! Add a status for the specified url. //! //! @param href //! URL that the @[status_node] is for. //! //! @param status_node //! Status for the @[href]. Typically a @[MultiStatusStatus] or //! a @[MultiStatusPropStat]. //! //! This variant makes it easy to transfer nodes beteen //! @[MultiStatus] objects. { status_set[href] = status_node; }
f8874d2004-04-29Martin Stjernholm  void add_namespace (string namespace) //! Add a namespace to the generated @tt{<multistatus>@} element. //! Useful if several properties share a namespace. { int ns_count = 0; string ns_name; while (args[ns_name = "xmlns:NS" + ns_count]) { if (args[ns_name] == namespace) return; ns_count++; } args[ns_name] = namespace; }
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleNode get_xml_node()
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode node; SimpleRootNode root = SimpleRootNode()-> add_child(SimpleHeaderNode((["version": "1.0", "encoding": "utf-8"])))-> add_child(node = SimpleElementNode("DAV:multistatus", args));
f8874d2004-04-29Martin Stjernholm 
a8e2b32004-05-07Henrik Grubbström (Grubba)  array(SimpleNode) response_xml = allocate(sizeof(status_set));
c8bfed2003-06-02Henrik Grubbström (Grubba)  int i;
ca04922003-12-22Henrik Grubbström (Grubba)  DAV_WERROR("Generating XML Nodes for status_set:%O\n", status_set);
c8bfed2003-06-02Henrik Grubbström (Grubba) 
8cedaa2004-05-07Martin Stjernholm  // Sort this because some client (which one?) requires collections // to come before the entries they contain.
ac02922012-01-23Henrik Grubbström (Grubba)  // Encode the hrefs because some clients (eg MacOS X) assume that // they are proper URLs (eg including fragment).
bd5d4d2003-12-15Henrik Grubbström (Grubba)  foreach(sort(indices(status_set)), string href) {
a8e2b32004-05-07Henrik Grubbström (Grubba)  SimpleElementNode response_node = SimpleElementNode("DAV:response", ([]))-> add_child(SimpleElementNode("DAV:href", ([]))->
65f9ae2012-01-23Henrik Grubbström (Grubba)  add_child(SimpleTextNode(href)));
197e812004-05-06Martin Stjernholm  response_xml[i++] = response_node; status_set[href]->build_response (response_node);
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
f8874d2004-04-29Martin Stjernholm  node->replace_children(response_xml);
c8bfed2003-06-02Henrik Grubbström (Grubba)  return root; } mapping(string:mixed) http_answer() { string xml = get_xml_node()->render_xml(); return ([ "error": 207, "data": xml, "len": sizeof(xml), "type": "text/xml; charset=\"utf-8\"", ]); }
65f9ae2012-01-23Henrik Grubbström (Grubba)  //! MultiStatus with a fix prefix. //! //! This object acts as a proxy for its parent.
fc40392008-08-15Martin Stjernholm  class Prefixed (protected string href_prefix)
ae77c42004-05-05Martin Stjernholm  {
65f9ae2012-01-23Henrik Grubbström (Grubba)  //! Get the parent @[MultiStatus] object.
ae77c42004-05-05Martin Stjernholm  MultiStatus get_multi_status() {return MultiStatus::this;}
65f9ae2012-01-23Henrik Grubbström (Grubba)  //! Add a property for a path. //! //! @note //! Note that the segments of the path will be //! encoded with @[Roxen.http_encode_url()].
c8bfed2003-06-02Henrik Grubbström (Grubba)  void add_property(string path, string prop_name,
a8e2b32004-05-07Henrik Grubbström (Grubba)  void|int(0..0)|string|array(SimpleNode)|SimpleNode|
197e812004-05-06Martin Stjernholm  MultiStatusStatus|mapping(string:mixed) prop_value)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
65f9ae2012-01-23Henrik Grubbström (Grubba)  path = map(path/"/", Roxen->http_encode_url)*"/";
c8bfed2003-06-02Henrik Grubbström (Grubba)  MultiStatus::add_property(href_prefix + path, prop_name, prop_value); }
65f9ae2012-01-23Henrik Grubbström (Grubba)  //! Add a status for a path. //! //! @note //! Note that the segments of the path will be //! encoded with @[Roxen.http_encode_url()].
ae77c42004-05-05Martin Stjernholm  void add_status (string path, int status_code, void|string message, mixed... args)
f8874d2004-04-29Martin Stjernholm  {
65f9ae2012-01-23Henrik Grubbström (Grubba)  path = map(path/"/", Roxen->http_encode_url)*"/";
ae77c42004-05-05Martin Stjernholm  MultiStatus::add_status (href_prefix + path, status_code, message, @args);
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
65f9ae2012-01-23Henrik Grubbström (Grubba) 
d225db2018-03-21Henrik Grubbström (Grubba)  //! Add a status for a path. //! //! @note //! Note that the segments of the path will be //! encoded with @[Roxen.http_encode_url()]. variant void add_status (string path, mapping(string:mixed) ret) { path = map(path/"/", Roxen->http_encode_url)*"/"; MultiStatus::add_status (href_prefix + path, ret); }
65f9ae2012-01-23Henrik Grubbström (Grubba)  //!
ae77c42004-05-05Martin Stjernholm  void add_namespace (string namespace)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
ae77c42004-05-05Martin Stjernholm  MultiStatus::add_namespace (namespace);
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
65f9ae2012-01-23Henrik Grubbström (Grubba)  //!
ae77c42004-05-05Martin Stjernholm  MultiStatus.Prefixed prefix(string href_prefix) { return this_program (this_program::href_prefix + href_prefix);
c8bfed2003-06-02Henrik Grubbström (Grubba)  } }
ae77c42004-05-05Martin Stjernholm  MultiStatus.Prefixed prefix (string href_prefix) //! Returns an object with the same @expr{add_*@} methods as this //! one except that @[href_prefix] is implicitly prepended to every //! path. { return Prefixed (href_prefix); }
c8bfed2003-06-02Henrik Grubbström (Grubba) }
c5dd9f2004-03-16Henrik Grubbström (Grubba) // Only for protyping and breaking of circularities.
fc40392008-08-15Martin Stjernholm protected class PropertySet
c5dd9f2004-03-16Henrik Grubbström (Grubba) { string path;
ae77c42004-05-05Martin Stjernholm  RequestID id; Stat get_stat(); mapping(string:string) get_response_headers();
c5dd9f2004-03-16Henrik Grubbström (Grubba)  multiset(string) query_all_properties();
a8e2b32004-05-07Henrik Grubbström (Grubba)  string|array(SimpleNode)|mapping(string:mixed)
c5dd9f2004-03-16Henrik Grubbström (Grubba)  query_property(string prop_name); mapping(string:mixed) start(); void unroll(); void commit(); mapping(string:mixed) set_property(string prop_name,
a8e2b32004-05-07Henrik Grubbström (Grubba)  string|array(SimpleNode) value);
c5dd9f2004-03-16Henrik Grubbström (Grubba)  mapping(string:mixed) set_dead_property(string prop_name,
a8e2b32004-05-07Henrik Grubbström (Grubba)  array(SimpleNode) value);
c5dd9f2004-03-16Henrik Grubbström (Grubba)  mapping(string:mixed) remove_property(string prop_name); mapping(string:mixed) find_properties(string mode,
ae77c42004-05-05Martin Stjernholm  MultiStatus.Prefixed result,
c5dd9f2004-03-16Henrik Grubbström (Grubba)  multiset(string)|void filt); }
0662752004-05-13Martin Stjernholm //! How to handle an existing destination when files or directories //! are moved or copied in a filesystem.
cdaab02004-05-10Martin Stjernholm enum Overwrite {
0662752004-05-13Martin Stjernholm  //! Fail if the destination exists. Corresponds to an Overwrite //! header with the value "F" (RFC 2518 9.6).
dd03b22006-04-20Henrik Grubbström (Grubba)  NEVER_OVERWRITE = -1,
0662752004-05-13Martin Stjernholm  //! If the source and destination are directories, overwrite the //! properties only. If the source and destination are files,
102dbe2004-05-13Martin Stjernholm  //! overwrite the file along with the properties. Otherwise fail if //! the destination exists.
dd03b22006-04-20Henrik Grubbström (Grubba)  MAYBE_OVERWRITE = 0,
0662752004-05-13Martin Stjernholm  //! If the destination exists then delete it recursively before //! writing the new content. Corresponds to an Overwrite header with //! the value "T" (RFC 2518 9.6).
dd03b22006-04-20Henrik Grubbström (Grubba)  DO_OVERWRITE = 1,
cdaab02004-05-10Martin Stjernholm };
dfc1ad2004-05-13Henrik Grubbström (Grubba) //! State of the DAV:propertybehavior. //! //! @mixed //! @type int(0..0) //! DAV:omit. //! Failure to copy a property does not cause the entire copy //! to fail. //! @type int(1..1) //! DAV:keepalive "*". //! All live properties must be kept alive. //! @type multiset(string)
a720412004-05-14Martin Stjernholm //! Set of properties to keep alive. Properties not in the set //! should be copied according to best effort. The properties are //! listed with complete namespaces.
dfc1ad2004-05-13Henrik Grubbström (Grubba) //! @endmixed typedef int(0..1)|multiset(string) PropertyBehavior;
cdaab02004-05-10Martin Stjernholm 
3a49642009-11-19Henrik Grubbström (Grubba) //! @appears RoxenModule
8f0b3f2009-11-19Henrik Grubbström (Grubba) //! The Roxen module interface.
a896932001-01-03Per Hedbor class RoxenModule { inherit BasicDefvar; constant is_module = 1; constant module_type = 0; constant module_unique = 1;
7787ac2001-07-31Per Hedbor  LocaleString module_name; LocaleString module_doc;
a896932001-01-03Per Hedbor 
0aa37d2001-08-23Martin Stjernholm  string module_identifier(); string module_local_id();
a896932001-01-03Per Hedbor  array(int|string|mapping) register_module(); string file_name_and_stuff();
d1f2c82006-05-31Martin Stjernholm  void start (void|int variable_save, void|Configuration conf, void|int newly_added); //! This function is called both when a module is loaded //! (@[variable_save] is 0) and when its variables are saved after a //! change (@[variable_save] is 2). //! //! @[newly_added] is 1 if the module is being added by the //! administrator through the admin interface (implies that //! @[variable_save] is 0). It's zero if the module is loaded //! normally, i.e. as a result of an entry in the server //! configuration file. //! //! @[conf] is the configuration that the module instance belongs //! to, i.e. the same as the return value from @[my_configuration]. //! //! @note //! A module can't assume that it has been loaded before in this //! server configuration just because @[newly_added] is zero. The //! administrator might for instance have edited the configuration //! file directly.
a896932001-01-03Per Hedbor 
9ed1442010-05-07Martin Stjernholm  void stop (void|RoxenModule new_instance);
ceb64e2008-05-21Martin Stjernholm  //! This function is called when a running module is stopped either //! because it's being dropped or reloaded in the admin interface, //! or the server is being shut down orderly.
9ed1442010-05-07Martin Stjernholm  //! //! If the module is being stopped because of a reload then //! @[new_instance] is the new module instance that is replacing //! this one. Note that @[new_instance] is not properly initialized //! at this stage; among other things its @[start] function has not //! yet been run.
ceb64e2008-05-21Martin Stjernholm 
fc40392008-08-15Martin Stjernholm  void ready_to_receive_requests (void|Configuration conf);
ceb64e2008-05-21Martin Stjernholm  //! This function is called after all modules in a configuration //! have been loaded and @[start]ed. If a function is added later on
e2ef142009-04-21Martin Stjernholm  //! it's called directly after @[start].
ceb64e2008-05-21Martin Stjernholm  //! //! When a configuration is loaded on server start, this function is //! still called before the handler threads are started. //!
fc40392008-08-15Martin Stjernholm  //! @[conf] is the configuration that the module instance belongs //! to, i.e. the same as the return value from @[my_configuration]. //!
ceb64e2008-05-21Martin Stjernholm  //! @note //! This function is intended for things that can't be done in
e2ef142009-04-21Martin Stjernholm  //! @[start] because all modules might not be loaded by then, e.g. //! for starting crawlers. There is no well defined order between //! calls to @[ready_to_receive_requests], so its usefulness //! diminishes the more modules that use it. In other words, don't //! use unless you absolutely have to. //! //! Calls to @[module_dependencies] with the third @expr{now@} //! argument set is a better way to sequence module startups, but //! that requires that the depended modules don't (ab)use //! @[ready_to_receive_requests].
ceb64e2008-05-21Martin Stjernholm 
a896932001-01-03Per Hedbor  string query_internal_location(); string query_location();
740d682004-06-17Henrik Grubbström (Grubba)  string|multiset(string) query_provides();
bd5d4d2003-12-15Henrik Grubbström (Grubba)  function(RequestID:int|mapping) query_seclevels();
119a552004-05-07Martin Stjernholm  void set_status_for_path (string path, RequestID id, int status_code, string|void message, mixed... args);
ce64272001-01-14Martin Nilsson  array(int)|object(Stdio.Stat) stat_file(string f, RequestID id);
c8c1352003-01-13Henrik Grubbström (Grubba)  array(string) find_dir(string f, RequestID id);
a896932001-01-03Per Hedbor  mapping(string:array(mixed)) find_dir_stat(string f, RequestID id); string real_file(string f, RequestID id); void save(); mapping api_functions(); mapping query_tag_callers(); mapping query_container_callers(); string info(object conf); string comment();
c8bfed2003-06-02Henrik Grubbström (Grubba) 
c11ef72004-05-10Martin Stjernholm  PropertySet|mapping(string:mixed) query_property_set(string path, RequestID id);
a8e2b32004-05-07Henrik Grubbström (Grubba)  string|array(SimpleNode)|mapping(string:mixed)
c8bfed2003-06-02Henrik Grubbström (Grubba)  query_property(string path, string prop_name, RequestID id);
e9ceb92004-06-02Martin Stjernholm  mapping(string:mixed) recurse_find_properties(string path, string mode, int depth, RequestID id, multiset(string)|void filt);
a98f752004-03-03Henrik Grubbström (Grubba)  mapping(string:mixed) patch_properties(string path, array(PatchPropertyCommand) instructions,
cdaab02004-05-10Martin Stjernholm  RequestID id);
4c87da2004-04-28Henrik Grubbström (Grubba)  mapping(string:mixed) set_property (string path, string prop_name,
a8e2b32004-05-07Henrik Grubbström (Grubba)  string|array(SimpleNode) value,
4c87da2004-04-28Henrik Grubbström (Grubba)  RequestID id); mapping(string:mixed) remove_property (string path, string prop_name, RequestID id);
8cedaa2004-05-07Martin Stjernholm 
0ddcae2004-05-04Martin Stjernholm  string resource_id (string path, RequestID id);
de070f2004-05-04Martin Stjernholm  string|int authenticated_user_id (string path, RequestID id);
da60482018-03-27Henrik Grubbström (Grubba)  mapping(string:DAVLock) find_locks(string path, int(-1..1) recursive, int(0..1) exclude_shared, RequestID id);
0850612004-05-03Martin Stjernholm  mapping(string:mixed) lock_file(string path, DAVLock lock, RequestID id);
4908322004-04-29Martin Stjernholm  mapping(string:mixed) unlock_file (string path, DAVLock lock, RequestID id);
28c81a2004-05-12Martin Stjernholm  mapping(string:mixed)|int(0..1) check_if_header(string relative_path, int(0..1) recursive, RequestID id);
8cedaa2004-05-07Martin Stjernholm 
b303da2004-03-03Henrik Grubbström (Grubba)  mapping(string:mixed)|int(-1..0)|Stdio.File find_file(string path, RequestID id);
48d26d2004-05-10Henrik Grubbström (Grubba)  mapping(string:mixed) recurse_delete_files(string path, RequestID id);
cdaab02004-05-10Martin Stjernholm  mapping(string:mixed) make_collection(string path, RequestID id);
dfc1ad2004-05-13Henrik Grubbström (Grubba)  mapping(string:mixed) recurse_copy_files(string source, string destination, PropertyBehavior behavior,
8e86d02018-03-20Henrik Grubbström (Grubba)  Overwrite overwrite, RequestID id, int|void one_level);
7d1c8e2004-05-13Henrik Grubbström (Grubba)  mapping(string:mixed) recurse_move_files(string source, string destination, PropertyBehavior behavior, Overwrite overwrite, RequestID id);
a896932001-01-03Per Hedbor }
0cb4c42004-03-15Martin Stjernholm class PatchPropertyCommand { constant command = ""; string property_name;
df04242004-03-16Henrik Grubbström (Grubba)  mapping(string:mixed) execute(PropertySet context);
0cb4c42004-03-15Martin Stjernholm }
a896932001-01-03Per Hedbor class _roxen { mapping(string:object) variables;
2284712004-04-25Henrik Grubbström (Grubba)  constant real_version = "";
a896932001-01-03Per Hedbor  object locale; int start_time; array(Configuration) configurations; mixed query(string a); void store(string a, mapping b, int c, object d); mapping(string:mixed) retrieve(string a, object b); void remove(string a, object b); string version(); void dump(string a); void nwrite(string a, int|void b, int|void c, void|mixed ... d); int main(int a, array(string) b); }
3e3bab2001-01-19Per Hedbor  class AuthModule
7f66d82001-08-24Martin Nilsson //! @appears AuthModule
3e3bab2001-01-19Per Hedbor //! The interface an authentication module must implement { inherit RoxenModule; constant module_type = MODULE_AUTH; constant thread_safe=1; constant name = "method name";
d5123a2017-10-09Karl Gustav Sterneberg 
3342dd2001-01-19Per Hedbor  User authenticate( RequestID id, UserDB db );
3e3bab2001-01-19Per Hedbor  //! Try to authenticate the request with users from the specified user //! database. If no @[db] is specified, all datbases in the current //! configuration are searched in order, then the configuration user //! database.
3342dd2001-01-19Per Hedbor  mapping authenticate_throw( RequestID id, string realm, UserDB db );
3e3bab2001-01-19Per Hedbor  //! Returns a reply mapping, similar to @[Roxen.http_rxml_reply] with //! friends. If no @[db] is specified, all datbases in the current //! configuration are searched in order, then the configuration user //! database. }
fc40392008-08-15Martin Stjernholm protected mapping(string:function(void:void)) user_sql_inited = ([]); protected Sql.Sql user_mysql; protected void init_user_sql(string table)
3342dd2001-01-19Per Hedbor {
e082212001-08-28Per Hedbor  string db = all_constants()->REPLICATE?"replicate":"local";
69fa3e2001-01-30Per Hedbor  if( !user_mysql )
e082212001-08-28Per Hedbor  user_mysql = master()->resolv("DBManager.get")( db );
c580392001-08-13Per Hedbor  if(catch(user_mysql->query( "SELECT module FROM "+ table+" WHERE module=''"))) {
69fa3e2001-01-30Per Hedbor  user_mysql->query( "CREATE TABLE "+table+" " " (module varchar(30) NOT NULL, "
3342dd2001-01-19Per Hedbor  " name varchar(30) NOT NULL, "
69fa3e2001-01-30Per Hedbor  " user varchar(30) NOT NULL, " " value blob, " " raw int not null, " " INDEX foo (module,name,user))" );
e082212001-08-28Per Hedbor  master()->resolv("DBManager.is_module_table")( 0, db, table, "Contains metadata about users. "
c580392001-08-13Per Hedbor  "Userdatabases can store information here " "at the request of other modules, or they " "can keep their own state in this table" ); }
e082212001-08-28Per Hedbor  user_sql_inited[ table ]= lambda(){user_mysql = master()->resolv("DBManager.get")( db );};
3342dd2001-01-19Per Hedbor }
7f66d82001-08-24Martin Nilsson //! @appears Group
3342dd2001-01-19Per Hedbor class Group( UserDB database )
3e3bab2001-01-19Per Hedbor { string name();
3342dd2001-01-19Per Hedbor  //! The group name
6533f22001-08-23Martin Nilsson 
9b94322001-01-29Per Hedbor  array(string) members()
3342dd2001-01-19Per Hedbor  //! All users that are members of this group. The default //! implementation loops over all users handled by the user database
3181852001-10-09Per Hedbor  //! and looks for users with the same gid as this group, or who is a //! member of it when the groups() method are called.
3342dd2001-01-19Per Hedbor  { array res = ({}); User uid; int id = gid(); foreach( database->list_users(), string u )
3181852001-10-09Per Hedbor  if( (uid = database->find_user( u )) && ((uid->gid() == id) || has_value(uid->groups(), name())))
9b94322001-01-29Per Hedbor  res += ({ u });
3342dd2001-01-19Per Hedbor  return res; }
d5123a2017-10-09Karl Gustav Sterneberg 
3342dd2001-01-19Per Hedbor  int gid(); //! A numerical GID, or -1 if not applicable
3181852001-10-09Per Hedbor  int set_name( string new_name ) { return 0; } int set_gid( int new_gid ) { return 0; } int set_members( array(string) members ) { return 0; } //! Returns 1 if it was possible to set the variable.
d5123a2017-10-09Karl Gustav Sterneberg 
3342dd2001-01-19Per Hedbor }
e082212001-08-28Per Hedbor #ifdef THREADS
fc40392008-08-15Martin Stjernholm protected Thread.Mutex mutex = Thread.Mutex();
e082212001-08-28Per Hedbor #endif
7f66d82001-08-24Martin Nilsson //! @appears User
3342dd2001-01-19Per Hedbor class User( UserDB database ) {
fc40392008-08-15Martin Stjernholm  protected string table;
69fa3e2001-01-30Per Hedbor 
3342dd2001-01-19Per Hedbor  string name(); //! The user (short) name
6533f22001-08-23Martin Nilsson 
3e3bab2001-01-19Per Hedbor  string real_name();
3342dd2001-01-19Per Hedbor  //! The real name of the user
0d1bcb2001-08-30Henrik Grubbström (Grubba)  int password_authenticate(string password)
3342dd2001-01-19Per Hedbor  //! Return 1 if the password is correct, 0 otherwise. The default //! implementation uses the crypted_password() method. {
0d1bcb2001-08-30Henrik Grubbström (Grubba)  string c = crypted_password();
2182f52013-03-08Henrik Grubbström (Grubba)  return !sizeof(c) || verify_password(password, c);
3342dd2001-01-19Per Hedbor  } int uid(); //! A numerical UID, or -1 if not applicable
6533f22001-08-23Martin Nilsson 
3342dd2001-01-19Per Hedbor  int gid(); //! A numerical GID, or -1 if not applicable
6533f22001-08-23Martin Nilsson 
3342dd2001-01-19Per Hedbor  string shell(); //! The shell, or 0 if not applicable
d5123a2017-10-09Karl Gustav Sterneberg 
3342dd2001-01-19Per Hedbor  string gecos() //! The gecos field, defaults to return the real name { return real_name(); } string homedir(); string crypted_password() { return "x"; } //! Used by compat_userinfo(). The default implementation returns "x"
9b94322001-01-29Per Hedbor  array(string) groups()
e3f4662001-01-19Per Hedbor  //! Return all groups this user is a member in. The default //! implementation returns ({}) { return ({}); }
3342dd2001-01-19Per Hedbor 
1d5c892001-01-19Per Hedbor  int set_name(string name) {} int set_real_name(string rname) {} int set_uid(int uid) {} int set_gid(int gid) {} int set_shell(string shell) {} int set_gecos(string gecos) {} int set_homedir(string hodir) {} int set_crypted_password(string passwd) {} int set_password(string passwd) {} //! Returns 1 if it was possible to set the variable.
d5123a2017-10-09Karl Gustav Sterneberg 
3342dd2001-01-19Per Hedbor  array compat_userinfo( ) //! Return a unix passwd compatible array with user information. The //! defualt implementation uses the other methods to assemble this //! information. //! //! Basically: //! return ({ name(), crypted_password(), //! uid(), gid(), gecos(), homedir(), //! shell() }); { return ({name(),crypted_password(),uid(),gid(),gecos(),homedir(),shell()}); }
3e3bab2001-01-19Per Hedbor 
69fa3e2001-01-30Per Hedbor  #define INIT_SQL() do{ \ if(!table) table = replace(database->my_configuration()->name," ","_")+"_user_variables"; \
e082212001-08-28Per Hedbor  if(!user_sql_inited[ table ] )init_user_sql( table );else user_sql_inited[ table ](); \
69fa3e2001-01-30Per Hedbor  } while( 0 )
e082212001-08-28Per Hedbor #ifdef THREADS #define LOCK() mixed ___key = mutex->lock() #else #define LOCK() #endif
fc40392008-08-15Martin Stjernholm  protected string module_name( RoxenModule module )
69fa3e2001-01-30Per Hedbor  { if( !module ) // NULL does not work together with indexes, but this is // not a valid modulename, so it's not a problem. return "'0'"; else return replace("'"+user_mysql->quote(module->sname())+"'","%","%%"); }
d5123a2017-10-09Karl Gustav Sterneberg 
1d5c892001-01-19Per Hedbor  mixed set_var( RoxenModule module, string index, mixed value )
3342dd2001-01-19Per Hedbor  //! Set a specified variable in the user. If @[value] is a string, //! it's stored as is in the database, otherwise it's encoded using
e3f4662001-01-19Per Hedbor  //! encode_value before it's stored. Returns the value.
3342dd2001-01-19Per Hedbor  //! //! You can use 0 for the @[module] argument.
d5123a2017-10-09Karl Gustav Sterneberg  //!
3342dd2001-01-19Per Hedbor  //! The default implementation stores the value in a mysql table
69fa3e2001-01-30Per Hedbor  //! '*_user_data' in the 'shared' database.
3342dd2001-01-19Per Hedbor  //! //! Use @[get_var] to retrieve the value, and @[delete_var] to //! delete it. { delete_var( module, index );
69fa3e2001-01-30Per Hedbor  mixed oval = value;
e082212001-08-28Per Hedbor  LOCK();
69fa3e2001-01-30Per Hedbor  INIT_SQL();
3342dd2001-01-19Per Hedbor  int encoded;
69fa3e2001-01-30Per Hedbor 
3342dd2001-01-19Per Hedbor  if( stringp( value ) ) value = string_to_utf8( value ); else { value = encode_value( value ); encoded = 1; }
e082212001-08-28Per Hedbor 
3342dd2001-01-19Per Hedbor  user_mysql->query(
69fa3e2001-01-30Per Hedbor  "INSERT INTO "+table+" (module,name,user,value,raw) " "VALUES ("+module_name( module )+", %s, %s, %s, %d)", index, name(), value, encoded
3342dd2001-01-19Per Hedbor  );
69fa3e2001-01-30Per Hedbor  return oval;
3342dd2001-01-19Per Hedbor  } mixed get_var( RoxenModule module, string index ) //! Return the value of a variable previously set with @[set_var] { array rows;
e082212001-08-28Per Hedbor  LOCK();
69fa3e2001-01-30Per Hedbor  INIT_SQL(); rows = user_mysql->query( "SELECT * FROM "+table+
e082212001-08-28Per Hedbor  " WHERE module="+module_name( module ) +" AND name=%s AND user=%s",
3342dd2001-01-19Per Hedbor  index, name() ); if( !sizeof( rows ) ) return 0; mapping m = rows[0];
69fa3e2001-01-30Per Hedbor 
3342dd2001-01-19Per Hedbor  if( (int)m->raw ) return decode_value( m->value ); return utf8_to_string( m->value ); } void delete_var( RoxenModule module, string index ) //! Delete a variable previously created with @[set_var] {
e082212001-08-28Per Hedbor  LOCK();
69fa3e2001-01-30Per Hedbor  INIT_SQL(); user_mysql->query( "DELETE FROM "+table+" WHERE (module="+ module_name( module )+ " AND name=%s AND user=%s)", index, name() );
3342dd2001-01-19Per Hedbor  }
69fa3e2001-01-30Per Hedbor #undef INIT_SQL
e082212001-08-28Per Hedbor #undef LOCK
3e3bab2001-01-19Per Hedbor } class UserDB
7f66d82001-08-24Martin Nilsson //! @appears UserDB
3e3bab2001-01-19Per Hedbor //! The interface a UserDB module must implement. { inherit RoxenModule; constant module_type = MODULE_USERDB;
8ea2012001-01-29Per Hedbor  constant thread_safe=1;
3e3bab2001-01-19Per Hedbor 
e3f4662001-01-19Per Hedbor  constant name = "db name";
99a70c2001-08-30Henrik Grubbström (Grubba)  User find_user( string s, RequestID|void id );
3e3bab2001-01-19Per Hedbor  //! Find a user
bd5d4d2003-12-15Henrik Grubbström (Grubba)  User find_user_from_uid( int uid, RequestID|void id )
3342dd2001-01-19Per Hedbor  //! Find a user given a UID. The default implementation loops over //! list_users() and checks the uid() of each one. {
bd5d4d2003-12-15Henrik Grubbström (Grubba)  User user;
3342dd2001-01-19Per Hedbor  foreach( list_users(), string u )
bd5d4d2003-12-15Henrik Grubbström (Grubba)  if( (user = find_user( u )) && (user->uid() == uid) ) return user;
d5123a2017-10-09Karl Gustav Sterneberg  }
3342dd2001-01-19Per Hedbor 
bd5d4d2003-12-15Henrik Grubbström (Grubba)  Group find_group( string group, RequestID|void id )
3342dd2001-01-19Per Hedbor  //! Find a group object given a group name. //! The default implementation returns 0. { }
d5123a2017-10-09Karl Gustav Sterneberg 
bd5d4d2003-12-15Henrik Grubbström (Grubba)  Group find_group_from_gid( int gid, RequestID|void id )
3342dd2001-01-19Per Hedbor  //! Find a group given a GID. The default implementation loops over //! list_groups() and checks the gid() of each one. {
bd5d4d2003-12-15Henrik Grubbström (Grubba)  Group group;
3342dd2001-01-19Per Hedbor  foreach( list_groups(), string u )
bd5d4d2003-12-15Henrik Grubbström (Grubba)  if( (group = find_group( u )) && (group->gid() == gid) ) return group;
3342dd2001-01-19Per Hedbor  }
d5123a2017-10-09Karl Gustav Sterneberg 
bd5d4d2003-12-15Henrik Grubbström (Grubba)  array(string) list_groups( RequestID|void id )
3342dd2001-01-19Per Hedbor  //! Return a list of all groups handled by this database module. //! The default implementation returns the empty array. { return ({}); }
bd5d4d2003-12-15Henrik Grubbström (Grubba)  array(string) list_users( RequestID|void id );
3e3bab2001-01-19Per Hedbor  //! Return a list of all users handled by this database module.
3342dd2001-01-19Per Hedbor  User create_user( string s )
788f082001-10-09Henrik Grubbström (Grubba)  //! Not necessarily implemented, as an example, it's not possible to
3e3bab2001-01-19Per Hedbor  //! create users in the system user database from Roxen WebServer.
3342dd2001-01-19Per Hedbor  //! The default implementation returns 0.
69fa3e2001-01-30Per Hedbor  { return 0; }
3181852001-10-09Per Hedbor  Group create_group( string s )
788f082001-10-09Henrik Grubbström (Grubba)  //! Not necessarily implemented, as an example, it's not possible to
3181852001-10-09Per Hedbor  //! create groups in the system user database from Roxen WebServer. //! The default implementation returns 0. { return 0; }
3e3bab2001-01-19Per Hedbor }