835c6c2001-06-17Martin Nilsson // This file is part of Roxen WebServer. // Copyright © 1996 - 2001, Roxen IS.
4246ca2001-06-28Martin Stjernholm // $Id: module.pike,v 1.114 2001/06/28 19:14:12 mast 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  private string _module_identifier; private Configuration _my_configuration; 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 * solution is not really all that beatiful, but it works. :-) */ 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() { if (!_module_identifier) {
4246ca2001-06-28Martin Stjernholm #if 1 string cname = my_configuration()->name; string mname = my_configuration()->otomod[this_object()]; if (!cname || !mname) error ("Called too early; module identifier not known yet.\n"); _module_identifier = cname + "/" + mname; #else
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);
4246ca2001-06-28Martin Stjernholm #endif
b9ec252000-01-05Martin Stjernholm  } return _module_identifier; }
b6fb051999-11-02Per Hedbor string _sprintf() {
4246ca2001-06-28Martin Stjernholm  return "RoxenModule(" + 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, }); }
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 {
5839c31999-01-22Marcus Comstedt  if(_my_configuration) return _my_configuration;
7fb7f51999-11-29Per Hedbor  Configuration conf;
b1fca01996-11-12Per Hedbor  foreach(roxen->configurations, conf)
a59d252000-07-04Per Hedbor  if(conf->otomod[this_object()])
33266d1999-05-24Per Hedbor  return _my_configuration = conf;
b1fca01996-11-12Per Hedbor  return 0; }
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( ) //! Returns the associated @ref{ModuleInfo} object {
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++) if (sizeof (urls[i]/"*") == 2)
162bd52000-02-20Martin Stjernholm  urls[i] = replace(urls[i], "*", hostname); 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 
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()))), functionp); 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 = (this_object()->ModuleTagSet || RXML.TagSet) (module_identifier(), tags); } 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 
1e9a1b2001-02-21Per Hedbor string get_my_table( string|array(string) name, void|array(string)|string defenition ) //! @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. //! //! In the first form, @[name] is the (postfix of) the name of the //! table, and @[types] is an array of defenitions, as an example: //! //! @example{ //! cache_table = get_my_table( "cache", ({ //! "id INT UNSIGNED AUTO_INCREMENT", //! "data BLOB", //! }) ); //! @} //! //! 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 "" //! //! If the table does not exist in the datbase, it is created. 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). //! //! @note This function may not be called from create { if( !defenition ) { defenition = name; name = ""; } else if(strlen(name)) name = "_"+name;
a730c22001-01-19Per Hedbor 
a665382001-01-29Per Hedbor  Sql.Sql sql = get_my_sql();
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  { mixed error = catch { get_my_sql()->query( "CREATE TABLE "+res+" ("+defenition+")" ); }; if( error ) { if( strlen( name ) ) name = " "+name; report_notice( "Failed to create table"+name+": "+ describe_error( error ) ); return 0; } return res; } // Update defenition if it has changed. For now, always update. // This might be a tad ineffective if this function is called // often, but mysql at least seems to ignore ALTER TABLE calls // when the defenition has not changed. 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 ) ); }
a730c22001-01-19Per Hedbor  return res; }
1e9a1b2001-02-21Per Hedbor string my_db = "shared"; void set_my_db( string to ) //! 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; } Sql.Sql get_my_sql( int|void read_only ) //! 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 {
1e9a1b2001-02-21Per Hedbor  return DBManager.get( my_db, _my_configuration, read_only );
a730c22001-01-19Per Hedbor }