835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer. // Copyright © 1996 - 2001, Roxen IS.
4cfe892003-06-16Henrik Grubbström (Grubba) // $Id: module.pike,v 1.138 2003/06/16 16:07:25 grubba Exp $
c20c872000-02-16Per Hedbor  #include <module_constants.h>
b1fca01996-11-12Per Hedbor #include <module.h>
c5e0961999-10-04Per Hedbor #include <request_trace.h>
b275871998-05-23Henrik Grubbström (Grubba) 
c089042001-06-11Per Hedbor constant __pragma_save_parent__ = 1;
a59d252000-07-04Per Hedbor inherit "basic_defvar";
9d9b9b1999-11-17Per Hedbor mapping(string:array(int)) error_log=([]);
c5e0961999-10-04Per Hedbor 
9d9b9b1999-11-17Per Hedbor constant is_module = 1;
a730c22001-01-19Per Hedbor // constant module_type = MODULE_ZERO; // constant module_name = "Unnamed module"; // constant module_doc = "Undocumented"; constant module_unique = 1;
c5e0961999-10-04Per Hedbor 
a59d252000-07-04Per Hedbor 
7211372001-06-29Martin Stjernholm private Configuration _my_configuration;
0aa37d2001-08-23Martin Stjernholm private string _module_local_identifier;
2e8d0f2001-06-28Martin Stjernholm private string _module_identifier =
7211372001-06-29Martin Stjernholm  lambda() {
3557f52001-06-30Martin Stjernholm  mixed init_info = roxen->bootstrap_info->get(); if (arrayp (init_info)) {
0aa37d2001-08-23Martin Stjernholm  [_my_configuration, _module_local_identifier] = init_info; return _my_configuration->name + "/" + _module_local_identifier;
7211372001-06-29Martin Stjernholm  } }();
a59d252000-07-04Per Hedbor static mapping _api_functions = ([]); string|array(string) module_creator; string module_url; RXML.TagSet module_tag_set;
c20c872000-02-16Per Hedbor /* These functions exists in here because otherwise the messages in * the event log does not always end up in the correct * module/configuration. And the reason for that is that if the * messages are logged from subclasses in the module, the DWIM in * roxenlib.pike cannot see that they are logged from a module. This
b7eaa22001-08-09Martin Stjernholm  * solution is not really all that beautiful, but it works. :-)
c20c872000-02-16Per Hedbor  */ void report_fatal( mixed ... args ) { predef::report_fatal( @args ); } void report_error( mixed ... args ) { predef::report_error( @args ); } void report_notice( mixed ... args ) { predef::report_notice( @args ); } void report_debug( mixed ... args ) { predef::report_debug( @args ); }
b9ec252000-01-05Martin Stjernholm string module_identifier()
0aa37d2001-08-23Martin Stjernholm //! Returns a string that uniquely identifies this module instance //! within the server. The identifier is the same as //! @[Roxen.get_module] and @[Roxen.get_modname] handles.
b9ec252000-01-05Martin Stjernholm {
4246ca2001-06-28Martin Stjernholm #if 1
2e8d0f2001-06-28Martin Stjernholm  return _module_identifier;
4246ca2001-06-28Martin Stjernholm #else
2e8d0f2001-06-28Martin Stjernholm  if (!_module_identifier) {
225c8e2001-01-31Marcus Comstedt  string|mapping name = this_object()->register_module()[1];
b9ec252000-01-05Martin Stjernholm  if (mappingp (name)) name = name->standard;
8d49192000-03-18Martin Stjernholm  string cname = sprintf ("%O", my_configuration()); if (sscanf (cname, "Configuration(%s", cname) == 1 && sizeof (cname) && cname[-1] == ')') cname = cname[..sizeof (cname) - 2];
55a8662000-11-20Per Hedbor  _module_identifier = sprintf ("%s,%s",
ad56072001-01-29Per Hedbor  name||this_object()->module_name, cname);
b9ec252000-01-05Martin Stjernholm  } return _module_identifier;
2e8d0f2001-06-28Martin Stjernholm #endif
b9ec252000-01-05Martin Stjernholm }
0aa37d2001-08-23Martin Stjernholm string module_local_id() //! Returns a string that uniquely identifies this module instance //! within the configuration. The returned string is the same as the //! part after the first '/' in the one returned from //! @[module_identifier]. { return _module_local_identifier; }
298be12001-08-13Per Hedbor RoxenModule this_module() { return this_object(); // To be used from subclasses. }
b6fb051999-11-02Per Hedbor string _sprintf() {
b7eaa22001-08-09Martin Stjernholm  return sprintf ("RoxenModule(%s)", _module_identifier || "?");
b6fb051999-11-02Per Hedbor }
c5e0961999-10-04Per Hedbor array register_module() { return ({
a730c22001-01-19Per Hedbor  this_object()->module_type,
ad56072001-01-29Per Hedbor  this_object()->module_name, this_object()->module_doc,
c5e0961999-10-04Per Hedbor  0, module_unique,
5b81122002-02-26Marcus Wellhardh  this_object()->module_locked,
c5e0961999-10-04Per Hedbor  }); }
2a2a5b1996-12-01Per Hedbor string fix_cvs(string from) {
fd0b6f1996-12-02Per Hedbor  from = replace(from, ({ "$", "Id: "," Exp $" }), ({"","",""}));
2a2a5b1996-12-01Per Hedbor  sscanf(from, "%*s,v %s", from);
00730e1999-11-18Per Hedbor  return replace(from,"/","-");
2a2a5b1996-12-01Per Hedbor }
72ac572000-01-31Per Hedbor int module_dependencies(Configuration configuration,
f5a2741999-10-18Per Hedbor  array (string) modules, int|void now)
facee72000-07-18Johan Sundström //! If your module depends on other modules present in the server, //! calling <pi>module_dependencies()</pi>, supplying an array of
cb9c222000-09-06Martin Stjernholm //! module identifiers. A module identifier is either the filename //! minus extension, or a string on the form that Roxen.get_modname //! returns. In the latter case, the <config name> and <copy> parts //! are ignored.
edb5061997-08-25Peter Bortas {
cb9c222000-09-06Martin Stjernholm  modules = map (modules, lambda (string modname) { sscanf ((modname / "/")[-1], "%[^#]", modname); return modname; }); Configuration conf = configuration || my_configuration(); if (!conf) report_warning ("Configuration not resolved; module(s) %s that %s " "depend on weren't added.", String.implode_nicely (modules),
4246ca2001-06-28Martin Stjernholm  module_identifier());
cb9c222000-09-06Martin Stjernholm  else conf->add_modules( modules, now );
edb5061997-08-25Peter Bortas  return 1; }
2a2a5b1996-12-01Per Hedbor string file_name_and_stuff() {
a59d252000-07-04Per Hedbor  return ("<b>Loaded from:</b> "+(roxen->filename(this_object()))+"<br>"+ (this_object()->cvs_version?
00730e1999-11-18Per Hedbor  "<b>CVS Version: </b>"+
a59d252000-07-04Per Hedbor  fix_cvs(this_object()->cvs_version)+"\n":""));
2a2a5b1996-12-01Per Hedbor }
5839c31999-01-22Marcus Comstedt 
9f2a971999-11-29Per Hedbor Configuration my_configuration()
facee72000-07-18Johan Sundström //! Returns the Configuration object of the virtual server the module //! belongs to.
b1fca01996-11-12Per Hedbor {
2e8d0f2001-06-28Martin Stjernholm  return _my_configuration;
b1fca01996-11-12Per Hedbor }
7fb7f51999-11-29Per Hedbor nomask void set_configuration(Configuration c)
5839c31999-01-22Marcus Comstedt { if(_my_configuration && _my_configuration != c) error("set_configuration() called twice.\n"); _my_configuration = c; }
48679b2000-02-03Johan Sundström void set_module_creator(string|array(string) c)
facee72000-07-18Johan Sundström //! Set the name and optionally email address of the author of the //! module. Names on the format "author name <author_email>" will //! end up as links on the module's information page in the admin //! interface. In the case of multiple authors, an array of such //! strings can be passed.
b1fca01996-11-12Per Hedbor { module_creator = c; } void set_module_url(string to)
facee72000-07-18Johan Sundström //! A common way of referring to a location where you maintain //! information about your module or similar. The URL will turn up //! on the module's information page in the admin interface, //! referred to as the module's home page.
b1fca01996-11-12Per Hedbor { module_url = to; } void free_some_sockets_please(){}
7fb7f51999-11-29Per Hedbor void start(void|int num, void|Configuration conf) {}
b1fca01996-11-12Per Hedbor 
a59d252000-07-04Per Hedbor string status() {}
c5e0961999-10-04Per Hedbor 
7fb7f51999-11-29Per Hedbor string info(Configuration conf)
72ac572000-01-31Per Hedbor {
facee72000-07-18Johan Sundström  return (this_object()->register_module()[2]);
df6cd11998-10-13Per Hedbor }
b1fca01996-11-12Per Hedbor 
a730c22001-01-19Per Hedbor string sname( ) {
4a50182001-01-30Per Hedbor  return my_configuration()->otomod[ this_object() ];
a730c22001-01-19Per Hedbor }
2f4ac12000-11-02Per Hedbor ModuleInfo my_moduleinfo( )
6533f22001-08-23Martin Nilsson //! Returns the associated @[ModuleInfo] object
2f4ac12000-11-02Per Hedbor {
a730c22001-01-19Per Hedbor  string f = sname();
2f4ac12000-11-02Per Hedbor  if( f ) return roxen.find_module( (f/"#")[0] ); }
2c13a81999-11-10Per Hedbor void save_me()
e7e6031999-11-05Per Hedbor { my_configuration()->save_one( this_object() );
2f4ac12000-11-02Per Hedbor  my_configuration()->module_changed( my_moduleinfo(), this_object() );
e7e6031999-11-05Per Hedbor }
1e9a1b2001-02-21Per Hedbor void save() { save_me(); } string comment() { return ""; }
b1fca01996-11-12Per Hedbor 
5839c31999-01-22Marcus Comstedt string query_internal_location()
facee72000-07-18Johan Sundström //! Returns the internal mountpoint, where <ref>find_internal()</ref> //! is mounted.
5839c31999-01-22Marcus Comstedt { if(!_my_configuration) error("Please do not call this function from create()!\n"); return _my_configuration->query_internal_location(this_object()); }
525f722000-12-05Martin Nilsson string query_absolute_internal_location(RequestID id) //! Returns the internal mountpoint as an absolute path. { return (id->misc->site_prefix_path || "") + query_internal_location(); }
b7c45e1997-01-27Per Hedbor string query_location()
1e9a1b2001-02-21Per Hedbor //! Returns the mountpoint as an absolute path. The default //! implementation uses the "location" configuration variable in the //! module.
b7c45e1997-01-27Per Hedbor { string s; catch{s = query("location");}; return s; }
162bd52000-02-20Martin Stjernholm array(string) location_urls()
facee72000-07-18Johan Sundström //! Returns an array of all locations where the module is mounted.
162bd52000-02-20Martin Stjernholm { string loc = query_location(); if (!loc) return ({}); if(!_my_configuration) error("Please do not call this function from create()!\n");
3adf202000-03-06Jonas Wallden  array(string) urls = copy_value(_my_configuration->query("URLs"));
7984d62000-10-06Martin Stjernholm  string hostname; if (string world_url = _my_configuration->query ("MyWorldLocation")) sscanf (world_url, "%*s://%s%*[:/]", hostname); if (!hostname) hostname = gethostname(); for (int i = 0; i < sizeof (urls); i++)
21182a2001-10-05Per Hedbor  { urls[i] = (urls[i]/"#")[0];
7984d62000-10-06Martin Stjernholm  if (sizeof (urls[i]/"*") == 2)
162bd52000-02-20Martin Stjernholm  urls[i] = replace(urls[i], "*", hostname);
21182a2001-10-05Per Hedbor  }
162bd52000-02-20Martin Stjernholm  return map (urls, `+, loc[1..]); }
ae32d01998-03-23David Hedbor /* By default, provide nothing. */
72ac572000-01-31Per Hedbor string query_provides() { return 0; }
b7c45e1997-01-27Per Hedbor 
bc0fa02001-03-08Per Hedbor function(RequestID:int|mapping) query_seclevels()
b1fca01996-11-12Per Hedbor {
a59d252000-07-04Per Hedbor  if(catch(query("_seclevels")) || (query("_seclevels") == 0))
bc0fa02001-03-08Per Hedbor  return 0; return roxen.compile_security_pattern(query("_seclevels"),this_object());
b1fca01996-11-12Per Hedbor }
8b8ecf2000-08-28Johan Sundström Stat stat_file(string f, RequestID id){} array(string) find_dir(string f, RequestID id){} mapping(string:Stat) find_dir_stat(string f, RequestID id)
a476711997-10-20Henrik Grubbström (Grubba) {
b275871998-05-23Henrik Grubbström (Grubba)  TRACE_ENTER("find_dir_stat(): \""+f+"\"", 0);
a476711997-10-20Henrik Grubbström (Grubba)  array(string) files = find_dir(f, id);
8b8ecf2000-08-28Johan Sundström  mapping(string:Stat) res = ([]);
a476711997-10-20Henrik Grubbström (Grubba) 
0c8b9a1997-10-22Henrik Grubbström (Grubba)  foreach(files || ({}), string fname) {
b275871998-05-23Henrik Grubbström (Grubba)  TRACE_ENTER("stat()'ing "+ f + "/" + fname, 0);
c0f4542001-02-19Jonas Wallden  Stat st = stat_file(replace(f + "/" + fname, "//", "/"), id);
a476711997-10-20Henrik Grubbström (Grubba)  if (st) {
0c8b9a1997-10-22Henrik Grubbström (Grubba)  res[fname] = st;
b275871998-05-23Henrik Grubbström (Grubba)  TRACE_LEAVE("OK"); } else { TRACE_LEAVE("No stat info");
a476711997-10-20Henrik Grubbström (Grubba)  } }
b275871998-05-23Henrik Grubbström (Grubba)  TRACE_LEAVE("");
a476711997-10-20Henrik Grubbström (Grubba)  return(res); }
a59d252000-07-04Per Hedbor 
265ca42003-06-02Henrik Grubbström (Grubba) // ISO 8601 Date and Time // RFC 2518 23.2 // No fraction, UTC only. static string iso8601_date_time(int ts) { mapping(string:int) gmt = gmtime(ts); return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", 1900 + gmt->year, gmt->mon, gmt->mday, gmt->hour, gmt->min, gmt->sec); } //! Returns a multiset with the names off all supported properties. multiset(string) query_all_properties(string path, RequestID id) { Stat st = stat_file(path, id); if (!st) return (<>); multiset(string) res = (< "DAV:creationdate", // 13.1 "DAV:displayname", // 13.2 "DAV:getlastmodified", // 13.7
6c09282003-06-11Henrik Grubbström (Grubba)  "DAV:resourcetype", // 13.9
265ca42003-06-02Henrik Grubbström (Grubba)  >); if (st->isreg) { res += (< "DAV:getcontentlength", // 13.4 "DAV:getcontenttype", // 13.5
4cfe892003-06-16Henrik Grubbström (Grubba)  "http://apache.org/dav/props/executable",
265ca42003-06-02Henrik Grubbström (Grubba)  >); } return res; } //! Returns the value of the specified property, or an error code //! mapping. //! //! @note //! Returning a string is shorthand for returning an array //! with a single text node. string|array(Parser.XML.Tree.Node)|mapping(string:mixed) query_property(string path, string prop_name, RequestID id) { Stat st = stat_file(path, id);
425da62003-06-16Henrik Grubbström (Grubba)  if (!st) return Roxen.http_low_answer(404, "No such file or directory.");
265ca42003-06-02Henrik Grubbström (Grubba)  switch(prop_name) { case "DAV:creationdate": // 13.1 return iso8601_date_time(st->ctime); case "DAV:displayname": // 13.2 return combine_path(query_location(), path); case "DAV:getcontentlength": // 13.4 if (st->isreg) { return (string)st->size; } break; case "DAV:getcontenttype": // 13.5 if (st->isreg) { return id->conf-> type_from_filename(path, 0, lower_case(Roxen.extension(path, id))); } break; case "DAV:getlastmodified": // 13.7 return iso8601_date_time(st->mtime);
6c09282003-06-11Henrik Grubbström (Grubba)  case "DAV:resourcetype": // 13.9 if (st->isdir) { return ({ Parser.XML.Tree.Node(Parser.XML.Tree.XML_ELEMENT, "DAV:collection", ([]), 0, "DAV:collection") }); // 12.2 } return "";
4cfe892003-06-16Henrik Grubbström (Grubba)  case "http://apache.org/dav/props/executable": // http://www.webdav.org/mod_dav/: // // Name: executable // Namespace: http://apache.org/dav/props/ // Purpose: Describes the executable status of the resource. // Value: "T" | "F" (case is significant) // Description: This property is defined by mod_dav's default // repository, the "filesystem" repository. It // corresponds to the "executable" permission flag // in most filesystems. // // This property is not defined on collections. if (st->isreg) { if (o->mode & 0111) return "T"; return "F"; } break;
265ca42003-06-02Henrik Grubbström (Grubba)  default: break; }
425da62003-06-16Henrik Grubbström (Grubba)  // RFC 2518 8.1: // A request to retrieve the value of a property which does not // exist is an error and MUST be noted, if the response uses a // multistatus XML element, with a response XML element which // contains a 404 (Not Found) status value. return Roxen.http_low_answer(404, "No such property.");
265ca42003-06-02Henrik Grubbström (Grubba) } //! Attempt to set property @[prop_name] for @[path] to @[value]. //! //! @param value //! Value to set the node to. //! The case of an array of a single text node is special cased, //! and is sent as a @expr{string@}. //! //! @returns //! Returns a result mapping. May return @expr{0@} (zero) on success. //! //! @note //! Actual changing of the property should be done first //! when @[patch_property_commit()] is called, or unrolled //! when @[patch_property_unroll()] is called. //! //! @note //! Overloaded variants should only set live properties; //! setting of dead properties should be done throuh //! overloading of @[set_dead_property()]. mapping(string:mixed) set_property(string path, string prop_name, string|array(Parser.XML.Tree.Node) value, RequestID id) { switch(prop_name) {
4cfe892003-06-16Henrik Grubbström (Grubba)  case "http://apache.org/dav/props/executable": // FIXME: Could probably be implemented R/W. // FALL_THROUGH
265ca42003-06-02Henrik Grubbström (Grubba)  case "DAV:creationdate": // 13.1 case "DAV:displayname": // 13.2 case "DAV:getcontentlength": // 13.4 case "DAV:getcontenttype": // 13.5 case "DAV:getlastmodified": // 13.7 return Roxen.http_low_answer(409,
4cfe892003-06-16Henrik Grubbström (Grubba)  "Attempt to set read-only property.");
265ca42003-06-02Henrik Grubbström (Grubba)  } return set_dead_property(path, prop_name, value, id); } //! Attempt to set dead property @[prop_name] for @[path] to @[value]. //! //! @returns //! Returns a result mapping. May return @expr{0@} (zero) on success. //! //! @note //! Actual changing of the property should be done first //! when @[patch_property_commit()] is called, or unrolled //! when @[patch_property_unroll()] is called. //! //! @note //! This function is called as a fallback by @[set_property()] //! if all else fails. //! //! @note //! The default implementation currently does not support setting //! of dead properties, and will return an error code. mapping(string:mixed) set_dead_property(string path, string prop_name, array(Parser.XML.Tree.Node) value, RequestID id) { return Roxen.http_low_answer(405, "Setting of dead properties is not supported."); } //! Attempt to remove the property @[prop_name] for @[path]. //! //! @note //! Actual removal of the property should be done first //! when @[patch_property_commit()] is called, or unrolled //! when @[patch_property_unroll()] is called. //! //! @returns //! Returns a result mapping. May return @expr{0@} (zero) on success. //! //! @note //! The default implementation does not support deletion. mapping(string:mixed) remove_property(string path, string prop_name, RequestID id) { switch(prop_name) { case "DAV:creationdate": // 13.1 case "DAV:displayname": // 13.2 case "DAV:getcontentlength": // 13.4 case "DAV:getcontenttype": // 13.5 case "DAV:getlastmodified": // 13.7 return Roxen.http_low_answer(409, "Attempt to remove a read-only property."); } return Roxen.http_low_answer(404, "Attempt to remove an unknown property."); } //! Default implementation of some RFC 2518 properties. //! //! @param path //! @[query_location()]-relative path. //! @param mode //! Query-mode. Currently one of //! @string mode //! @value "DAV:propname" //! Query after names of supported properties. //! @value "DAV:allprop" //! Query after all properties and their values. //! @value "DAV:prop" //! Query after properties specified by @[filt] and //! their values. //! @endstring //! @param result //! Result object. //! @param id //! Id of the current request. //! @param filt //! Optional multiset of requested properties. If this parameter //! is @expr{0@} (zero) then all available properties are requested. //! //! @note //! id->not_query() does not necessarily contain the same value as @[path]. void find_properties(string path, string mode, MultiStatus result, RequestID id, multiset(string)|void filt) { Stat st = stat_file(path, id); if (!st) return; switch(mode) { case "DAV:propname": foreach(indices(query_all_properties(path, id)), string prop_name) { result->add_property(path, prop_name, ""); } return; case "DAV:allprop": filt = query_all_properties(path, id); // FALL_THROUGH case "DAV:prop": foreach(indices(filt), string prop_name) { result->add_property(path, prop_name, query_property(path, prop_name, id)); } return; } // FIXME: Unsupported DAV operation. return; } void recurse_find_properties(string path, string mode, int depth, MultiStatus result, RequestID id, multiset(string)|void filt) { Stat st = stat_file(path, id); if (!st) return; find_properties(path, mode, result, id, filt); if ((depth <= 0) || !st->isdir) return; depth--; foreach(find_dir(path, id), string filename) { recurse_find_properties(combine_path(path, filename), mode, depth, result, id, filt); } } // RFC 2518 8.2 // Instructions MUST either all be executed or none executed. // Thus if any error occurs during procesing all executed // instructions MUST be undone and a proper error result // returned. //! Signal start of patching of properties for @[path]. void patch_property_start(string path, RequestID id) { } //! Patching of the properties for @[path] failed. //! Restore the state to what it was when @[patch_property_start()] //! was called. void patch_property_unroll(string path, RequestID id) { } //! Patching of the properties for @[path] succeeded. void patch_property_commit(string path, RequestID id) { } void patch_properties(string path, array(PatchPropertyCommand) instructions, MultiStatus result, RequestID id) { patch_property_start(path, id); array(mapping(string:mixed)) results; mixed err = catch { results = instructions->execute(path, this_object(), id); }; if (err) { report_debug("patch_properties() failed:\n" "%s\n", describe_backtrace(err)); mapping(string:mixed) answer = Roxen.http_low_answer(500, "Internal Server Error."); foreach(instructions, PatchPropertyCommand instr) { result->add_property(path, instr->property_name, answer); } patch_property_unroll(path, id); } else { int any_failed; foreach(results, mapping(string:mixed) answer) { if (any_failed = (answer && (answer->error >= 300))) { break; } } if (any_failed) { // Unroll and fail any succeeded items. int i; mapping(string:mixed) answer = Roxen.http_low_answer(424, "Failed dependency."); for(i = 0; i < sizeof(results); i++) { if (!results[i] || results[i]->error < 300) { result->add_property(path, instructions[i]->property_name, answer); } else { result->add_property(path, instructions[i]->property_name, results[i]); } } patch_property_unroll(path, id); } else { int i; for(i = 0; i < sizeof(results); i++) { result->add_property(path, instructions[i]->property_name, results[i]); } patch_property_commit(path, id); } } } void recurse_patch_properties(string path, int depth, array(PatchPropertyCommand) instructions, MultiStatus result, RequestID id) { Stat st = stat_file(path, id); patch_properties(path, instructions, result, id); if (!st || (depth <= 0) || !st->isdir) return; depth--; foreach(find_dir(path, id), string filename) { recurse_patch_properties(combine_path(path, filename), depth, instructions, result, id); } }
8b8ecf2000-08-28Johan Sundström string real_file(string f, RequestID id){}
b1fca01996-11-12Per Hedbor 
4f4bc11998-02-04Per Hedbor void add_api_function( string name, function f, void|array(string) types) { _api_functions[name] = ({ f, types }); } mapping api_functions() { return _api_functions; }
60b81c2001-01-04Martin Nilsson #if ROXEN_COMPAT <= 1.4
9ed3ea2000-08-06Martin Stjernholm mapping(string:function) query_tag_callers()
facee72000-07-18Johan Sundström //! Compat
48ca161999-05-18Per Hedbor {
9ed3ea2000-08-06Martin Stjernholm  mapping(string:function) m = ([]);
48ca161999-05-18Per Hedbor  foreach(glob("tag_*", indices( this_object())), string q) if(functionp( this_object()[q] )) m[replace(q[4..], "_", "-")] = this_object()[q]; return m; }
9ed3ea2000-08-06Martin Stjernholm mapping(string:function) query_container_callers() //! Compat
48ca161999-05-18Per Hedbor {
9ed3ea2000-08-06Martin Stjernholm  mapping(string:function) m = ([]);
48ca161999-05-18Per Hedbor  foreach(glob("container_*", indices( this_object())), string q) if(functionp( this_object()[q] )) m[replace(q[10..], "_", "-")] = this_object()[q]; return m; }
60b81c2001-01-04Martin Nilsson #endif
48ca161999-05-18Per Hedbor 
9ed3ea2000-08-06Martin Stjernholm mapping(string:array(int|function)) query_simpletag_callers()
7d2a5d2000-01-31Martin Nilsson {
9ed3ea2000-08-06Martin Stjernholm  mapping(string:array(int|function)) m = ([]);
7d2a5d2000-01-31Martin Nilsson  foreach(glob("simpletag_*", indices(this_object())), string q) if(functionp(this_object()[q]))
d6b20a2000-02-08Martin Stjernholm  m[replace(q[10..],"_","-")] = ({ intp (this_object()[q + "_flags"]) && this_object()[q + "_flags"], this_object()[q] });
7d2a5d2000-01-31Martin Nilsson  return m;
48ca161999-05-18Per Hedbor }
ea062e1999-11-21Martin Nilsson 
9ed3ea2000-08-06Martin Stjernholm mapping(string:array(int|function)) query_simple_pi_tag_callers() { mapping(string:array(int|function)) m = ([]); foreach (glob ("simple_pi_tag_*", indices (this_object())), string q) if (functionp (this_object()[q])) m[replace (q[sizeof ("simple_pi_tag_")..], "_", "-")] = ({(intp (this_object()[q + "_flags"]) && this_object()[q + "_flags"]) | RXML.FLAG_PROC_INSTR, this_object()[q]}); return m; }
1b2b752000-01-07Martin Stjernholm RXML.TagSet query_tag_set()
b9ec252000-01-05Martin Stjernholm {
395e462000-01-18Martin Stjernholm  if (!module_tag_set) { array(function|program|object) tags = filter (rows (this_object(), glob ("Tag*", indices (this_object()))),
d6b0f82003-01-15Henrik Grubbström (Grubba)  lambda(mixed x) { return functionp(x)||programp(x); });
395e462000-01-18Martin Stjernholm  for (int i = 0; i < sizeof (tags); i++) if (programp (tags[i])) if (!tags[i]->is_RXML_Tag) tags[i] = 0; else tags[i] = tags[i](); else { tags[i] = tags[i](); // Bogosity: The check is really a little too late here.. if (!tags[i]->is_RXML_Tag) tags[i] = 0; } tags -= ({0}); module_tag_set =
dd9a412001-08-24Martin Stjernholm  (this_object()->ModuleTagSet || RXML.TagSet) (this_object(), "", tags);
395e462000-01-18Martin Stjernholm  } return module_tag_set;
b9ec252000-01-05Martin Stjernholm }
12e79e1999-12-07Martin Nilsson mixed get_value_from_file(string path, string index, void|string pre)
ea062e1999-11-21Martin Nilsson {
7fb7f51999-11-29Per Hedbor  Stdio.File file=Stdio.File();
ea062e1999-11-21Martin Nilsson  if(!file->open(path,"r")) return 0;
12e79e1999-12-07Martin Nilsson  if(index[sizeof(index)-2..sizeof(index)-1]=="()") { return compile_string((pre||"")+file->read())[index[..sizeof(index)-3]](); } return compile_string((pre||"")+file->read())[index];
ea062e1999-11-21Martin Nilsson }
a730c22001-01-19Per Hedbor 
2be1982001-08-01Per Hedbor static private mapping __my_tables = ([]);
2466122001-08-01Per Hedbor array(mapping(string:mixed)) sql_query( string query, mixed ... args )
2be1982001-08-01Per Hedbor //! Do a SQL-query using @[get_my_sql], the table names in the query //! should be written as &table; instead of table. As an example, if //! the tables 'meta' and 'data' have been created with create_tables //! or get_my_table, this query will work: //! //! SELECT &meta;.id AS id, &data;.data as DATA //! FROM &data;, &meta; WHERE &my.meta;.xsize=200 //! { return get_my_sql()->query( replace( query, __my_tables ), @args ); }
2466122001-08-01Per Hedbor object sql_big_query( string query, mixed ... args )
2be1982001-08-01Per Hedbor //! Identical to @[sql_query], but the @[Sql.sql()->big_query] method //! will be used instead of the @[Sql.sql()->query] method. { return get_my_sql()->big_query( replace( query, __my_tables ), @args ); }
2466122001-08-01Per Hedbor array(mapping(string:mixed)) sql_query_ro( string query, mixed ... args ) //! Do a read-only SQL-query using @[get_my_sql], the table names in the query //! should be written as &table; instead of table. As an example, if //! the tables 'meta' and 'data' have been created with create_tables //! or get_my_table, this query will work: //! //! SELECT &meta;.id AS id, &data;.data as DATA //! FROM &data;, &meta; WHERE &my.meta;.xsize=200 //! { return get_my_sql(1)->query( replace( query, __my_tables ), @args ); } object sql_big_query_ro( string query, mixed ... args ) //! Identical to @[sql_query_ro], but the @[Sql.sql()->big_query] method //! will be used instead of the @[Sql.sql()->query] method. { return get_my_sql(1)->big_query( replace( query, __my_tables ), @args ); }
6d22f92003-04-23Martin Stjernholm static int create_sql_tables( mapping(string:array(string)) definitions,
3a7e562001-08-23Per Hedbor  string|void comment, int|void no_unique_names )
2be1982001-08-01Per Hedbor //! Create multiple tables in one go. See @[get_my_table]
3a7e562001-08-23Per Hedbor //! Returns the number of tables that were actually created.
2be1982001-08-01Per Hedbor {
3a7e562001-08-23Per Hedbor  int ddc;
59ccd82001-08-14Per Hedbor  if( !no_unique_names )
6d22f92003-04-23Martin Stjernholm  foreach( indices( definitions ), string t ) ddc+=get_my_table( t, definitions[t], comment, 1 );
59ccd82001-08-14Per Hedbor  else { Sql.Sql sql = get_my_sql();
6d22f92003-04-23Martin Stjernholm  foreach( indices( definitions ), string t )
59ccd82001-08-14Per Hedbor  {
3a7e562001-08-23Per Hedbor  if( !catch {
6d22f92003-04-23Martin Stjernholm  sql->query("CREATE TABLE "+t+" ("+definitions[t]*","+")" );
3a7e562001-08-23Per Hedbor  } ) ddc++;
59ccd82001-08-14Per Hedbor  DBManager.is_module_table( this_object(), my_db, t, comment ); } }
3a7e562001-08-23Per Hedbor  return ddc;
2be1982001-08-01Per Hedbor }
2466122001-08-01Per Hedbor static string sql_table_exists( string name )
298be12001-08-13Per Hedbor //! Return the real name of the table 'name' if it exists.
2466122001-08-01Per Hedbor { if(strlen(name)) name = "_"+name; string res = hash(_my_configuration->name)->digits(36) + "_" + replace(sname(),"#","_") + name;
8214e62001-08-01Per Hedbor  return catch(get_my_sql()->query( "SELECT * FROM "+res+" LIMIT 1" ))?0:res;
2466122001-08-01Per Hedbor }
3a7e562001-08-23Per Hedbor static string|int get_my_table( string|array(string) name, void|array(string)|string defenition, string|void comment, int|void flag )
1e9a1b2001-02-21Per Hedbor //! @decl string get_my_table( string name, array(string) types ) //! @decl string get_my_table( string name, string defenition ) //! @decl string get_my_table( string defenition ) //! @decl string get_my_table( array(string) defenition ) //! //! Returns the name of a table in the 'shared' database that is //! unique for this module. It is possible to select another database //! by using @[set_my_db] before calling this function.
2be1982001-08-01Per Hedbor //! //! You can use @[create_sql_tables] instead of this function if you want //! to create more than one table in one go.
1e9a1b2001-02-21Per Hedbor //!
3a7e562001-08-23Per Hedbor //! If @[flag] is true, return 1 if a table was created, and 0 otherwise. //!
1e9a1b2001-02-21Per Hedbor //! In the first form, @[name] is the (postfix of) the name of the
6d22f92003-04-23Martin Stjernholm //! table, and @[types] is an array of definitions, as an example:
1e9a1b2001-02-21Per Hedbor //!
3a7e562001-08-23Per Hedbor //!
6533f22001-08-23Martin Nilsson //! @code{
1e9a1b2001-02-21Per Hedbor //! cache_table = get_my_table( "cache", ({ //! "id INT UNSIGNED AUTO_INCREMENT", //! "data BLOB", //! }) );
6533f22001-08-23Martin Nilsson //! @} //!
1e9a1b2001-02-21Per Hedbor //! In the second form, the whole table defenition is instead sent as //! a string. The cases where the name is not included (the third and //! fourth form) is equivalent to the first two cases with the name "" //!
a0487b2001-07-31Per Hedbor //! If the table does not exist in the datbase, it is created.
1e9a1b2001-02-21Per Hedbor //!
6533f22001-08-23Martin Nilsson //! @note //! This function may not be called from create //
a0487b2001-07-31Per Hedbor // If it exists, but it's defenition is different, the table will be // altered with a ALTER TABLE call to conform to the defenition. This // might not work if the database the table resides in is not a MySQL // database (normally it is, but it's possible, using @[set_my_db], // to change this).
1e9a1b2001-02-21Per Hedbor {
2be1982001-08-01Per Hedbor  string oname;
3a7e562001-08-23Per Hedbor  int ddc;
1e9a1b2001-02-21Per Hedbor  if( !defenition ) { defenition = name;
2be1982001-08-01Per Hedbor  oname = name = "";
1e9a1b2001-02-21Per Hedbor  } else if(strlen(name))
2be1982001-08-01Per Hedbor  name = "_"+(oname = name);
a730c22001-01-19Per Hedbor 
a665382001-01-29Per Hedbor  Sql.Sql sql = get_my_sql();
2466122001-08-01Per Hedbor 
1e9a1b2001-02-21Per Hedbor  string res = hash(_my_configuration->name)->digits(36) + "_" + replace(sname(),"#","_") + name;
a665382001-01-29Per Hedbor  if( !sql ) { report_error("Failed to get SQL handle, permission denied for "+my_db+"\n"); return 0; }
1e9a1b2001-02-21Per Hedbor  if( arrayp( defenition ) ) defenition *= ", ";
a665382001-01-29Per Hedbor  if( catch(sql->query( "SELECT * FROM "+res+" LIMIT 1" )) )
1e9a1b2001-02-21Per Hedbor  {
3a7e562001-08-23Per Hedbor  ddc++;
1e9a1b2001-02-21Per Hedbor  mixed error = catch { get_my_sql()->query( "CREATE TABLE "+res+" ("+defenition+")" );
298be12001-08-13Per Hedbor  DBManager.is_module_table( this_object(), my_db, res, oname+"\0"+comment );
1e9a1b2001-02-21Per Hedbor  }; if( error ) { if( strlen( name ) ) name = " "+name;
2466122001-08-01Per Hedbor  report_error( "Failed to create table"+name+": "+ describe_error( error ) );
1e9a1b2001-02-21Per Hedbor  return 0; }
3a7e562001-08-23Per Hedbor  if( flag ) { __my_tables[ "&"+oname+";" ] = res; return ddc; }
2be1982001-08-01Per Hedbor  return __my_tables[ "&"+oname+";" ] = res;
1e9a1b2001-02-21Per Hedbor  }
2be1982001-08-01Per Hedbor // // Update defenition if it has changed.
a0487b2001-07-31Per Hedbor // mixed error = // catch // { // get_my_sql()->query( "ALTER TABLE "+res+" ("+defenition+")" ); // }; // if( error ) // { // if( strlen( name ) ) // name = " for "+name; // report_notice( "Failed to update table defenition"+name+": "+ // describe_error( error ) ); // }
3a7e562001-08-23Per Hedbor  if( flag ) { __my_tables[ "&"+oname+";" ] = res; return ddc; }
2be1982001-08-01Per Hedbor  return __my_tables[ "&"+oname+";" ] = res;
a730c22001-01-19Per Hedbor }
e082212001-08-28Per Hedbor static string my_db = "local";
6533f22001-08-23Martin Nilsson 
2be1982001-08-01Per Hedbor static void set_my_db( string to )
1e9a1b2001-02-21Per Hedbor //! Select the database in which tables will be created with //! get_my_table, and also the one that will be returned by //! @[get_my_sql] { my_db = to; }
2466122001-08-01Per Hedbor Sql.Sql get_my_sql( int|void read_only )
1e9a1b2001-02-21Per Hedbor //! Return a SQL-object for the database set with @[set_my_db], //! defaulting to the 'shared' database. If read_only is specified, //! the database will be opened in read_only mode. //! //! See also @[DBManager.get]
a730c22001-01-19Per Hedbor {
2be1982001-08-01Per Hedbor  return DBManager.cached_get( my_db, _my_configuration, read_only );
a730c22001-01-19Per Hedbor }