httplib._pmod
Go to the documentation of this file.
1 /* Copyright (C) 2000-2006 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
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.
7  *
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.
12  *
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
16  *
17  * $Id: httplib.pmod,v 1.1 2008/03/31 13:39:57 exodusd Exp $
18  */
19 #include <config.h>
20 #include <macros.h>
21 #include <attributes.h>
22 #include <classes.h>
23 #include <database.h>
24 
25 
26 
27 
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" });
35 
36 
37 /**
38  * Identify a browser and return identified parameters
39  * as a mapping:
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)
45  *
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)
49  */
50 mapping identify_browser(array id, mapping req_headers)
51 {
52  int i;
53  mapping client = ([ "language": "english", ]);
54 
55  if ( !arrayp(id) || !mappingp(req_headers) )
56  return client;
57 
58  if ( (i=search(id, "MSIE")) >= 0 ) {
59  client["client"] = "MSIE";
60  client["client-version"] = id[i+1];
61  }
62  else {
63  string sclient, sversion;
64  sscanf(id[0],"%s/%s",sclient,sversion);
65  client["client"] = client;
66  client["client-version"] = version;
67  }
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";
81  else
82  client["os"] = "unknown";
83 
84 
85  int xres, yres, tmp1, tmp2;
86  xres = 1024;
87  yres = 768;
88  foreach(id, string modifier) {
89  if ( sscanf(modifier, "%dx%d%*s", tmp1, tmp2) >= 2 ) {
90  xres = tmp1;
91  yres = tmp2;
92  }
93  }
94  client["xres"] = (string)xres;
95  client["yres"] = (string)yres;
96  if ( mappingp(req_headers) ) {
97  client->language = identify_language(req_headers);
98  }
99  return client;
100 }
101 
102 string identify_language(mapping req_headers)
103 {
104  if ( stringp(req_headers["accept-language"]) ) {
105  int de, en, zh;
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 )
110  return "chinese";
111  else if ( de == -1 )
112  return "english";
113  else if ( en == -1 || de < en )
114  return "german";
115  else
116  return "english";
117  }
118  return "english";
119 }
120 
121 mapping vars_to_utf8(mapping vars, string encoding)
122 {
123  foreach(indices(vars), string key)
124  if ( stringp(vars[key]) )
125  vars[key] = string_to_utf8(vars[key]);
126  return vars;
127 }
128 
129 /**
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.
133  *
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.
138  */
139 int is_modified(string a, int t, void|int len)
140 {
141  mapping t1;
142 
143  int day, year, month, hour, minute, second, length;
144  string m, extra;
145  if(!a)
146  return 1;
147  t1=gmtime(t);
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)
151  return 1;
152 
153  if(search(a, "-") != -1)
154  {
155  sscanf(a, "%d-%s-%d %d:%d:%d", day, m, year, hour, minute, second);
156  year += 1900;
157  month=months[m];
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;
161  month=months[m];
162  } else if(!(int)a) {
163  sscanf(a, "%*[^ ] %s %d %d:%d:%d %d", m, day, hour, minute, second, year);
164  month=months[m];
165  } else {
166  sscanf(a, "%d %s %d %d:%d:%d", day, m, year, hour, minute, second);
167  month=months[m];
168  if(year < 1900) year += 1900;
169  }
170 
171  if(year < (t1["year"]+1900))
172  return 1;
173  else if(year == (t1["year"]+1900))
174  if(month < (t1["mon"]))
175  return 1;
176  else if(month == (t1["mon"]))
177  if(day < (t1["mday"]))
178  return 1;
179  else if(day == (t1["mday"]))
180  if(hour < (t1["hour"]))
181  return 1;
182  else if(hour == (t1["hour"]))
183  if(minute < (t1["min"]))
184  return 1;
185  else if(minute == (t1["min"]))
186  if(second < (t1["sec"]))
187  return 1;
188  return 0;
189 }
190 
191 string http_date(int t)
192 {
193 #if constant(gmtime)
194  mapping l = gmtime( t );
195 #else
196  mapping l = localtime(t);
197  t += l->timezone - 3600*l->isdst;
198  l = localtime(t);
199 #endif
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));
203 
204 }
205 
206 /**
207  * Get a html page which redirects to the given URL.
208  *
209  * @param string url - the URL to redirect to.
210  * @return html code of redirection to url.
211  */
212 string redirect(string url, int|void refresh_time)
213 {
214  return "<html><head>"+redirect_meta(url, refresh_time)+"</head></html>";
215 }
216 
217 string redirect_meta(string url, int|void refresh_time)
218 {
219  return "<meta http-equiv=\"refresh\" content=\""+
220  refresh_time+";URL="+url+"\"/>";
221 }
222 
223 /**
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
228  * parts of the body.
229  * This function was taken out of caudium code.
230  *
231  * @param object req - the request object
232  * @param string data - the body of the message
233  * @return mapping of parsed variables.
234  */
235 mapping parse_multipart_form_data(object req, string data)
236 {
237  mapping variables = ([ ]);
238 
239  MIME.Message messg = MIME.Message(data, req->request_headers);
240 
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)];
251  }
252 
253  }
254  variables[part->disp_params->name+".filename"]=fname;
255  } else {
256  if(variables[part->disp_params->name])
257  variables[part->disp_params->name] += "\0" + part->getdata();
258  else
259  variables[part->disp_params->name] = part->getdata();
260  }
261  }
262  return variables;
263 }
264 
265 /**
266  * return a backtrace in html with <li>
267  *
268  * @param array|string bt - the pike backtrace
269  * @return html generated
270  */
271 string backtrace_html(array|object|string bt)
272 {
273  string errStr = "";
274 
275  if ( intp(bt) ) {
276  werror("wrong backtrace format:\n"+
277  sprintf("%O\n",backtrace()));
278  throw( ({
279  "Wrong backtrace format in backtrace_html().\n", backtrace() })) ;
280  }
281 
282  if ( arrayp(bt) || objectp(bt) ) {
283  errStr += "<ul>\n";
284  for ( int i = sizeof(bt)-1; i >= 0; i-- ) {
285  array line = bt[i];
286  if ( stringp(line[0]) ) {
287  string oname = line[0];
288  int oid;
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]);
293  }
294  }
295  errStr += "</ul>\n";
296  }
297  else
298  errStr += replace(bt, ({ "\r\n", "\n", "<", ">" }),
299  ({ "<BR/>", "<BR/>", "&lt;", "&gt;" }) );
300  return errStr;
301 }
302 
303 string html_backtrace(array|object|string bt)
304 {
305  return backtrace_html(bt);
306 }
307 
308 
309 /**
310  * return a page for displaying errors
311  *
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
315  */
316 string error_page(string message, string|void back)
317 {
318  string html = "";
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 / ".";
324 
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 &&\
328  (int)wiv[1]>=1) ) {
329  object xsl;
330  string xml;
331 
332  object user = geteuid();
333  if (!objectp(user)) user = this_user();
334  object css_style;
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'?>"+
338  "<Object>";
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>";
343  }
344  xml += "<actions/>\n<message><![CDATA["+message+
345  "]]></message></Object>";
346 
347  mapping myvars = ([]);
348  myvars["back"] = back;
349  //myvars["message"] = message;
350  /*
351  // doesnt work yet, need help and more testing
352  // produces strange error "Start Tag expected..." but xsl.xml file looks fine \
353 ?!
354 );
355  werror("xml="+xml+"\r\n");
356  */
357  html = _Server->get_module("libxslt")->run(xml, xsl, myvars );
358  }
359  else { // do it the old-fashioned way...
360  object xsl;
361  string xml;
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,]) );
367  }
368  return html;
369 
370 }
371 
372 
373 /**
374  * Remove umlaute from a string str and return the changed string.
375  *
376  * @param string str - the string to convert.
377  * @return the source string with umlaute removed.
378  */
379 string no_uml(string str)
380 {
381  if ( !stringp(str) ) return "";
382  return "<![CDATA["+replace_uml(str)+"]]>";
383 }
384 
385 string encode_url(string str)
386 {
387  return replace_uml(str);
388 }
389 
390 string replace_uml(string str)
391 {
392  if ( !stringp(str) )
393  return "";
394  string result = "";
395  for ( int i = 0; i < sizeof(str); i++ ) {
396  switch(str[i]) {
397  case '"':
398  result += "%22";
399  break;
400  case '<':
401  result += "%3c";
402  break;
403  case '>':
404  result += "%3e";
405  break;
406  case '?':
407  result += "%3f";
408  break;
409  case ' ':
410  result += "%20";
411  break;
412  case '#':
413  result += "%23";
414  break;
415  case '&':
416  result += "%26";
417  break;
418  case '+':
419  result += "%2b";
420  break;
421  case ',':
422  result += "%2c";
423  break;
424  case ';':
425  result += "%3b";
426  break;
427  case '@':
428  result += "%40";
429  break;
430  case '$':
431  result += "%24";
432  break;
433  case '%':
434  result += "%25";
435  break;
436  case '{':
437  result += "%7b";
438  break;
439  case '}':
440  result += "%7d";
441  break;
442  case '|':
443  result += "%7c";
444  break;
445  case '\\':
446  result += "%5c";
447  break;
448  case '[':
449  result += "%5b";
450  break;
451  case ']':
452  result += "%5d";
453  break;
454  case '`':
455  result += "%60";
456  break;
457  default:
458  if ( str[i] >= 128 ) {
459  result += sprintf("%%%x", str[i]);
460  }
461  else
462  result += str[i..i];
463  }
464  }
465  return result;
466 
467 #if 0
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" }));
472 #endif
473 }
474 
475 /**
476  * Replace the Umlaute and other unpleasent chars.
477  *
478  * @param string str - the string to replace.
479  * @return replaced string
480  */
481 string uml_to_html(string str)
482 {
483  if ( !stringp(str) ) return "";
484  return
485  replace(str, ({ "ä","ö","ü", "Ä", "Ö", "Ü", "<", ">", "&" }),
486  ({ "&auml;", "&ouml;", "&uuml;", "&Auml;", "&Ouml;",
487  "&Uuml;", "&lt;","&gt;", "&amp;" }));
488 }
489 
490 string url_to_string(string str)
491 {
492  return Protocols.HTTP.Server.http_decode_string(str);
493 #if 0
494  int len = strlen(str);
495  string res = "";
496 
497  for ( int i = 0; i < len; i++ ) {
498  if ( str[i] == '%' && i < len - 2 ) {
499  int val;
500  string v = str[i..i];
501  if ( sscanf(str[i+1..i+2], "%x", val) )
502  v[0] = val;
503  res += v;
504  // jump over %xx value
505  i += 2;
506  }
507  else {
508  res += str[i..i];
509  }
510  }
511  return res;
512  return replace(str,
513  ({ "%e4", "%f6", "%fc", "%c4", "%d6", "%dc", "%df",
514  "%3c", "%3e", "%3f", "%20", "%5B", "%5D" }),
515  ({ "ä", "ö", "ü", "Ä", "Ö", "Ü", "ß", "<", ">", "?", " ",
516  "[", "]" }));
517 #endif
518 }
519 
520 
521 /**
522  * Detect encoding in a string (utf-8 or iso-8859-1)
523  *
524  * @param string text - the text to check (should be html)
525  * @return the detected encoding - utf8 if nothing else found.
526  */
527 string detect_encoding(string text)
528 {
529  string encoding = "iso-8859-1";
530 
531  if ( !stringp(text) )
532  return encoding;
533 
534  int n = search(text, "charset=");
535  if ( n == -1 )
536  n = search(text, "CHARSET=");
537 
538  if ( n > 0 ) {
539  string enc;
540 
541  sscanf(text[n+8..], "%s\"%*s", enc);
542  if ( lower_case(enc) == "utf-8" )
543  encoding = "utf-8";
544  else
545  encoding = lower_case(enc);
546  }
547  return encoding;
548 }
549 
550 
551 /**
552  * Get the html representation of text/plain content.
553  *
554  * @param string text
555  * @return the html representation
556  */
557 string text_to_html(string text)
558 {
559  return replace(text, ({ "\n", " " }), ({ "<br/>", "&nbsp;" }));
560 }
561 
562 
563 /**
564  * Return a result page in html with the message in the content
565  * area.
566  *
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.
572  */
573 string
574 result_page(string message, string next, string|void title, string|void head)
575 {
576  string html = "";
577  // need a webinterface version check here
578  object web = _Server->get_module("package:web");
579 
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) ) {
586  object xsl;
587  string xml;
588 
589  if ( !stringp(title) ) title = "Result";
590  if ( !stringp(head) ) head = "";
591 
592  object user = geteuid();
593  if (!objectp(user)) user = this_user();
594  object css_style;
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'?>"+
598  "<Object>";
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>";
603  }
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;
612  /*
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");
616  */
617  html = _Server->get_module("libxslt")->run(xml, xsl, myvars );
618  }
619  else { // do it the old-fashioned way...
620  object xsl;
621  string xml;
622 
623  if ( !stringp(title) ) title = "Result";
624  if ( !stringp(head) ) head = "";
625 
626  xsl = find_object("/stylesheets/result.xsl");
627  xml = "<?xml version='1.0' encoding='utf-8'?>"+
628  "<result>"+
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,]) );
633  }
634  return html;
635 }
636 
637 /**
638  * Return a mapping in appropriate format for Protocols.HTTP to handle.
639  *
640  * @param int code - the http return code.
641  * @return mapping suitable for Protocols.HTTP
642  */
643 mapping low_answer(int code, string response, void|mapping extra)
644 {
645  mapping answer = ([ "error": code, "rettext": response, ]);
646  if ( mappingp(extra) )
647  answer->extra_heads = extra;
648  return answer;
649 }
650 
651 /**
652  * Get a mapping of client informations from the submitted vars mapping.
653  *
654  * @param mapping vars - the submitted variables.
655  * @return mapping of client information
656  */
657 mapping get_client_map(mapping vars)
658 {
659  if ( !mappingp(vars->__internal) )
660  return ([ ]);
661  mapping client_map =
662  identify_browser(
663  vars["__internal"]["client"],
664  vars["__internal"]["request_headers"]);
665  object user = this_user();
666  if ( !objectp(user) )
667  return client_map;
668  string l = user->query_attribute(USER_LANGUAGE);
669  if ( stringp(l) )
670  client_map->language = l;
671  return client_map;
672 }
673 
674 
675 mapping
676 set_auth_cookie(string user, string pass)
677 {
678  mapping c =
679  set_cookie("steam_auth", MIME.encode_base64(user+":"+pass));
680  c["Cache-Control"] = "no-cache=\"set-cookie\"";
681  return c;
682 }
683 
684 /**
685  * Set a cookie, returns just the header information in a mapping which
686  * should be send to http as extra-heads.
687  *
688  * @param string name - the name of the cookie.
689  * @param string value - value of the cookie.
690  * @return mapping of cookie representation.
691  */
692 mapping
693 set_cookie(string name, string value, int|void expires, string|void path)
694 {
695  return ([ "Set-Cookie":
696  name+"="+Protocols.HTTP.http_encode_cookie(value), ]);
697 }
698 
699 
700 
701 /**
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>).
706  *
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
710  */
711 string run_xml(object|string obj, object xsl, mapping vars)
712 {
713  string html, xml;
714 
715  if ( !vars->client ) {
716  mapping client_map = get_client_map(vars);
717  vars |= client_map;
718  }
719 
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());
726  else
727  vars["user_id"] = (string)(_GUEST->get_object_id());
728 
729  if ( stringp(obj) ) {
730  xml = obj;
731  }
732  else {
733  xml = _Server->get_module("Converter:XML")->get_xml(obj, xsl,
734  vars, (int)vars["active"]);
735  }
736  if ( vars->source == "true" )
737  return xml;
738 
739  html = _Server->get_module("libxslt")->run(xml, xsl, vars);
740 
741  return html;
742 }
743 
744 
745 protected:
746  object parse_rxml_tag(object node, mapping variables, mapping tags)
747 {
748  object father = node->father;
749 
750  // is it registered rxml entity ?
751  object tag = tags[node->name];
752  if ( objectp(tag) ) {
753  mixed err = catch {
754  variables->args = ([ ]);
755 
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];
761  }
762  string result;
763 
764  if ( tag->get_object_class() & CLASS_DOCLPC )
765  result = tag->call_script(variables);
766  else
767  result = tag->execute(variables);
768 
769  // replace this node with text node with result !
770  node->replace_node(result);
771  };
772  if ( err != 0 ) {
773  if ( stringp(err) || intp(err) )
774  node->replace_node("Some error occured: " + err);
775  else
776  node->replace_node(
777  "An error occured on tag " + node->name + "<br/>"+
778  err[0]+"<br/><!---"+html_backtrace(err[1])+"--->");
779  }
780  }
781  return father;
782 }
783 
784 public:
785 
786 /**
787  * Parse rxml tags inside html files. The html code needs to be
788  * wellformed (xhtml).
789  *
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.
794  */
795 string rxml(string result, mapping variables, mapping tags)
796 {
797  // parse result
798  object root;
799  float t = gauge {
800  object parser = ((program)"/base/xml_parser")();
801  object node = parser->parse_data(result);
802  root = node->get_parent();
803 
804  // now traverse tree to bottom and from there replace rxml tags
805  array leafs = node->get_leafs();
806 
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 });
813  }
814  };
815  return root->get_last_child()->get_xml();
816 }
817 
818 protected:
819  object stylesheet_from_map(mixed stylesheet)
820 {
821  if ( stringp(stylesheet) )
822  return _FILEPATH->path_to_object(stylesheet);
823  else if ( objectp(stylesheet) )
824  return stylesheet;
825  return 0;
826 }
827 
828 public:
829 
830 protected:
831  object find_user_stylesheet(object user, object obj, string type)
832 {
833  object xsl;
834  mixed xslMap = obj->query_attribute("xsl:"+type);
835 
836  if ( objectp(xslMap) )
837  return xslMap;
838 
839  if ( !mappingp(xslMap) )
840  xslMap = ([ ]);
841  object aGroup = user->get_active_group();
842 
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]);
847  else {
848  object grp;
849 
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();
854  }
855 
856  foreach( groups, grp ) {
857  if ( objectp(xslMap[grp]) )
858  xsl = stylesheet_from_map(xslMap[grp]);
859  }
860  }
861  }
862  return xsl;
863 }
864 
865 public:
866 
867 /**
868  * Get the appropriate stylesheet for a user to display obj.
869  *
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.
874  */
875 object get_xsl_stylesheet(object user, object obj, mapping vars)
876 {
877  object xsl;
878 
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);
892  }
893  }
894  if ( !objectp(xsl) ) {
895  if ( vars->type == "PDA:content" )
896  vars->type = "zaurus";
897  xsl = _FILEPATH->path_to_object("/stylesheets/"+vars->type+".xsl");
898  }
899 
900  return xsl;
901 }
902 
903 
904 /**
905  * Get the appropriate stylesheet for an object (usually xml document)
906  * checks for xsl:document
907  *
908  * @param object obj - the object to show
909  * @return the appropriate stylesheet to be used using xsl:document or xsl:public
910  */
911 object get_stylesheet(object obj)
912 {
913  object xsl;
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");
918  // try to parse ...
919  if ( obj->query_attribute(DOC_MIME_TYPE) == "text/xml" ) {
920  string content = obj->get_content();
921  if ( stringp(content) && content != "" ) {
922  catch {
923  object node = xmlDom.parse(content);
924  mapping pi = node->get_pi();
925  if ( pi["xml-stylesheet"] ) {
926  array stylesheets;
927 
928  if ( !arrayp(pi["xml-stylesheet"]) )
929  stylesheets = ({ pi["xml-stylesheet"] });
930  else
931  stylesheets = pi["xml-stylesheet"];
932  foreach(stylesheets, string stylesheet ) {
933  string styleref;
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) )
941  {
942  if ( sscanf(styleref, "%s.css", styleref) )
943  continue; // no css
944 
945  if ( styleref[0] != '/' )
946  return fp->resolve_path(env,styleref);
947  else
948  return fp->path_to_object(styleref);
949  }
950  }
951  }
952  };
953  }
954  }
955  }
956  return xsl;
957 }
958 
959 
960 
961 class testTag {
962 public:
963  string execute(mapping vars) {
964  return "Hello World to " + vars->args->name;
965  }
966 }
967 
968 class tagTag {
969 public:
970  string execute(mapping vars) {
971  return "<BODY>"+vars->args->body+"</BODY>";
972  }
973 }
974 
975 {
976  // first test rxml
977  string result =
978  "<html><body>Welcome! <h2><test name='test'/></h2></body></html>";
979 
980  result = rxml(result, ([ ]), ([ "test": testTag(), ]));
981  if ( result !=
982  "<html><body>Welcome! <h2>Hello World to test</h2></body></html>" )
983  error("rxml test failed - wrong result " + result);
984 
985  result = "<a><b><c apply='1'>"+
986  "<d name='x'/></c>"+
987  "<d name='y'/></b></a>";
988 
989  result = rxml(result, ([ ]), ([ "d": testTag(), "c":tagTag(),]));
990  if ( result !=
991  "<a><b><BODY>Hello World to x</BODY>Hello World to y</b></a>" )
992  error("nested rxml test failed !");
993 
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");
998 }
999 
1000 string get_tag_name(object tag)
1001 {
1002  string name = tag->get_identifier();
1003  sscanf(name, "%s.pike", name);
1004  return name;
1005 }
1006 
1007 function get_tag_function(object tag)
1008 {
1009  object instance;
1010  if ( !objectp(tag) )
1011  return 0;
1012  catch(instance = tag->provide_instance());
1013  if ( !objectp(instance) )
1014  return 0;
1015 
1016  return instance->execute;
1017 }
1018 
1019 function find_tag(string name)
1020 {
1021  object tags = OBJ("/tags");
1022  if ( !objectp(tags) )
1023  return 0;
1024  object tag = tags->get_object_byname(name+".pike");
1025  if ( !objectp(tag) )
1026  return 0;
1027  return get_tag_function(tag);
1028 }
1029 
1030 
1031 mapping find_tags(object obj)
1032 {
1033  if ( !objectp(obj) )
1034  return 0;
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);
1042  else
1043  result[tagname] = f;
1044  }
1045  return result;
1046  }
1047  else if ( obj->get_object_class() & CLASS_DOCXSL) {
1048  object env = obj->get_environment();
1049  if ( objectp(env) )
1050  return find_tags(env->get_object_byname("tags"));
1051  }
1052  return 0;
1053 }
1054 
1055 // hack to force pike to load JSON into the chroot
1056 #if constant(Standards.JSON)
1057 int json = 1;
1058 #endif
1059 
1060 
1061 };