835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer. // Copyright © 2001, Roxen IS.
a896932001-01-03Per Hedbor #include <stat.h> #include <config.h>
f7ea0c2002-02-06Martin Stjernholm #include <module.h>
c8bfed2003-06-02Henrik Grubbström (Grubba) #include <variables.h>
3e3bab2001-01-19Per Hedbor #include <module_constants.h>
d1e4b82004-04-30Henrik Grubbström (Grubba) constant cvs_version="$Id: prototypes.pike,v 1.92 2004/04/30 09:19:30 grubba Exp $";
c2d7aa2004-04-13Martin Stjernholm  #ifdef DAV_DEBUG #define DAV_WERROR(X...) werror(X) #else /* !DAV_DEBUG */ #define DAV_WERROR(X...) #endif /* DAV_DEBUG */ // To avoid reference cycles. Set to the Roxen module object by // roxenloader.pike. object Roxen;
a896932001-01-03Per Hedbor  class Variable { 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 ); void set_invisibility_check_callback( function(RequestID,Variable:int) cb ); function(Variable:void) get_changed_callback( ); void set_changed_callback( function(Variable:void) cb ); void add_changed_callback( function(Variable:void) cb ); 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 ); int low_set( mixed to ); 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 ); Variable defvar(string var, mixed value, mapping|string|void|object name, int|void type, mapping|string|void|object doc_str, 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; } } class ModuleInfo { string sname; string filename; int last_checked; int type, multiple_copies;
ea262c2002-04-17Marcus Wellhardh  int|string locked; mapping(Configuration:int) config_locked;
a896932001-01-03Per Hedbor  string get_name(); string get_description(); RoxenModule instance( object conf, void|int silent ); void save(); void update_with( RoxenModule mod, string what ); int init_module( string what ); int rec_find_module( string what, string dir ); 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); }
4d7e512001-01-16Martin Nilsson  string _sprintf( ) { return "ModuleCopies("+sizeof(copies)+")"; }
a896932001-01-03Per Hedbor }
b0c9b82004-04-30Henrik Grubbström (Grubba) // Simulate an import of useful stuff from Parser.XML.Tree. static constant Node = Parser.XML.Tree.Node; static constant RootNode = Parser.XML.Tree.RootNode; static constant HeaderNode = Parser.XML.Tree.HeaderNode; static constant TextNode = Parser.XML.Tree.TextNode; static constant ElementNode = Parser.XML.Tree.ElementNode;
4c87da2004-04-28Henrik Grubbström (Grubba) class DAVLock(string locktoken, string path,
8265772004-04-29Martin Stjernholm  int(0..1) recursive, string|Node lockscope, string|Node locktype, void|string owner, ) //! Container for information about outstanding DAV write locks. No
4908322004-04-29Martin Stjernholm //! field except @[owner] may change after the object has been created //! since filesystem modules might store this info persistently.
4c87da2004-04-28Henrik Grubbström (Grubba) {
8265772004-04-29Martin Stjernholm  //! @decl string locktoken; //! //! 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. //! @decl string path; //! //! Canonical absolute path to the locked resource. Always ends with //! a @expr{"/"@}. //! @decl int(0..1) recursive; //! //! @expr{1@} if the lock applies to all resources under @[path], //! @expr{0@} if it applies to @[path] only. //! @decl string|Node lockscope; //! //! 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"@}. //! @decl string|Node locktype; //! //! 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"@}. //! @decl string owner; //! //! The owner identification (RFC 2518 12.10), or zero if unknown. //! The content is XML in string form.
4908322004-04-29Martin Stjernholm  //! //! @[RoxenModule.lock_file] may set this if it's zero, otherwise //! it shouldn't change.
b0c9b82004-04-30Henrik Grubbström (Grubba)  //! Returns a DAV:activelock @[Parser.XML.Tree.Node] structure //! describing the lock. Node get_xml() { ElementNode res = ElementNode("DAV:activelock", ([])); ElementNode tmp; res->add_child(tmp = ElementNode("DAV:locktype", ([]))); tmp->add_child(stringp(locktype)?ElementNode(locktype, ([])):locktype); res->add_child(tmp = ElementNode("DAV:lockscope", ([]))); tmp->add_child(stringp(lockscope)?ElementNode(lockscope, ([])):lockscope); res->add_child(tmp = ElementNode("DAV:depth", ([]))); tmp->add_child(recursive?TextNode("Infinity"):TextNode("0")); #if 0 if (owner) { res->add_child(tmp = ElementNode("DAV:owner", ([]))); tmp->add_child(owner); // FIXME: How? } #endif /* 0 */
d1e4b82004-04-30Henrik Grubbström (Grubba)  // FIXME: <DAV:timeout>.
b0c9b82004-04-30Henrik Grubbström (Grubba)  res->add_child(tmp = ElementNode("DAV:locktoken", ([]))); tmp->add_child(tmp = ElementNode("DAV:href", ([]))); tmp->add_child(TextNode(locktoken)); return res; }
4c87da2004-04-28Henrik Grubbström (Grubba) }
7f66d82001-08-24Martin Nilsson class Configuration
a896932001-01-03Per Hedbor { inherit BasicDefvar; constant is_configuration = 1; mapping enabled_modules = ([]); mapping(string:array(int)) error_log=([]); #ifdef PROFILE mapping profile_map = ([]); #endif class Priority { string _sprintf() { return "Priority()"; } array (RoxenModule) url_modules = ({ }); array (RoxenModule) logger_modules = ({ }); array (RoxenModule) location_modules = ({ }); array (RoxenModule) filter_modules = ({ }); array (RoxenModule) last_modules = ({ }); array (RoxenModule) first_modules = ({ }); mapping (string:array(RoxenModule)) file_extension_modules = ([ ]); mapping (RoxenModule:multiset(string)) provider_modules = ([ ]); } class DataCache { int current_size, max_size, max_file_size; int hits, misses; void flush(); void expire_entry( string url ); void set( string url, string data, mapping meta, int expire ); array(string|mapping(string:mixed)) get( string url ); void init_from_variables( ); }; array(Priority) allocate_pris(); object throttler; RoxenModule types_module; RoxenModule dir_module; function types_fun; string name; int inited; // Protocol specific statistics. int requests, sent, hsent, received; function(string:int) log_function; DataCache datacache; 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();
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); 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);
a896932001-01-03Per Hedbor  void start(int num); void save_me(); int save_one( RoxenModule o ); RoxenModule reload_module( string modname ); RoxenModule enable_module( string modname, RoxenModule|void me, ModuleInfo|void moduleinfo,
7902be2002-04-09Marcus Wellhardh  int|void nostart, int|void nosave );
a896932001-01-03Per Hedbor  void call_start_callbacks( RoxenModule me, ModuleInfo moduleinfo, ModuleCopies module ); void call_low_start_callbacks( RoxenModule me, ModuleInfo moduleinfo, 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); Sql.Sql sql_connect(string db); #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 );
a896932001-01-03Per Hedbor  static string _sprintf( ) { return "Configuration("+name+")"; } }
7f66d82001-08-24Martin Nilsson //! @appears Protocol
a896932001-01-03Per Hedbor class Protocol { 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); Configuration find_configuration_for_url( string url, RequestID id, int|void no_default ); string get_key(); void save(); void restore(); };
e0ffe32001-02-05Per Hedbor class FakedVariables( mapping real_variables ) { static array _indices() { return indices( real_variables ); } static array _values() { return map( _indices(), `[] ); } static mixed fix_value( mixed what ) { if( !what ) return what; if( !arrayp(what) ) return what; // huh if( sizeof( what ) == 1 ) return what[0]; return what*"\0"; } static mixed `[]( string ind ) { return fix_value( real_variables[ ind ] ); } static mixed `->(string ind ) { return `[]( ind ); } static mixed `[]=( string ind, mixed what ) { real_variables[ ind ] = ({ what }); return what; } static mixed `->=(string ind, mixed what ) { return `[]=( ind,what ); } static 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 ) ); } static int _equal( mixed what ) { return `==(what); } static int `==( mixed what ) { if( mappingp( what ) && (real_variables == what) ) return 1; } static string _sprintf( int f ) { switch( f ) { case 'O': return sprintf( "FakedVariables(%O)", real_variables ); default: return sprintf( sprintf("%%%c", f ), real_variables ); } } static this_program `|( mapping what ) { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); } static this_program `+=( mapping what ) { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); } static this_program `+( mapping what ) { foreach( indices(what), string q )`[]=( q,what[q] ); return this_object(); }
e734d52001-02-18Mirar (Pontus Hagland)  static mapping cast(string to) { if (to[..6]=="mapping")
3ce5922001-02-18Mirar (Pontus Hagland)  { array v=indices(real_variables); return mkmapping(v,map(v,`[])); }
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; array(string) subtags=({}); array(string) languages=({}); array(float) qualities=({}); 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; } void sort_lang() { if(sorted && decoded) return; array(float) q; array(string) s=reverse(languages)-({""}), u=({}); 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); q+=({n}); u+=({sub}); return x; }); s-=({""}); decoded=1; } else q=reverse(qualities); sort(q,s,u); languages=reverse(s); qualities=reverse(q); subtags=reverse(u); sorted=1; } }
48b4112003-02-05Jonas Wallden  // Kludge for resolver problems static function _charset_decoder_func;
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 //! request has passed through all levels of the <ref>module type calling //! sequence</ref>. { Configuration conf; Protocol port_obj; //! The port object this request came from. int time; //! Time of the request, standard unix time (seconds since the epoch; 1970). string raw_url; //! The nonparsed, nontouched, non-* URL requested by the client. //! Hence, this path is unlike <ref>not_query</ref> and //! <ref>virtfile</ref> 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. 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 //! <tt>form</tt> scope in RXML. Both query (as found in the query part of //! the URL) and POST (submitted in the request body) variables share this //! scope, with query variables having priority over POST ones. In other //! words, the query part of the URL overrides whatever variables are sent //! in the request body. //! //! The indices and values of this mapping map to the names and values of //! the variable names. All data (names and values) are decoded from their //! possible transport encoding.
e0ffe32001-02-05Per Hedbor  //! //! The value is always an array
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.
9644732001-11-27Martin Stjernholm  mapping (string:mixed) misc;
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.
99e65a2003-06-18Tomas Nilsson  mapping (string:mixed) connection_misc; //! 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.
a896932001-01-03Per Hedbor  mapping (string:string) cookies; //! 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.
5588692001-04-22Per Hedbor  mapping (string:array(string)|string) request_headers;
a896932001-01-03Per Hedbor  //! Indices and values map to the names and values of all HTTP headers sent //! with the request; all data has been transport decoded, and the header //! names are canonized (lowercased) on top of that. Here is where you look //! for the "user-agent" header, the "referer" [sic!] header and similar //! interesting data provided by the client. 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>. 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. 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. string raw; //! The raw, untouched request in its entirety. string query; //! The entire raw query part (all characters after the first question mark, //! '?') of the requested URL. string not_query; //! The part of the path segment of the requested URL that is below //! the virtual server's mountpoint. For a typical server //! registering a URL with no ending path component, not_query will //! contain all characters from the leading '/' to, but not //! including, the first question mark ('?') of the URL. string extra_extension; string data; //! The raw request body, containing non-decoded post variables et cetera. string leftovers;
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.
46d4cb2001-08-22Martin Stjernholm  multiset(string) cache_status = (<>); //! Contains the caches that was hit when the request was served. //! See the docstring for @tt{$cache-status@} in the @tt{LogFormat@} //! global variable for known values, but note that the multiset //! actually never contains the value "nocache"; it's only written //! when the multiset is empty.
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 //! similar conditions that invoke @[clone_me()].
a896932001-01-03Per Hedbor  static void create(Stdio.File fd, Protocol port, Configuration conf){} void send(string|object what, int|void len){}
c5ac932003-04-22Henrik Grubbström (Grubba) #if constant(Parser.XML.Tree.XMLNSParser)
24e9142003-04-22Henrik Grubbström (Grubba)  static Parser.XML.Tree.Node xml_data; // XML data for the request. Parser.XML.Tree.Node get_xml_data() {
b105b52003-06-11Henrik Grubbström (Grubba)  if (!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);
24e9142003-04-22Henrik Grubbström (Grubba)  return xml_data = Parser.XML.Tree.parse_input(data, 0, 0, 0, 1); }
c5ac932003-04-22Henrik Grubbström (Grubba) #endif /* Parser.XML.Tree.XMLNSParser */
24e9142003-04-22Henrik Grubbström (Grubba) 
5430602001-07-21Martin Stjernholm  static string cached_url_base; 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. { // Note: Code duplication in protocols/http.pike. if (!cached_url_base) { string tmp;
3c49612001-07-21Martin Stjernholm  // First consult the port object. if (port_obj) { string host = port_obj->conf_data[conf]->hostname; if (host == "*" && conf && sizeof (host = conf->get_url())) if (sscanf (host, "%*s://%[^:/]", host) < 2) host = port_obj->ip; cached_url_base = port_obj->prot_name + "://" + host;
5430602001-07-21Martin Stjernholm  if (port_obj->port != port_obj->default_port) cached_url_base += ":" + port_obj->port; }
3c49612001-07-21Martin Stjernholm  // Then try the configuration url. else if (conf && sizeof (tmp = conf->get_url())) cached_url_base = tmp[..sizeof (tmp) - 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 //! overridden, instead another (duplicate) header line will be sent //! in the response. //! //! @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 //! adding the header directly to @tt{misc->moreheads@} or //! @tt{misc->defines[" _extra_heads"]@}.
f7ea0c2002-02-06Martin Stjernholm  { mapping hdrs = misc->defines && misc->defines[" _extra_heads"] || misc->moreheads; 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; if (hdrs == misc->moreheads) hdrs[name] = cur_val; else if (object/*(RXML.Context)*/ ctx = RXML_CONTEXT) 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 //! "Expire-Time", otherwise @[add_response_header] is typically a //! better choice. //! //! @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 //! adding the header directly to @tt{misc->moreheads@} or //! @tt{misc->defines[" _extra_heads"]@}.
f7ea0c2002-02-06Martin Stjernholm  { if (misc->defines && misc->defines[" _extra_heads"]) { misc->defines[" _extra_heads"][name] = value; if (object/*(RXML.Context)*/ ctx = RXML_CONTEXT) ctx->signal_var_change (name, "header"); } else { if (!misc->moreheads) misc->moreheads = ([]); misc->moreheads[name] = value; } }
b421932002-01-29Martin Stjernholm 
48b4112003-02-05Jonas Wallden  // Charset handling
9689f12002-09-03Martin Stjernholm  array(string) output_charset = ({}); string input_charset; void set_output_charset( string|function to, int|void mode ) {
80f43b2003-03-24Martin Stjernholm  if (object/*(RXML.Context)*/ ctx = RXML_CONTEXT) ctx->add_p_code_callback ("set_output_charset", to, mode);
9689f12002-09-03Martin Stjernholm  if( search( output_charset, to ) != -1 ) // Already done. return; switch( mode ) { 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. output_charset |= ({ to }); break; } }
48b4112003-02-05Jonas Wallden  static string charset_name(function|string what) { switch (what) { case string_to_unicode: return "ISO10646-1"; case string_to_utf8: return "UTF-8"; default: return upper_case((string) what); } } static function charset_function(function|string what, int allow_entities) { switch (what) { case "ISO-10646-1": case "ISO10646-1": case string_to_unicode: return string_to_unicode; case "UTF-8": case string_to_utf8: return string_to_utf8; default: catch { // Use entity fallback if content type allows it function fallback_func = allow_entities && lambda(string char) { return sprintf("&#x%x;", char[0]); }; _charset_decoder_func =
c2d7aa2004-04-13Martin Stjernholm  _charset_decoder_func || Roxen->_charset_decoder;
48b4112003-02-05Jonas Wallden  return _charset_decoder_func(Locale.Charset.encoder((string) what, "", fallback_func)) ->decode; }; } return lambda(string what) { return what; }; } static array(string) join_charset(string old, function|string add, function oldcodec, int allow_entities) { switch (old && upper_case(old)) { case 0: 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... return ({ charset_name(add), charset_function(add, allow_entities) }); } } 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") return ({ "ISO-8859-1", what }); } else { if (sizeof(output_charset) == 1 && upper_case(output_charset[0]) == "ISO-8859-1") return ({ "ISO-8859-1", what }); } }
48b4112003-02-05Jonas Wallden  if (!force_charset) { string charset; function encoder; foreach( output_charset, string|function f ) [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); return ({ charset, what }); } else return ({ 0, Locale.Charset.encoder((force_charset / "=")[-1])->feed(what)->drain() }); }
a896932001-01-03Per Hedbor  string scan_for_query( string f ) { if(sscanf(f,"%s?%s", f, query) == 2) { string v, a, b; foreach(query / "&", v)
ccf8312001-08-15Per Hedbor  if(sscanf(v, "%s=%s", a, b) == 2) { a = _Roxen.http_decode_string(replace(a, "+", " ")); b = _Roxen.http_decode_string(replace(b, "+", " ")); real_variables[ a ] += ({ b }); } else if(strlen( rest_query )) rest_query += "&" + _Roxen.http_decode_string( v ); else rest_query = _Roxen.http_decode_string( v );
a896932001-01-03Per Hedbor  rest_query=replace(rest_query, "+", "\000"); } return f; }
c2d7aa2004-04-13Martin Stjernholm  mapping(string: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)) { if(!file->stat) 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 ) { if( !file->len && fstat[1] >= 0 ) file->len = fstat[1]; if ( fstat[ST_MTIME] > misc->last_modified ) misc->last_modified = fstat[ST_MTIME]; } if (!file->error) file->error = Protocols.HTTP.HTTP_OK;
c2d7aa2004-04-13Martin Stjernholm 
de29ac2004-04-14Martin Stjernholm  if(!file->type) file->type="text/plain";
a7332e2004-04-20Martin Stjernholm  mapping(string:string) heads = ([]);
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 ); } 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); { string charset=""; if( stringp(file->data) ) { if (sizeof (output_charset) || has_prefix (file->type, "text/") || (String.width(file->data) > 8)) { int allow_entities = has_prefix(file->type, "text/xml") || has_prefix(file->type, "text/html"); [charset,file->data] = output_encode( file->data, allow_entities ); if( charset && (search(file["type"], "; charset=") == -1)) charset = "; charset="+charset; else charset = ""; } file->len = strlen(file->data); }
de29ac2004-04-14Martin Stjernholm  heads["Content-Type"] = file->type + charset;
c2d7aa2004-04-13Martin Stjernholm  } heads["Accept-Ranges"] = "bytes";
2284712004-04-25Henrik Grubbström (Grubba)  heads["Server"] = replace(roxenp()->version(), " ", "·");
c2d7aa2004-04-13Martin Stjernholm  if( misc->connection ) 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] &&
c2d7aa2004-04-13Martin Stjernholm  (file->len < conf->datacache->max_file_size)) { 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 = Crypto.string_to_hex(Crypto.md5()->update(data)->digest()); heads->Vary = "ETag"; } #endif /* RAM_CACHE */
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(){} void send_result(mapping|void result){} 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; c->cookies = cookies; 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; 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; } }
c8bfed2003-06-02Henrik Grubbström (Grubba) class XMLStatusNode {
1904282003-06-17Henrik Grubbström (Grubba)  inherit ElementNode;
70cd442003-07-07Martin Stjernholm  static void create(int|string code, void|string message)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
1904282003-06-17Henrik Grubbström (Grubba)  ::create("DAV:status", ([]));
c8bfed2003-06-02Henrik Grubbström (Grubba)  if (intp(code)) { code = sprintf("HTTP/1.1 %d %s", code,
70cd442003-07-07Martin Stjernholm  message||errors[code]||"Unknown status");
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
1904282003-06-17Henrik Grubbström (Grubba)  add_child(TextNode(code));
c8bfed2003-06-02Henrik Grubbström (Grubba)  } } class XMLPropStatNode {
1904282003-06-17Henrik Grubbström (Grubba)  inherit Parser.XML.Tree.ElementNode;
c8bfed2003-06-02Henrik Grubbström (Grubba) 
1904282003-06-17Henrik Grubbström (Grubba)  static mapping(string:Node) properties = ([]);
c8bfed2003-06-02Henrik Grubbström (Grubba)  static multiset(string) descriptions = (<>);
1904282003-06-17Henrik Grubbström (Grubba)  static Node prop_node; static Node description_node; void add_property(string prop_name, void|string|array(Node)|Node value)
c8bfed2003-06-02Henrik Grubbström (Grubba)  {
1904282003-06-17Henrik Grubbström (Grubba)  Node n; if (!(n = properties[prop_name])) {
a7332e2004-04-20Martin Stjernholm  string type; // The DAV client in Windows XP Pro (at least) requries types on // the date fields to parse them correctly. The type system is // of course some MS goo. switch (prop_name) { case "DAV:creationdate": type = "dateTime.tz"; break; case "DAV:getlastmodified": type = "dateTime.rfc1123"; break; // MS header - format unknown. //case "DAV:lastaccessed": type = "dateTime.tz"; break; } n = ElementNode(prop_name, type ? (["urn:schemas-microsoft-com:datatypesdt": type]) : ([]));
1904282003-06-17Henrik Grubbström (Grubba)  properties[prop_name] = n; prop_node->add_child(n);
c8bfed2003-06-02Henrik Grubbström (Grubba)  }
1904282003-06-17Henrik Grubbström (Grubba)  if (value) { if (stringp(value)) { value = TextNode(value); } if (arrayp(value)) { n->replace_children(value); } else { n->replace_children(({ value })); } } else { n->replace_children(({}));
c8bfed2003-06-02Henrik Grubbström (Grubba)  } } void add_description(string description) {
1904282003-06-17Henrik Grubbström (Grubba)  if (descriptions[description]) return;
c8bfed2003-06-02Henrik Grubbström (Grubba)  descriptions[description] = 1;
1904282003-06-17Henrik Grubbström (Grubba)  if (!description_node) { description_node = ElementNode("DAV:responsedescription", ([])); add_child(description_node); } description_node->add_child(TextNode(description + "\n"));
c8bfed2003-06-02Henrik Grubbström (Grubba)  } int http_code;
70cd442003-07-07Martin Stjernholm  static void create(int|void code, string|void message)
c8bfed2003-06-02Henrik Grubbström (Grubba)  { http_code = code || 200;
1904282003-06-17Henrik Grubbström (Grubba)  ::create("DAV:propstat", ([]));
70cd442003-07-07Martin Stjernholm  add_child(XMLStatusNode(code, message));
bd5d4d2003-12-15Henrik Grubbström (Grubba)  add_child(prop_node = ElementNode("DAV:prop", ([])));
c8bfed2003-06-02Henrik Grubbström (Grubba)  } } class MultiStatus { static mapping(string:array(Node)) status_set = ([]);
f8874d2004-04-29Martin Stjernholm  static mapping(string:string) args = ([ "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", ]);
b303da2004-03-03Henrik Grubbström (Grubba)  int(0..1) is_empty() { return !sizeof(status_set); }
c8bfed2003-06-02Henrik Grubbström (Grubba)  void add_response(string href, Node response_node) { if (!status_set[href]) { status_set[href] = ({ response_node }); } else { status_set[href] += ({ response_node }); } } //! 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.
b105b52003-06-11Henrik Grubbström (Grubba)  //! @type string|array(Node)|Node
c8bfed2003-06-02Henrik Grubbström (Grubba)  //! Property has value @[prop_value]. //! @type mapping(string:mixed) //! Operation failed as described by the mapping. //! @endmixed void add_property(string href, string prop_name,
b105b52003-06-11Henrik Grubbström (Grubba)  void|int(0..0)|string|array(Node)|Node| mapping(string:mixed) prop_value)
c8bfed2003-06-02Henrik Grubbström (Grubba)  { array(XMLStatusNode) stat_nodes; XMLStatusNode stat_node;
70cd442003-07-07Martin Stjernholm  int code = 200; string message; if (mappingp(prop_value)) { code = prop_value->error;
762b932004-03-03Martin Stjernholm  message = prop_value->rettext;
70cd442003-07-07Martin Stjernholm  }
c8bfed2003-06-02Henrik Grubbström (Grubba) 
ca04922003-12-22Henrik Grubbström (Grubba)  DAV_WERROR("Adding property %O code:%O val:%O\n", prop_name, code, prop_value);
c8bfed2003-06-02Henrik Grubbström (Grubba)  if (!(stat_nodes = status_set[href])) {
70cd442003-07-07Martin Stjernholm  status_set[href] = ({ stat_node = XMLPropStatNode(code, message) });
c8bfed2003-06-02Henrik Grubbström (Grubba)  } else {
bd5d4d2003-12-15Henrik Grubbström (Grubba)  int index;
a7332e2004-04-20Martin Stjernholm  for (; index < sizeof (stat_nodes); index++) { XMLStatusNode n = stat_nodes[index];
c8bfed2003-06-02Henrik Grubbström (Grubba)  if (n->http_code == code) { stat_node = n; break; }
bd5d4d2003-12-15Henrik Grubbström (Grubba)  if (n->http_code > code) break;
c8bfed2003-06-02Henrik Grubbström (Grubba)  } if (!stat_node) {
bd5d4d2003-12-15Henrik Grubbström (Grubba)  status_set[href] = status_set[href][..index-1] + ({ stat_node = XMLPropStatNode(code, message) }) + status_set[href][index..];
c8bfed2003-06-02Henrik Grubbström (Grubba)  } }
70cd442003-07-07Martin Stjernholm  if (message) {
c8bfed2003-06-02Henrik Grubbström (Grubba)  // FIXME: Add a global error description?
70cd442003-07-07Martin Stjernholm  stat_node->add_description(message);
c8bfed2003-06-02Henrik Grubbström (Grubba)  prop_value = 0; } stat_node->add_property(prop_name, prop_value); }
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; }
c8bfed2003-06-02Henrik Grubbström (Grubba)  Node get_xml_node() {
f8874d2004-04-29Martin Stjernholm  RootNode root = RootNode(); root->add_child (HeaderNode ((["version": "1.0", "encoding": "utf-8"]))); ElementNode node = ElementNode ("DAV:multistatus", args); root->add_child (node);
c8bfed2003-06-02Henrik Grubbström (Grubba)  array(Node) response_xml = allocate(sizeof(status_set)); 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) 
bd5d4d2003-12-15Henrik Grubbström (Grubba)  foreach(sort(indices(status_set)), string href) { array(Node) responses = status_set[href];
1904282003-06-17Henrik Grubbström (Grubba)  Node href_node = ElementNode("DAV:href", ([])); href_node->add_child(TextNode(href));
c8bfed2003-06-02Henrik Grubbström (Grubba)  (response_xml[i++] =
1904282003-06-17Henrik Grubbström (Grubba)  ElementNode("DAV:response", ([])))->
c8bfed2003-06-02Henrik Grubbström (Grubba)  replace_children(({href_node})+responses); }
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\"", ]); } class prefix(static string href_prefix) {
316f3b2004-03-23Martin Stjernholm  int(0..1) is_empty() { return MultiStatus::is_empty(); }
c8bfed2003-06-02Henrik Grubbström (Grubba)  void add_response(string href, Node response_node) { MultiStatus::add_response(href_prefix + href, response_node); } void add_property(string path, string prop_name, void|int(0..0)|string|Node|mapping(string:mixed) prop_value) { MultiStatus::add_property(href_prefix + path, prop_name, prop_value); }
f8874d2004-04-29Martin Stjernholm  void add_namespace (string namespace) { MultiStatus::add_namespace (namespace); }
c8bfed2003-06-02Henrik Grubbström (Grubba)  this_program prefix(string href_prefix) { return MultiStatus::prefix(this_program::href_prefix + href_prefix); } Node get_xml_node() { return MultiStatus::get_xml_node(); } mapping(string:mixed) http_answer() { return MultiStatus::http_answer(); } } }
c5dd9f2004-03-16Henrik Grubbström (Grubba) // Only for protyping and breaking of circularities. static class PropertySet { RequestID id; string path; Stat st; multiset(string) query_all_properties(); string|array(Parser.XML.Tree.Node)|mapping(string:mixed) query_property(string prop_name); mapping(string:mixed) start(); void unroll(); void commit(); mapping(string:mixed) set_property(string prop_name, string|array(Parser.XML.Tree.Node) value); mapping(string:mixed) set_dead_property(string prop_name, array(Parser.XML.Tree.Node) value); mapping(string:mixed) remove_property(string prop_name); mapping(string:mixed) find_properties(string mode, MultiStatus result, multiset(string)|void filt); }
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(); void start(void|int num, void|object conf); string query_internal_location(); string query_location(); string query_provides();
bd5d4d2003-12-15Henrik Grubbström (Grubba)  function(RequestID:int|mapping) query_seclevels();
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) 
316f3b2004-03-23Martin Stjernholm  PropertySet|mapping(string:mixed) query_properties(string path, RequestID id);
c8bfed2003-06-02Henrik Grubbström (Grubba)  string|array(Parser.XML.Tree.Node)|mapping(string:mixed) query_property(string path, string prop_name, RequestID id); void recurse_find_properties(string path, string mode, int depth,
a98f752004-03-03Henrik Grubbström (Grubba)  MultiStatus result, RequestID id, multiset(string)|void filt); mapping(string:mixed) patch_properties(string path, array(PatchPropertyCommand) instructions, MultiStatus result, RequestID id);
4c87da2004-04-28Henrik Grubbström (Grubba)  mapping(string:mixed) set_property (string path, string prop_name, string|array(Parser.XML.Tree.Node) value, RequestID id); mapping(string:mixed) remove_property (string path, string prop_name, RequestID id); multiset(DAVLock) find_all_locks(string path, int(0..1) recursive, RequestID id);
4908322004-04-29Martin Stjernholm  DAVLock|int(-2..1) check_locks(string path, int(0..1) recursive, RequestID id); mapping(string:mixed)|int(0..1) lock_file(string path, DAVLock lock, RequestID id); mapping(string:mixed) unlock_file (string path, DAVLock lock, RequestID id);
b303da2004-03-03Henrik Grubbström (Grubba)  mapping(string:mixed)|int(-1..0)|Stdio.File find_file(string path, RequestID id); mapping(string:mixed) delete_file(string path, RequestID id); int(0..1) recurse_delete_files(string path, MultiStatus result, 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";
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. }
e082212001-08-28Per Hedbor static mapping(string:function(void:void)) user_sql_inited = ([]);
a665382001-01-29Per Hedbor static Sql.Sql user_mysql;
69fa3e2001-01-30Per Hedbor static 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; } 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.
3342dd2001-01-19Per Hedbor }
e082212001-08-28Per Hedbor #ifdef THREADS
61e1cd2001-09-05Martin Nilsson static Thread.Mutex mutex = Thread.Mutex();
e082212001-08-28Per Hedbor #endif
7f66d82001-08-24Martin Nilsson //! @appears User
3342dd2001-01-19Per Hedbor class User( UserDB database ) {
69fa3e2001-01-30Per Hedbor  static string table;
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(); return !sizeof(c) || crypt(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 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.
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
69fa3e2001-01-30Per Hedbor  static string module_name( RoxenModule module ) { 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())+"'","%","%%"); }
3342dd2001-01-19Per Hedbor 
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. //! //! 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;
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. { }
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  }
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 }