1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * $Id: httplib.pmod,v 1.1 2008/03/31 13:39:57 exodusd Exp $
21 #include <attributes.h>
28 constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" });
29 constant months=(["Jan":0, "Feb":1, "Mar":2, "Apr":3, "May":4, "Jun":5,
30 "Jul":6, "Aug":7, "Sep":8, "Oct":9, "Nov":10, "Dec":11,
31 "jan":0, "feb":1, "mar":2, "apr":3, "may":4, "jun":5,
32 "jul":6, "aug":7, "sep":8, "oct":9, "nov":10, "dec":11,]);
33 constant montharr = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
34 "Sep", "Oct", "Nov", "Dec" });
38 * Identify a browser and return identified parameters
40 * client: MSIE|mozilla|opera
41 * os: Linux|macosx|windows 95|windows 98|windows nt|windows
42 * language: german|english|...
43 * xres: resolution x (width)
44 * yres: resolution y (height)
46 * @param array id - components of client request header
47 * @param mapping request_headers - the mapping of all request headers
48 * @return mapping (see function description)
50 mapping identify_browser(array id, mapping req_headers)
53 mapping client = ([ "language": "english", ]);
55 if ( !arrayp(id) || !mappingp(req_headers) )
58 if ( (i=search(id, "MSIE")) >= 0 ) {
59 client["client"] = "MSIE";
60 client["client-version"] = id[i+1];
63 string sclient, sversion;
64 sscanf(id[0],"%s/%s",sclient,sversion);
65 client["client"] = client;
66 client["client-version"] = version;
68 if ( search(id, "Linux") >= 0 )
69 client["os"] = "linux";
70 else if ( search(id, "Mac OS X") >= 0 )
71 client["os"] = "macosx";
72 else if ( search(id, "Windows NT 4.0") >= 0 )
73 client["os"] = "windows nt";
74 else if ( search(id, "Windows 95") >= 0 )
75 client["os"] = "windows 95";
76 else if ( search(id, "Windows 98") >= 0 )
77 client["os"] = "windows 98";
78 else if ( stringp(req_headers["user-agent"]) &&
79 search(req_headers["user-agent"], "Windows") >= 0 )
80 client["os"] = "windows";
82 client["os"] = "unknown";
85 int xres, yres, tmp1, tmp2;
88 foreach(id, string modifier) {
89 if ( sscanf(modifier, "%dx%d%*s", tmp1, tmp2) >= 2 ) {
94 client["xres"] = (string)xres;
95 client["yres"] = (string)yres;
96 if ( mappingp(req_headers) ) {
97 client->language = identify_language(req_headers);
102 string identify_language(mapping req_headers)
104 if ( stringp(req_headers["accept-language"]) ) {
106 de = search(req_headers["accept-language"], "de");
107 en = search(req_headers["accept-language"], "en");
108 zh = search(req_headers["accept-language"], "zh");
109 if ( de == -1 && en == -1 && zh >= 0 )
113 else if ( en == -1 || de < en )
121 mapping vars_to_utf8(mapping vars, string encoding)
123 foreach(indices(vars), string key)
124 if ( stringp(vars[key]) )
125 vars[key] = string_to_utf8(vars[key]);
130 * Returns if time t is later than time/length string a, or len
131 * differs from the length encoded in the string a.
132 * Taken from caudium webserver.
134 * @param string a - the timestamp (cached on client side?!)
135 * @param int t - a timestamp, modification date with length.
136 * @param void|int len - the current length of a file
137 * @return 1 or 0, true or false.
139 int is_modified(string a, int t, void|int len)
143 int day, year, month, hour, minute, second, length;
148 // Expects 't' as returned from time(), not UTC.
149 sscanf(lower_case(a), "%*s, %s; %s", a, extra);
150 if(extra && sscanf(extra, "length=%d", length) && len && length != len)
153 if(search(a, "-") != -1)
155 sscanf(a, "%d-%s-%d %d:%d:%d", day, m, year, hour, minute, second);
158 } else if(search(a, ",") == 3) {
159 sscanf(a, "%*s, %d %s %d %d:%d:%d", day, m, year, hour, minute, second);
160 if(year < 1900) year += 1900;
163 sscanf(a, "%*[^ ] %s %d %d:%d:%d %d", m, day, hour, minute, second, year);
166 sscanf(a, "%d %s %d %d:%d:%d", day, m, year, hour, minute, second);
168 if(year < 1900) year += 1900;
171 if(year < (t1["year"]+1900))
173 else if(year == (t1["year"]+1900))
174 if(month < (t1["mon"]))
176 else if(month == (t1["mon"]))
177 if(day < (t1["mday"]))
179 else if(day == (t1["mday"]))
180 if(hour < (t1["hour"]))
182 else if(hour == (t1["hour"]))
183 if(minute < (t1["min"]))
185 else if(minute == (t1["min"]))
186 if(second < (t1["sec"]))
191 string http_date(int t)
194 mapping l = gmtime( t );
196 mapping l = localtime(t);
197 t += l->timezone - 3600*l->isdst;
200 return(sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
201 days[l->wday], l->mday, montharr[l->mon], 1900+l->year,
202 l->hour, l->min, l->sec));
207 * Get a html page which redirects to the given URL.
209 * @param string url - the URL to redirect to.
210 * @return html code of redirection to url.
212 string redirect(string url, int|void refresh_time)
214 return "<html><head>"+redirect_meta(url, refresh_time)+"</head></html>";
217 string redirect_meta(string url, int|void refresh_time)
219 return "<meta http-equiv=\"refresh\" content=\""+
220 refresh_time+";URL="+url+"\"/>";
224 * This function takes request and parses the given data,which
225 * represents a MIME Message (the body of the request).
226 * Returned is a mapping with the parts of the message.
227 * HTTP create multipart messages with variable encoded as
229 * This function was taken out of caudium code.
231 * @param object req - the request object
232 * @param string data - the body of the message
233 * @return mapping of parsed variables.
235 mapping parse_multipart_form_data(object req, string data)
237 mapping variables = ([ ]);
239 MIME.Message messg = MIME.Message(data, req->request_headers);
241 foreach(messg->body_parts||({}), object part) {
242 if(part->disp_params->filename) {
243 // FIXME: this overwrites multiple parts and only keeps the last one
244 variables[part->disp_params->name]=part->getdata();
245 string fname=part->disp_params->filename;
246 if( part->headers["content-disposition"] ) {
247 array fntmp=part->headers["content-disposition"]/";";
248 if( sizeof(fntmp) >= 3 && search(fntmp[2],"=") != -1 ) {
249 fname=((fntmp[2]/"=")[1]);
250 fname=fname[1..(sizeof(fname)-2)];
254 variables[part->disp_params->name+".filename"]=fname;
256 if(variables[part->disp_params->name])
257 variables[part->disp_params->name] += "\0" + part->getdata();
259 variables[part->disp_params->name] = part->getdata();
266 * return a backtrace in html with <li>
268 * @param array|string bt - the pike backtrace
269 * @return html generated
271 string backtrace_html(array|object|string bt)
276 werror("wrong backtrace format:\n"+
277 sprintf("%O\n",backtrace()));
279 "Wrong backtrace format in backtrace_html().\n", backtrace() })) ;
282 if ( arrayp(bt) || objectp(bt) ) {
284 for ( int i = sizeof(bt)-1; i >= 0; i-- ) {
286 if ( stringp(line[0]) ) {
287 string oname = line[0];
289 if ( sscanf(oname, "/DB:#%d", oid) > 0 )
290 oname = _Server->get_module("filepath:tree")->
291 object_to_filename(find_object(oid));
292 errStr += sprintf("<li> %s, line %d</li>\n", oname, line[1]);
298 errStr += replace(bt, ({ "\r\n", "\n", "<", ">" }),
299 ({ "<BR/>", "<BR/>", "<", ">" }) );
303 string html_backtrace(array|object|string bt)
305 return backtrace_html(bt);
310 * return a page for displaying errors
312 * @param message - the error message
313 * @param back - what page to go back to (action back)
314 * @return the html code for the page
316 string error_page(string message, string|void back)
319 // need a webinterface version check here
320 object web = _Server->get_module("package:web");
321 string wi_version = "0.0.0";
322 if (objectp(web)) wi_version = web->get_version();
323 array wiv = wi_version / ".";
325 // if webinterface version >= 2.1, then use new method respecting user
326 // stylesheet setting (and may use internationalization in the future..)
327 if ( (sizeof(wiv) > 0 && (int)wiv[0]>2) || (sizeof(wiv) > 1 && (int)wiv[0]>=2 &&\
332 object user = geteuid();
333 if (!objectp(user)) user = this_user();
335 if (objectp(user)) css_style = user->query_attribute("USER_CSS_STYLE");
336 xsl = find_object("/stylesheets/errors.xsl");
337 xml = "<?xml version='1.0' encoding='utf-8'?>"+
339 if (objectp(css_style)) {
340 xml += "<properties><css_style><Object><path>"+
341 _Server->get_module("filepath:tree")->object_to_filename(css_style)+
342 "</path></Object></css_style></properties>";
344 xml += "<actions/>\n<message><![CDATA["+message+
345 "]]></message></Object>";
347 mapping myvars = ([]);
348 myvars["back"] = back;
349 //myvars["message"] = message;
351 // doesnt work yet, need help and more testing
352 // produces strange error "Start Tag expected..." but xsl.xml file looks fine \
355 werror("xml="+xml+"\r\n");
357 html = _Server->get_module("libxslt")->run(xml, xsl, myvars );
359 else { // do it the old-fashioned way...
362 xsl = find_object("/stylesheets/errors.xsl");
363 xml = "<?xml version='1.0' encoding='utf-8'?>"+
364 "<error><actions/>\n<message><![CDATA["+message+
365 "]]></message></error>";
366 html = _Server->get_module("libxslt")->run(xml, xsl, (["back": back,]) );
374 * Remove umlaute from a string str and return the changed string.
376 * @param string str - the string to convert.
377 * @return the source string with umlaute removed.
379 string no_uml(string str)
381 if ( !stringp(str) ) return "";
382 return "<![CDATA["+replace_uml(str)+"]]>";
385 string encode_url(string str)
387 return replace_uml(str);
390 string replace_uml(string str)
395 for ( int i = 0; i < sizeof(str); i++ ) {
458 if ( str[i] >= 128 ) {
459 result += sprintf("%%%x", str[i]);
468 return replace(result,
469 ({ "ä", "ö", "ü", "Ä", "Ö", "Ü", "ß", "<", ">", "?", " ", "#", ":", "\303\244", "\303\266", "\303\274", "\303\234", "\303\204", "\303\226", "\303\237", "\"" }),
470 ({ "%e4", "%f6", "%fc", "%c4", "%d6", "%dc", "%df",
471 "%3c", "%3e", "%3f", "%20", "%23", "%3a", "%C3%A4", "%C3%B6", "%C3%BC", "%C3%9C", "%C3%84", "%C3%96", "%C3%9F", "%2d" }));
476 * Replace the Umlaute and other unpleasent chars.
478 * @param string str - the string to replace.
479 * @return replaced string
481 string uml_to_html(string str)
483 if ( !stringp(str) ) return "";
485 replace(str, ({ "ä","ö","ü", "Ä", "Ö", "Ü", "<", ">", "&" }),
486 ({ "ä", "ö", "ü", "Ä", "Ö",
487 "Ü", "<",">", "&" }));
490 string url_to_string(string str)
492 return Protocols.HTTP.Server.http_decode_string(str);
494 int len = strlen(str);
497 for ( int i = 0; i < len; i++ ) {
498 if ( str[i] == '%' && i < len - 2 ) {
500 string v = str[i..i];
501 if ( sscanf(str[i+1..i+2], "%x", val) )
504 // jump over %xx value
513 ({ "%e4", "%f6", "%fc", "%c4", "%d6", "%dc", "%df",
514 "%3c", "%3e", "%3f", "%20", "%5B", "%5D" }),
515 ({ "ä", "ö", "ü", "Ä", "Ö", "Ü", "ß", "<", ">", "?", " ",
522 * Detect encoding in a string (utf-8 or iso-8859-1)
524 * @param string text - the text to check (should be html)
525 * @return the detected encoding - utf8 if nothing else found.
527 string detect_encoding(string text)
529 string encoding = "iso-8859-1";
531 if ( !stringp(text) )
534 int n = search(text, "charset=");
536 n = search(text, "CHARSET=");
541 sscanf(text[n+8..], "%s\"%*s", enc);
542 if ( lower_case(enc) == "utf-8" )
545 encoding = lower_case(enc);
552 * Get the html representation of text/plain content.
555 * @return the html representation
557 string text_to_html(string text)
559 return replace(text, ({ "\n", " " }), ({ "<br/>", " " }));
564 * Return a result page in html with the message in the content
567 * @param string message - the message to print out.
568 * @param string next - the next link in the header area.
569 * @param string|void title - the title for the page.
570 * @param string|void head - optional header arguments.
571 * @return html result page.
574 result_page(string message, string next, string|void title, string|void head)
577 // need a webinterface version check here
578 object web = _Server->get_module("package:web");
580 string wi_version = "0.0.0";
581 if (objectp(web)) wi_version = web->get_version();
582 array wiv = wi_version / ".";
583 // if webinterface version >= 2.1, then use new method respecting user
584 // stylesheet setting (and may use internationalization in the future..)
585 if ( (sizeof(wiv) > 0 && (int)wiv[0]>2) || (sizeof(wiv) > 1 && (int)wiv[0]>=2 && (int)wiv[1]>=1) ) {
589 if ( !stringp(title) ) title = "Result";
590 if ( !stringp(head) ) head = "";
592 object user = geteuid();
593 if (!objectp(user)) user = this_user();
595 if (objectp(user)) css_style = user->query_attribute("USER_CSS_STYLE");
596 xsl = find_object("/stylesheets/result.xsl");
597 xml = "<?xml version='1.0' encoding='utf-8'?>"+
599 if (objectp(css_style)) {
600 xml += "<properties><css_style><Object><path>"+
601 _Server->get_module("filepath:tree")->object_to_filename(css_style)+
602 "</path></Object></css_style></properties>";
604 xml += "<head><![CDATA["+head+"]]></head>"+
605 "<title>"+title+"</title><actions/>\n<message><![CDATA["+message+
606 "]]></message></Object>";
607 mapping myvars = ([]);
608 myvars["back"] = next;
609 myvars["title"] = title;
610 myvars["head"] = head;
611 // myvars["message"] = message;
613 // doesnt work yet, need help and more testing
614 // produces strange error "Start Tag expected..." but xsl.xml file looks fine ?!
615 werror("xml="+xml+"\r\n");
617 html = _Server->get_module("libxslt")->run(xml, xsl, myvars );
619 else { // do it the old-fashioned way...
623 if ( !stringp(title) ) title = "Result";
624 if ( !stringp(head) ) head = "";
626 xsl = find_object("/stylesheets/result.xsl");
627 xml = "<?xml version='1.0' encoding='utf-8'?>"+
629 "<head><![CDATA["+head+"]]></head>"+
630 "<title>"+title+"</title><actions/>\n<message><![CDATA["+message+
631 "]]></message></result>";
632 html = _Server->get_module("libxslt")->run(xml, xsl, (["back": next,]) );
638 * Return a mapping in appropriate format for Protocols.HTTP to handle.
640 * @param int code - the http return code.
641 * @return mapping suitable for Protocols.HTTP
643 mapping low_answer(int code, string response, void|mapping extra)
645 mapping answer = ([ "error": code, "rettext": response, ]);
646 if ( mappingp(extra) )
647 answer->extra_heads = extra;
652 * Get a mapping of client informations from the submitted vars mapping.
654 * @param mapping vars - the submitted variables.
655 * @return mapping of client information
657 mapping get_client_map(mapping vars)
659 if ( !mappingp(vars->__internal) )
663 vars["__internal"]["client"],
664 vars["__internal"]["request_headers"]);
665 object user = this_user();
666 if ( !objectp(user) )
668 string l = user->query_attribute(USER_LANGUAGE);
670 client_map->language = l;
676 set_auth_cookie(string user, string pass)
679 set_cookie("steam_auth", MIME.encode_base64(user+":"+pass));
680 c["Cache-Control"] = "no-cache=\"set-cookie\"";
685 * Set a cookie, returns just the header information in a mapping which
686 * should be send to http as extra-heads.
688 * @param string name - the name of the cookie.
689 * @param string value - value of the cookie.
690 * @return mapping of cookie representation.
693 set_cookie(string name, string value, int|void expires, string|void path)
695 return ([ "Set-Cookie":
696 name+"="+Protocols.HTTP.http_encode_cookie(value), ]);
702 * Basic function to get html from a given object. The function
703 * sets some parameters like ports and the hostname of this server.
704 * If the variables source is set to true, then only the xml code
705 * will be returned wrapped in a html page (inside <pre>).
707 * @param object obj - the object to get xml/html code for
708 * @param object xsl - the xsl stylesheet to be used for transformation
709 * @return html (?) code as a string
711 string run_xml(object|string obj, object xsl, mapping vars)
715 if ( !vars->client ) {
716 mapping client_map = get_client_map(vars);
720 vars["host"] = _Server->query_config(CFG_WEBSERVER);
721 vars["port_ftp"] = (string)_Server->query_config("ftp_port");
722 vars["port_http"] = (string)_Server->query_config("https_port");
723 vars["port_irc"] = (string)_Server->query_config("irc_port");
724 if ( objectp(this_user()) )
725 vars["user_id"] = (string)(this_user()->get_object_id());
727 vars["user_id"] = (string)(_GUEST->get_object_id());
729 if ( stringp(obj) ) {
733 xml = _Server->get_module("Converter:XML")->get_xml(obj, xsl,
734 vars, (int)vars["active"]);
736 if ( vars->source == "true" )
739 html = _Server->get_module("libxslt")->run(xml, xsl, vars);
746 object parse_rxml_tag(object node, mapping variables, mapping tags)
748 object father = node->father;
750 // is it registered rxml entity ?
751 object tag = tags[node->name];
752 if ( objectp(tag) ) {
754 variables->args = ([ ]);
756 // dont trust data in node->data, it does not contain tags
757 variables->args->body = node->get_sub_xml();
758 // convert the attributes into vars->args
759 foreach(indices(node->attributes), string idx) {
760 variables->args[idx] = node->attributes[idx];
764 if ( tag->get_object_class() & CLASS_DOCLPC )
765 result = tag->call_script(variables);
767 result = tag->execute(variables);
769 // replace this node with text node with result !
770 node->replace_node(result);
773 if ( stringp(err) || intp(err) )
774 node->replace_node("Some error occured: " + err);
777 "An error occured on tag " + node->name + "<br/>"+
778 err[0]+"<br/><!---"+html_backtrace(err[1])+"--->");
787 * Parse rxml tags inside html files. The html code needs to be
788 * wellformed (xhtml).
790 * @param string result - the html code to parse
791 * @param mapping variables - a mapping of variables
792 * @param mapping tags - tags to replace inside the html code.
793 * @return resulting html code with rxml tags replaced.
795 string rxml(string result, mapping variables, mapping tags)
800 object parser = ((program)"/base/xml_parser")();
801 object node = parser->parse_data(result);
802 root = node->get_parent();
804 // now traverse tree to bottom and from there replace rxml tags
805 array leafs = node->get_leafs();
807 for ( int i = 0; i < sizeof(leafs); i++ ) {
808 object leaf = leafs[i];
809 object father = parse_rxml_tag(leaf, variables, tags);
810 // add the father to list for BFS
811 if ( objectp(father) && search(leafs, father) == -1 )
812 leafs += ({ father });
815 return root->get_last_child()->get_xml();
819 object stylesheet_from_map(mixed stylesheet)
821 if ( stringp(stylesheet) )
822 return _FILEPATH->path_to_object(stylesheet);
823 else if ( objectp(stylesheet) )
831 object find_user_stylesheet(object user, object obj, string type)
834 mixed xslMap = obj->query_attribute("xsl:"+type);
836 if ( objectp(xslMap) )
839 if ( !mappingp(xslMap) )
841 object aGroup = user->get_active_group();
843 // select the apropriate stylesheet depending on the group
844 if ( !objectp(xsl) ) {
845 if ( objectp(xslMap[aGroup]) )
846 xsl = stylesheet_from_map(xslMap[aGroup]);
850 array groups = user->get_groups();
851 for ( int i = 0; i < sizeof(groups); i++ ) {
852 if (objectp(groups[i]) && groups[i]->status()>=0)
853 groups += groups[i]->get_groups();
856 foreach( groups, grp ) {
857 if ( objectp(xslMap[grp]) )
858 xsl = stylesheet_from_map(xslMap[grp]);
868 * Get the appropriate stylesheet for a user to display obj.
870 * @param object user - the active user
871 * @param object obj - the object to show
872 * @param mapping vars - variables.
873 * @return the appropriate stylesheet to be used.
875 object get_xsl_stylesheet(object user, object obj, mapping vars)
879 xsl = find_user_stylesheet(user, obj, vars->type);
880 if ( stringp(vars["style"]) )
881 xsl = _FILEPATH->path_to_object(vars["style"]);
882 else if ( !objectp(xsl) ) {
883 // if no stylesheet is set for the given type explicitely, we take
884 // content and search in the content directory (styles/style)
885 xsl = find_user_stylesheet(user, obj, "content");
886 if ( objectp(xsl) ) {
887 object xslcont = xsl->get_environment();
888 if ( !objectp(xslcont) )
889 xslcont = find_object("/stylesheets");
890 // MESSAGE("XSL Container is: %s\n", xslcont->get_identifier());
891 xsl = xslcont->get_object_byname(vars->type);
894 if ( !objectp(xsl) ) {
895 if ( vars->type == "PDA:content" )
896 vars->type = "zaurus";
897 xsl = _FILEPATH->path_to_object("/stylesheets/"+vars->type+".xsl");
905 * Get the appropriate stylesheet for an object (usually xml document)
906 * checks for xsl:document
908 * @param object obj - the object to show
909 * @return the appropriate stylesheet to be used using xsl:document or xsl:public
911 object get_stylesheet(object obj)
914 xsl = find_user_stylesheet(this_user(), obj, "document");
915 if ( !objectp(xsl) ) {
916 if ( obj->query_attribute("xsl:use_public") )
917 return obj->query_attribute("xsl:public");
919 if ( obj->query_attribute(DOC_MIME_TYPE) == "text/xml" ) {
920 string content = obj->get_content();
921 if ( stringp(content) && content != "" ) {
923 object node = xmlDom.parse(content);
924 mapping pi = node->get_pi();
925 if ( pi["xml-stylesheet"] ) {
928 if ( !arrayp(pi["xml-stylesheet"]) )
929 stylesheets = ({ pi["xml-stylesheet"] });
931 stylesheets = pi["xml-stylesheet"];
932 foreach(stylesheets, string stylesheet ) {
934 object fp = _Server->get_module("filepath:tree");
935 object env = obj->get_environment();
936 string type = "text/xsl";
937 if ( search(stylesheet, "type=") >= 0 )
938 sscanf(stylesheet, "%*shref=\"%s\"%*s", type);
939 if ( type == "text/xsl" &&
940 sscanf(stylesheet, "%*shref=\"%s\"%*s", styleref) )
942 if ( sscanf(styleref, "%s.css", styleref) )
945 if ( styleref[0] != '/' )
946 return fp->resolve_path(env,styleref);
948 return fp->path_to_object(styleref);
963 string execute(mapping vars) {
964 return "Hello World to " + vars->args->name;
970 string execute(mapping vars) {
971 return "<BODY>"+vars->args->body+"</BODY>";
978 "<html><body>Welcome! <h2><test name='test'/></h2></body></html>";
980 result = rxml(result, ([ ]), ([ "test": testTag(), ]));
982 "<html><body>Welcome! <h2>Hello World to test</h2></body></html>" )
983 error("rxml test failed - wrong result " + result);
985 result = "<a><b><c apply='1'>"+
987 "<d name='y'/></b></a>";
989 result = rxml(result, ([ ]), ([ "d": testTag(), "c":tagTag(),]));
991 "<a><b><BODY>Hello World to x</BODY>Hello World to y</b></a>" )
992 error("nested rxml test failed !");
994 result = "<a><?xml version='1.0'?><x>test</x></a>";
995 result = rxml(result, ([ ]), (["a": tagTag(), ]));
996 if ( result != "<BODY><?xml version='1.0'?>\n\n<x>test</x></BODY>" )
997 error("Failed test with rxml tag with xml in body.\n");
1000 string get_tag_name(object tag)
1002 string name = tag->get_identifier();
1003 sscanf(name, "%s.pike", name);
1007 function get_tag_function(object tag)
1010 if ( !objectp(tag) )
1012 catch(instance = tag->provide_instance());
1013 if ( !objectp(instance) )
1016 return instance->execute;
1019 function find_tag(string name)
1021 object tags = OBJ("/tags");
1022 if ( !objectp(tags) )
1024 object tag = tags->get_object_byname(name+".pike");
1025 if ( !objectp(tag) )
1027 return get_tag_function(tag);
1031 mapping find_tags(object obj)
1033 if ( !objectp(obj) )
1035 if ( obj->get_object_class() & CLASS_CONTAINER ) {
1036 mapping result = ([ ]);
1037 foreach(obj->get_inventory_by_class(CLASS_DOCLPC), object tag) {
1038 function f = get_tag_function(tag);
1039 string tagname = get_tag_name(tag);
1040 if ( !functionp(f) )
1041 FATAL("Warning - no tag function for tag: %s", tagname);
1043 result[tagname] = f;
1047 else if ( obj->get_object_class() & CLASS_DOCXSL) {
1048 object env = obj->get_environment();
1050 return find_tags(env->get_object_byname("tags"));
1055 // hack to force pike to load JSON into the chroot
1056 #if constant(Standards.JSON)