f41b982009-05-07Martin Stjernholm // This is a roxen module. Copyright © 1996 - 2009, Roxen IS.
4817401999-08-13Martin Nilsson //
6d754f2001-06-18Martin Stjernholm #define _stat RXML_CONTEXT->misc[" _stat"] #define _error RXML_CONTEXT->misc[" _error"]
3e77c72002-01-30Martin Stjernholm //#define _extra_heads RXML_CONTEXT->misc[" _extra_heads"]
6d754f2001-06-18Martin Stjernholm #define _rettext RXML_CONTEXT->misc[" _rettext"] #define _ok RXML_CONTEXT->misc[" _ok"]
4817401999-08-13Martin Nilsson 
0917d32013-03-04Anders Johansson constant cvs_version = "$Id$";
0023a62000-08-22Martin Nilsson constant thread_safe = 1;
d76d262008-11-01Martin Stjernholm constant language = roxen.language;
4817401999-08-13Martin Nilsson  #include <module.h>
b156c62001-04-23Martin Nilsson #include <config.h> #include <request_trace.h>
4817401999-08-13Martin Nilsson inherit "module";
6743432000-03-19Martin Nilsson 
4817401999-08-13Martin Nilsson  // ---------------- Module registration stuff ----------------
b3281f2000-09-10Martin Nilsson constant module_type = MODULE_TAG | MODULE_PROVIDER;
46c69c2008-11-02Martin Stjernholm constant module_name = "Tags: RXML tags";
59ae152000-04-06Mattias Wingstedt constant module_doc = "This module provides the common RXML tags.";
0be6412000-01-18Martin Nilsson 
def33f2001-06-15Jonas Wallden  // Cached copy of conf->query("compat_level"). This setting is defined // to require a module reload to take effect so we only query it when
70beb32008-11-19Martin Stjernholm // the module instance is created.
5e09bd2008-10-30Martin Stjernholm float compat_level = (float) my_configuration()->query("compat_level");
def33f2001-06-15Jonas Wallden 
16509b1999-12-14Martin Nilsson void start()
659d4b1999-10-18Martin Nilsson {
ecab8b2000-01-23Martin Nilsson  add_api_function("query_modified", api_query_modified, ({ "string" }));
0be6412000-01-18Martin Nilsson  query_tag_set()->prepare_context=set_entities;
4817401999-08-13Martin Nilsson }
295f8d2002-08-13Martin Stjernholm int cache_static_in_2_5()
4e3c212002-06-27Martin Stjernholm {
295f8d2002-08-13Martin Stjernholm  return compat_level >= 2.5 && RXML.FLAG_IS_CACHE_STATIC;
4e3c212002-06-27Martin Stjernholm }
48ef1c2002-04-15Jonas Wallden multiset query_provides() { return (< "modified", "rxmltags" >);
b7e95b1999-12-14Martin Nilsson }
4490ae2011-04-21Martin Stjernholm private Regexp.PCRE.Plain rxml_var_splitter = #if constant(Regexp.PCRE.UTF8_SUPPORTED) #define LETTER "\\pL" Regexp.PCRE.StudiedWidestring #else #define LETTER "A-Za-z" Regexp.PCRE.Studied #endif
eff71d2014-10-13Per Cederqvist  ("(?s)^(.*?)"
4490ae2011-04-21Martin Stjernholm  // Must start with a letter or "_" and contain at least one dot. // Also be as picky as possible when accepting a negation sign. "(["LETTER"_]["LETTER"_0-9]*(?:\\.(?:["LETTER"_0-9]+|-[0-9]+)?)+)" "(.*)$"); private string fix_rxml_vars (string code, RXML.Context ctx) { string res = ""; while (array split = rxml_var_splitter->split (code)) { res += sprintf ("%s(index(%{%O,%}))", split[0], ctx->parse_user_var (split[1])); code = split[2]; } return res + code; }
9bdfd52017-02-23Martin Karlgren private object sexpr_funcs = class
4490ae2011-04-21Martin Stjernholm  { // A class for the special functions in sexpr_constants. This is // to give these function proper names, since those names can // occur in the compiler errors that are shown to users. mixed search (mixed a, mixed b) {
5482902012-06-27Jonas Wallden  return predef::search (a, b) + 1; // RXML uses base 1.
4490ae2011-04-21Martin Stjernholm  } int INT (void|mixed x) { return intp (x) || floatp (x) || stringp (x) ? (int) x : 0; } float FLOAT (void|mixed x) { return intp (x) || floatp (x) || stringp (x) ? (float) x : 0.0; } string STRING (void|mixed x) { return intp (x) || floatp (x) || stringp (x) ? (string) x : ""; } mixed var (string var) { return RXML_CONTEXT->user_get_var (var); } mixed index (string scope, string|int... rxml_var_ref) { return RXML_CONTEXT->get_var (rxml_var_ref, scope); }
8bedde2012-05-10Martin Stjernholm  array regexp_split (string regexp, string data) { Regexp.PCRE.Plain re; if (mixed err = catch (re = Regexp.PCRE.Widestring (regexp))) RXML.parse_error (describe_error (err)); return re->split2 (data) || Val.false; }
cc728b2015-04-23Henrik Grubbström (Grubba)  float exp(void|int x) { return predef::exp(intp(x) ? (float) x : x); }
1d7e872016-03-24Pontus Östlund 
5482902012-06-27Jonas Wallden  float log(void|mixed x) {
5e4ff22012-06-27Jonas Wallden  return predef::log(intp(x) ? (float) x : x);
5482902012-06-27Jonas Wallden  }
1d7e872016-03-24Pontus Östlund 
5482902012-06-27Jonas Wallden  int floor(void|mixed x) {
5e4ff22012-06-27Jonas Wallden  return (int) predef::floor(intp(x) ? (float) x : x);
5482902012-06-27Jonas Wallden  } int ceil(void|mixed x) {
5e4ff22012-06-27Jonas Wallden  return (int) predef::ceil(intp(x) ? (float) x : x);
5482902012-06-27Jonas Wallden  } int round(void|mixed x) {
5e4ff22012-06-27Jonas Wallden  return (int) predef::round(intp(x) ? (float) x : x);
5482902012-06-27Jonas Wallden  }
1d7e872016-03-24Pontus Östlund 
4490ae2011-04-21Martin Stjernholm  }();
0bc3592004-03-03Martin Stjernholm private mapping(string:mixed) sexpr_constants = ([
cc728b2015-04-23Henrik Grubbström (Grubba)  "this":0, "this_function":0,
0bc3592004-03-03Martin Stjernholm  "this_program":0,
1d181a2001-06-08Martin Nilsson 
904ddf2008-11-02Martin Stjernholm  // The (function) casts below is to avoid very bulky types that // causes the compiler to take almost a second longer to compile // this file.
57014d2008-09-09Martin Stjernholm  "`+": (function) `+, "`-": (function) `-, "`*": (function) `*, "`/": (function) `/, "`%": (function) `%, "`!": (function) `!, "`!=": (function) `!=, "`&": (function) `&, "`|": (function) `|, "`^": (function) `^, "`<": (function) `<, "`>": (function) `>, "`==": (function) `==, "`<=": (function) `<=, "`>=": (function) `>=,
1d181a2001-06-08Martin Nilsson 
cc728b2015-04-23Henrik Grubbström (Grubba)  "arrayp": arrayp, "callablep": callablep, "floatp": floatp, "functionp": functionp, "intp": intp, "mappingp": mappingp, "multisetp": multisetp, "objectp": objectp, "programp": programp, "stringp": stringp, "undefinedp": undefinedp, "zero_type": zero_type, "has_index": has_index, "has_prefix": has_prefix, "has_suffix": has_suffix, "has_value": has_value, "indices": indices, "values": values, "combine_path": combine_path_unix,
da280f2008-09-05Martin Stjernholm  "equal": equal,
0bc3592004-03-03Martin Stjernholm  "sizeof": sizeof,
cc728b2015-04-23Henrik Grubbström (Grubba)  "strlen": strlen,
5a18c42005-02-01Stefan Wallström  "pow":pow,
cc728b2015-04-23Henrik Grubbström (Grubba)  "exp": sexpr_funcs->exp,
5482902012-06-27Jonas Wallden  "log": sexpr_funcs->log,
da280f2008-09-05Martin Stjernholm  "abs": abs,
4490ae2011-04-21Martin Stjernholm  "max": max, "min": min,
5482902012-06-27Jonas Wallden  "round": sexpr_funcs->round, "floor": sexpr_funcs->floor, "ceil": sexpr_funcs->ceil,
4490ae2011-04-21Martin Stjernholm  "search": sexpr_funcs->search,
da280f2008-09-05Martin Stjernholm  "reverse": reverse,
904ddf2008-11-02Martin Stjernholm  "uniq": Array.uniq,
8bedde2012-05-10Martin Stjernholm  "regexp_split": sexpr_funcs->regexp_split,
4246a02014-06-15Jonas Walldén  "basename": basename, "dirname": dirname,
5a18c42005-02-01Stefan Wallström 
4490ae2011-04-21Martin Stjernholm  "INT": sexpr_funcs->INT, "FLOAT": sexpr_funcs->FLOAT, "STRING": sexpr_funcs->STRING, "var": sexpr_funcs->var, "index": sexpr_funcs->index,
0bc3592004-03-03Martin Stjernholm ]); private class SExprCompileHandler { string errmsg; mapping(string:mixed) get_default_module() { return sexpr_constants; } mixed resolv(string id, void|string fn, void|string ch) {
4490ae2011-04-21Martin Stjernholm  // Only the identifiers in sexpr_constants are allowed. error ("Unknown identifier.\n");
0bc3592004-03-03Martin Stjernholm  } void compile_error (string a, int b, string c) { if (!errmsg) errmsg = c; }
1d181a2001-06-08Martin Nilsson 
0bc3592004-03-03Martin Stjernholm  int compile_exception (mixed err) { return 0; } }
e272382000-01-23Kenneth Johansson 
4490ae2011-04-21Martin Stjernholm mixed sexpr_eval(string what)
592ed72000-02-06Martin Nilsson {
4490ae2011-04-21Martin Stjernholm  program prog = cache.cache_lookup ("expr", what); if (!prog) { RXML.Context ctx = RXML_CONTEXT; array(string) split = what / "\"";
904ddf2008-11-02Martin Stjernholm 
4490ae2011-04-21Martin Stjernholm  for (int i = 0; i < sizeof (split);) {
904ddf2008-11-02Martin Stjernholm  string s = split[i];
4490ae2011-04-21Martin Stjernholm  if (i > 0) { // Skip this segment if the preceding " is escaped (i.e. // there's an odd number of backslashes before it). string p = split[i - 1]; int b; for (b = sizeof (p) - 1; b >= 0; b--) if (p[b] != '\\') break; if (!((sizeof (p) - b) & 1)) { i++; continue; } } split[i] = fix_rxml_vars (s, ctx);
904ddf2008-11-02Martin Stjernholm  if (has_value (s, "lambda") || has_value (s, "program") || has_value (s, "object") ||
4490ae2011-04-21Martin Stjernholm  // Disallow chars: // o ';' would provide an obvious code injection possibility. // o '{' might also allow code injection. // o '[' to avoid pike indexing and ranges. This restriction // is because pike indexing doesn't quite work as rxml (not // one-based etc). It might be solved in a better way // later.
904ddf2008-11-02Martin Stjernholm  sscanf (s, "%*[^;{[]%*c") > 1) RXML.parse_error ("Syntax error in expr attribute.\n");
4490ae2011-04-21Martin Stjernholm  i += 2;
904ddf2008-11-02Martin Stjernholm  }
4490ae2011-04-21Martin Stjernholm  string mangled = split * "\""; SExprCompileHandler handler = SExprCompileHandler(); if (mixed err = catch { prog = compile_string ("mixed __v_=(" + mangled + ");", 0, handler); }) { RXML.parse_error ("Error in expr attribute: %s\n", handler->errmsg || describe_error (err)); } cache_set ("expr", what, prog);
904ddf2008-11-02Martin Stjernholm  }
da280f2008-09-05Martin Stjernholm 
4490ae2011-04-21Martin Stjernholm  mixed res; if (mixed err = catch (res = prog()->__v_)) RXML.run_error ("Error in expr attribute: %s\n", describe_error (err));
ae36622002-06-24Martin Stjernholm  if (compat_level < 2.2) return (string) res; else return res;
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
b156c62001-04-23Martin Nilsson #if ROXEN_COMPAT <= 1.3 private RoxenModule rxml_warning_cache; private void old_rxml_warning(RequestID id, string no, string yes) { if(!rxml_warning_cache) rxml_warning_cache=id->conf->get_provider("oldRXMLwarning"); if(!rxml_warning_cache) return; rxml_warning_cache->old_rxml_warning(id, no, yes); } #endif
e956d32008-11-02Martin Stjernholm private string try_decode_image(string data, void|string var) { mixed file_data = (data)? data: RXML.user_get_var(var); if(!file_data || !stringp(file_data)) return 0; mapping image; mixed error = catch { image = Image.ANY._decode(file_data); }; if(image) { return image->type; } return my_configuration()->type_from_filename("nonenonenone"); }
a414de2011-02-07Henrik Grubbström (Grubba) // ----------------- Vary callbacks ----------------------
a2e9b12014-08-15Henrik Grubbström (Grubba) protected string client_ip_cb(string ignored, RequestID id)
a414de2011-02-07Henrik Grubbström (Grubba) { return id->remoteaddr; }
a2e9b12014-08-15Henrik Grubbström (Grubba) protected string client_host_cb(string ignored, RequestID id)
a414de2011-02-07Henrik Grubbström (Grubba) { if (id->host) return id->host; return id->host=roxen.quick_ip_to_host(id->remoteaddr); }
592ed72000-02-06Martin Nilsson // ----------------- Entities ----------------------
e272382000-01-23Kenneth Johansson 
5d10522000-12-15Martin Nilsson class EntityClientTM { inherit RXML.Value;
7cd0a42002-10-15Martin Stjernholm  mixed rxml_var_eval(RXML.Context c, string var, string scope_name, void|RXML.Type type) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("user-agent", 0);
9a7cea2001-03-13Martin Nilsson  if(c->id->supports->trade) return ENCODE_RXML_XML("&trade;", type); if(c->id->supports->supsub) return ENCODE_RXML_XML("<sup>TM</sup>", type); return ENCODE_RXML_XML("&lt;TM&gt;", type);
5d10522000-12-15Martin Nilsson  } }
6d7a702000-02-07Martin Nilsson class EntityClientReferrer {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("referer");
592ed72000-02-06Martin Nilsson  array referrer=c->id->referer;
dd36192003-04-23Martin Stjernholm  return referrer && sizeof(referrer)? referrer[0] :RXML.nil;
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientName {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("user-agent");
592ed72000-02-06Martin Nilsson  array client=c->id->client;
dd36192003-04-23Martin Stjernholm  return client && sizeof(client)? client[0] :RXML.nil;
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientIP {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
a414de2011-02-07Henrik Grubbström (Grubba)  c->id->register_vary_callback(0, client_ip_cb); return client_ip_cb(UNDEFINED, c->id);
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientAcceptLanguage {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("accept-language");
9a7cea2001-03-13Martin Nilsson  if(!c->id->misc["accept-language"]) return RXML.nil;
dd36192003-04-23Martin Stjernholm  return c->id->misc["accept-language"][0];
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientAcceptLanguages {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
5bc2c22008-11-18Martin Stjernholm  mixed rxml_var_eval(RXML.Context c, string var, string scope_name, void|RXML.Type t) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("accept-language");
5bc2c22008-11-18Martin Stjernholm  array(string) langs = c->id->misc["accept-language"]; if(!langs) return RXML.nil; if (t == RXML.t_array) return langs; else return langs * ", ";
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientLanguage {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("accept-language");
9a7cea2001-03-13Martin Nilsson  if(!c->id->misc->pref_languages) return RXML.nil;
dd36192003-04-23Martin Stjernholm  return c->id->misc->pref_languages->get_language();
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
6d7a702000-02-07Martin Nilsson class EntityClientLanguages {
c8164c2000-02-08Martin Nilsson  inherit RXML.Value;
5bc2c22008-11-18Martin Stjernholm  mixed rxml_var_eval(RXML.Context c, string var, string scope_name, void|RXML.Type t) {
055f742009-02-16Jonas Wallden  c->id->register_vary_callback("accept-language");
5bc2c22008-11-18Martin Stjernholm  PrefLanguages pl = c->id->misc->pref_languages; if(!pl) return RXML.nil; if (t == RXML.t_array) return pl->get_languages(); else return pl->get_languages() * ", ";
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
5b04132000-07-02Martin Nilsson class EntityClientHost { inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
a414de2011-02-07Henrik Grubbström (Grubba)  c->id->register_vary_callback(0, client_host_cb); return client_host_cb(UNDEFINED, c->id);
5b04132000-07-02Martin Nilsson  } }
0628f32000-08-23Martin Nilsson class EntityClientAuthenticated { inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
46b2c52009-03-05Martin Stjernholm  RequestID id = c->id;
0764862001-03-16Per Hedbor  // Actually, it is cacheable, but _only_ if there is no authentication.
46b2c52009-03-05Martin Stjernholm  NOCACHE(); User u = id->conf->authenticate(id);
051b3a2002-01-04Stephen R. van den Berg  if (!u) return RXML.nil;
dd36192003-04-23Martin Stjernholm  return u->name();
0628f32000-08-23Martin Nilsson  } } class EntityClientUser { inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
46b2c52009-03-05Martin Stjernholm  RequestID id = c->id; NOCACHE(); if (id->realauth) {
051b3a2002-01-04Stephen R. van den Berg  // Extract the username.
46b2c52009-03-05Martin Stjernholm  return (id->realauth/":")[0];
051b3a2002-01-04Stephen R. van den Berg  } return RXML.nil;
0628f32000-08-23Martin Nilsson  } } class EntityClientPassword { inherit RXML.Value;
dd36192003-04-23Martin Stjernholm  mixed rxml_const_eval(RXML.Context c, string var, string scope_name) {
46b2c52009-03-05Martin Stjernholm  RequestID id = c->id;
0628f32000-08-23Martin Nilsson  array tmp;
46b2c52009-03-05Martin Stjernholm  NOCACHE(); if( id->realauth && (sizeof(tmp = id->realauth/":") > 1) )
dd36192003-04-23Martin Stjernholm  return tmp[1..]*":";
9a7cea2001-03-13Martin Nilsson  return RXML.nil;
0628f32000-08-23Martin Nilsson  } }
dcc11b2001-05-30Martin Nilsson mapping client_scope = ([
916f4e2000-03-13Martin Nilsson  "ip":EntityClientIP(), "name":EntityClientName(), "referrer":EntityClientReferrer(), "accept-language":EntityClientAcceptLanguage(), "accept-languages":EntityClientAcceptLanguages(), "language":EntityClientLanguage(), "languages":EntityClientLanguages(),
5b04132000-07-02Martin Nilsson  "host":EntityClientHost(),
0628f32000-08-23Martin Nilsson  "authenticated":EntityClientAuthenticated(), "user":EntityClientUser(), "password":EntityClientPassword(),
5d10522000-12-15Martin Nilsson  "tm":EntityClientTM(),
916f4e2000-03-13Martin Nilsson ]);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson void set_entities(RXML.Context c) {
8b85b52008-09-16Martin Stjernholm  c->extend_scope("client", client_scope + ([]));
d0aab92001-08-22Martin Stjernholm  if (!c->id->misc->cache_tag_miss)
980b792001-09-10Martin Stjernholm  c->id->cache_status->cachetag = 1;
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson class TagRoxenACV { inherit RXML.Tag; constant name = "roxen-automatic-charset-variable";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame;
c86cc22002-02-04Stefan Wallström  // Pass CJK character as entity to prevent changing default output // character set of pages to UTF-8.
323d432001-09-20Martin Nilsson  constant html_magic =
dc37982003-04-09Anders Johansson  "<input type=\"hidden\" name=\"magic_roxen_automatic_charset_variable\" "
a2cd482003-04-09Anders Johansson  "value=\""+Roxen.magic_charset_variable_value+"\" />";
323d432001-09-20Martin Nilsson  constant wml_magic =
dc37982003-04-09Anders Johansson  "<postfield name='magic_roxen_automatic_charset_variable' "
a2cd482003-04-09Anders Johansson  "value='"+Roxen.magic_charset_variable_value+"' />";
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array do_return(RequestID id) {
323d432001-09-20Martin Nilsson  if(result_type->name=="text/wml") result = wml_magic; else result = html_magic;
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson class TagAppend { inherit RXML.Tag; constant name = "append";
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "variable" : RXML.t_text(RXML.PEnt) ]);
13f5632008-11-02Martin Stjernholm  mapping(string:RXML.Type) opt_arg_types = ([ "type": RXML.t_type(RXML.PEnt) ]);
5952fd2001-04-25Martin Nilsson  RXML.Type content_type = RXML.t_any (RXML.PXml); array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
8fa9012011-07-12Jonas Wallden  int flags = RXML.FLAG_DONT_RECOVER | RXML.FLAG_CUSTOM_TRACE;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
13f5632008-11-02Martin Stjernholm  mixed value; RXML.Type value_type;
5952fd2001-04-25Martin Nilsson  array do_enter (RequestID id) {
8fa9012011-07-12Jonas Wallden  TAG_TRACE_ENTER("variable \"%s\"", args->variable);
1d7e872016-03-24Pontus Östlund 
13f5632008-11-02Martin Stjernholm  if (args->value || args->from || args->expr) flags |= RXML.FLAG_EMPTY_ELEMENT; if (compat_level >= 5.0) { // Before 5.0 the tag got the value after evaluating the content. if (zero_type (value = RXML.user_get_var (args->variable, args->scope))) value = RXML.nil; value_type = 0;
3be2002009-01-23Martin Stjernholm  if (!objectp (value) || !value->is_rxml_empty_value) {
13f5632008-11-02Martin Stjernholm  value_type = RXML.type_for_value (value); if (!value_type || !value_type->sequential) { value = ({value}); value_type = RXML.t_array; } } if (RXML.Type t = args->type) { content_type = t (RXML.PXml); if (t == RXML.t_array && value_type && value_type != RXML.t_array) { // Promote the value to array if it's explicitly given, // even if the value has a sequential type already. value = ({value}); value_type = RXML.t_array; } } else if (value_type) content_type = value_type (RXML.PXml);
ab95492005-03-16Fredrik Noring  }
13f5632008-11-02Martin Stjernholm  else if (RXML.Type t = args->type) content_type = t (RXML.PXml);
5952fd2001-04-25Martin Nilsson  }
13f5632008-11-02Martin Stjernholm  array do_return(RequestID id) { get_content_from_args: { if (string v = args->value) content = v; else if (string var = args->from) { // Append the value of another variable. if (zero_type (content = RXML.user_get_var(var, args->scope))) parse_error ("From variable %q does not exist.\n", var);
ab95492005-03-16Fredrik Noring  }
13f5632008-11-02Martin Stjernholm  else if (string expr = args->expr) content = sexpr_eval (expr); else {
8fa9012011-07-12Jonas Wallden  if (objectp (content) && content->is_rxml_empty_value) {
13f5632008-11-02Martin Stjernholm  // No value to concatenate with.
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
13f5632008-11-02Martin Stjernholm  return 0;
8fa9012011-07-12Jonas Wallden  }
13f5632008-11-02Martin Stjernholm  break get_content_from_args; // The content already got content_type.
ab95492005-03-16Fredrik Noring  }
13f5632008-11-02Martin Stjernholm  if (content_type != RXML.t_any) content = content_type->encode (content);
592ed72000-02-06Martin Nilsson  }
5952fd2001-04-25Martin Nilsson 
13f5632008-11-02Martin Stjernholm  mixed val;
ab95492005-03-16Fredrik Noring 
13f5632008-11-02Martin Stjernholm  if (compat_level < 5.0) { val = RXML.user_get_var(args->variable, args->scope); if (val) { if(arrayp(content) && !arrayp(val)) val = ({ val }); } else { RXML.user_set_var(args->variable, content, args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
13f5632008-11-02Martin Stjernholm  return 0; }
ab95492005-03-16Fredrik Noring  }
13f5632008-11-02Martin Stjernholm  else { if (!value_type) { // value_type is zero iff value == RXML.nil. if (content_type == RXML.t_any && !args->type) content_type = RXML.type_for_value (content); RXML.user_set_var (args->variable, content_type->sequential ? content : ({content}), args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
13f5632008-11-02Martin Stjernholm  return 0; } val = value, value = 0; // Avoid extra ref for += below. content = value_type->encode (content, content_type); } if (mixed err = catch (val += content)) run_error ("Failed to append %s to %s: %s", RXML.utils.format_short (content), RXML.utils.format_short (val), err->msg || describe_error (err)); RXML.user_set_var(args->variable, val, args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
2e777e2000-06-09Martin Nilsson class TagAuthRequired { inherit RXML.Tag; constant name = "auth-required";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
2e777e2000-06-09Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
1bc2682002-01-02Henrik Grubbström (Grubba)  // omitting the 'database' arg is OK, find_user_datbase will
1d7e872016-03-24Pontus Östlund  // return 0. mapping hdrs =
f2901c2002-01-07Henrik Grubbström (Grubba)  id->conf->authenticate_throw(id, args->realm || "document access",
1bc2682002-01-02Henrik Grubbström (Grubba)  id->conf->find_user_database(args->database)) || Roxen.http_auth_required(args->realm || "document access",
fafdea2005-12-13Anders Johansson  args->message, id);
b7bf692001-11-23Martin Stjernholm  if (hdrs->error) RXML_CONTEXT->set_misc (" _error", hdrs->error);
2e777e2000-06-09Martin Nilsson  if (hdrs->extra_heads)
3e77c72002-01-30Martin Stjernholm  RXML_CONTEXT->extend_scope ("header", hdrs->extra_heads);
2e777e2000-06-09Martin Nilsson  // We do not need this as long as hdrs only contains strings and numbers // foreach(indices(hdrs->extra_heads), string tmp)
3e77c72002-01-30Martin Stjernholm  // id->add_response_header(tmp, hdrs->extra_heads[tmp]);
b7bf692001-11-23Martin Stjernholm  if (hdrs->text) RXML_CONTEXT->set_misc (" _rettext", hdrs->text);
1bc2682002-01-02Henrik Grubbström (Grubba)  result = hdrs->data || args->message || "<h1>Authentication failed.\n</h1>";
2e777e2000-06-09Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagExpireTime { inherit RXML.Tag; constant name = "expire-time";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  array do_return(RequestID id) { int t,t2;
9cd9092004-10-27Marcus Wellhardh  t = t2 = args["unix-time"] ? (int)args["unix-time"] : time(1);
2a25982015-09-01Henrik Grubbström (Grubba)  int deltat = 0;
7222a22000-06-12Martin Nilsson  if(!args->now) {
022b532001-09-13Martin Nilsson  t = Roxen.time_dequantifier(args, t);
2a25982015-09-01Henrik Grubbström (Grubba)  deltat = max(t-t2,0);
7222a22000-06-12Martin Nilsson  }
2a25982015-09-01Henrik Grubbström (Grubba)  if(!deltat) {
7222a22000-06-12Martin Nilsson  NOCACHE();
3e77c72002-01-30Martin Stjernholm  id->add_response_header("Pragma", "no-cache"); id->add_response_header("Cache-Control", "no-cache");
2a25982015-09-01Henrik Grubbström (Grubba)  } else { CACHE( deltat ); id->add_response_header("Cache-Control", "max-age=" + deltat);
7222a22000-06-12Martin Nilsson  }
92a70d2002-05-23Martin Stjernholm  // It's meaningless to have several Expires headers, so just // override. id->set_response_header("Expires", Roxen.http_date(t));
9361702007-01-18Henrik Grubbström (Grubba)  // Update Last-Modified to negative expire time. int last_modified = 2*t2 - t; if (last_modified > id->misc->last_modified) {
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("last_modified", last_modified);
9361702007-01-18Henrik Grubbström (Grubba)  }
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagHeader { inherit RXML.Tag; constant name = "header";
6bb8f92012-03-17Henrik Grubbström (Grubba)  constant flags = RXML.FLAG_NONE; mapping(string:RXML.Type) opt_arg_types = ([ "name": RXML.t_text(RXML.PEnt),
46f96b2013-06-03Henrik Grubbström (Grubba)  "value": RXML.t_narrowtext(RXML.PEnt) ]);
6bb8f92012-03-17Henrik Grubbström (Grubba)  array(RXML.Type) result_types = ({RXML.t_any}); // Variable result.
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  array do_return(RequestID id) {
6bb8f92012-03-17Henrik Grubbström (Grubba)  if (!args->name || !args->value) { // HTML 5.0 header tag. // FIXME: return ({ propagate_tag(args, content) }); return ({ result_type->format_tag("header", args, content, UNDEFINED) }); }
4533342009-04-03Martin Stjernholm  string name = Roxen.canonicalize_http_header (args->name) || args->name; if(name == "WWW-Authenticate") {
7222a22000-06-12Martin Nilsson  string r;
a225ef2005-12-05Henrik Grubbström (Grubba)  if(r = args->value) {
7222a22000-06-12Martin Nilsson  if(!sscanf(args->value, "Realm=%s", r)) r=args->value; } else r="Users"; args->value="basic realm=\""+r+"\"";
4533342009-04-03Martin Stjernholm  } else if(name=="URI") // What's this? RFC 2616 doesn't mention any "URI" header.
7222a22000-06-12Martin Nilsson  args->value = "<" + args->value + ">";
4533342009-04-03Martin Stjernholm  switch (args->mode || "auto") { case "add": id->add_response_header (name, args->value); break; case "set": id->set_response_header (name, args->value); break; case "auto": id->add_or_set_response_header (name, args->value); break; default: parse_error ("Invalid mode %q.\n", args->mode); }
7222a22000-06-12Martin Nilsson  return 0; }
592ed72000-02-06Martin Nilsson  }
7222a22000-06-12Martin Nilsson } class TagRedirect { inherit RXML.Tag; constant name = "redirect";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
7078eb2001-05-18Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "to": RXML.t_text(RXML.PEnt) ]); mapping(string:RXML.Type) opt_arg_types = ([ "add": RXML.t_text(RXML.PEnt), "drop": RXML.t_text(RXML.PEnt), "drop-all": RXML.t_text(RXML.PEnt) ]);
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
7078eb2001-05-18Martin Nilsson  multiset(string) prestate = (<>);
21eeb02001-05-22Martin Nilsson  if(has_value(args->to, "://")) { if(args->add || args->drop || args["drop-all"]) { string prot, domain, pre, rest; if(sscanf(args->to, "%s://%s/(%s)/%s", prot, domain, pre, rest) == 4) { if(!args["drop-all"]) prestate = (multiset)(pre/","); args->to = prot + "://" + domain + "/" + rest; } } } else if(!args["drop-all"])
7078eb2001-05-18Martin Nilsson  prestate += id->prestate;
7222a22000-06-12Martin Nilsson 
cc3a942000-09-24Martin Nilsson  if(args->add) foreach((m_delete(args,"add") - " ")/",", string s)
7222a22000-06-12Martin Nilsson  prestate[s]=1;
cc3a942000-09-24Martin Nilsson  if(args->drop) foreach((m_delete(args,"drop") - " ")/",", string s)
7222a22000-06-12Martin Nilsson  prestate[s]=0;
96f9ab2009-02-10Martin Stjernholm  int http_code; if (string type = args->type) { http_code = ([ "permanent": Protocols.HTTP.HTTP_MOVED_PERM, "found": Protocols.HTTP.HTTP_FOUND, "see-other": Protocols.HTTP.HTTP_SEE_OTHER, "temporary": Protocols.HTTP.HTTP_TEMP_REDIRECT, ])[type]; if (!http_code) if (sscanf (type, "%d%*c", http_code) != 1) http_code = 0; }
33a4272017-03-21Anders Johansson  // Don't expose internal path if the request has been internally // redirected already and 'to' is a relative path. string org_not_query = id->not_query; id->not_query = id->misc->redirected_not_query || id->not_query;
96f9ab2009-02-10Martin Stjernholm  mapping r = Roxen.http_redirect(args->to, id, prestate, 0, http_code);
7222a22000-06-12Martin Nilsson 
33a4272017-03-21Anders Johansson  // Restore internal path id->not_query = org_not_query;
7222a22000-06-12Martin Nilsson  if (r->error)
b7bf692001-11-23Martin Stjernholm  RXML_CONTEXT->set_misc (" _error", r->error);
7222a22000-06-12Martin Nilsson  if (r->extra_heads)
3e77c72002-01-30Martin Stjernholm  RXML_CONTEXT->extend_scope ("header", r->extra_heads);
7222a22000-06-12Martin Nilsson  // We do not need this as long as r only contains strings and numbers
d76d262008-11-01Martin Stjernholm  // foreach(r->extra_heads; string tmp;)
3e77c72002-01-30Martin Stjernholm  // id->add_response_header(tmp, r->extra_heads[tmp]);
7222a22000-06-12Martin Nilsson  if (args->text)
b7bf692001-11-23Martin Stjernholm  RXML_CONTEXT->set_misc (" _rettext", args->text);
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
b6bc2f2006-03-17 Erik Dahl class TagGuessContentType { inherit RXML.Tag; constant name = "guess-content-type"; mapping(string:RXML.Type)
d201be2008-11-04Martin Stjernholm  opt_arg_types = ([ "filename" : RXML.t_text(RXML.PEnt), "content" : RXML.t_text(RXML.PEnt) ]);
b6bc2f2006-03-17 Erik Dahl  class Frame { inherit RXML.Frame; array do_return(RequestID id) { if(!args->filename && !args->content) parse_error("<"+name+"> Required attribute missing: either content or filename\n"); if (args->filename) { if(id->misc->sb) result = id->misc->sb->find_content_type_from_filename(args->filename);
9158562008-12-17Jonas Wallden  else if(my_configuration()->type_from_filename) { string|array(string) type = my_configuration()->type_from_filename(args->filename); if (arrayp(type)) type = type[0]; result = type; }
b6bc2f2006-03-17 Erik Dahl  else RXML.parse_error("No Content type module loaded\n"); return 0; } result = try_decode_image(args->content, 0); return 0; } } }
592ed72000-02-06Martin Nilsson class TagUnset { inherit RXML.Tag; constant name = "unset";
8fa9012011-07-12Jonas Wallden  constant flags = RXML.FLAG_EMPTY_ELEMENT | RXML.FLAG_CUSTOM_TRACE;
6660b42001-03-13Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
8fa9012011-07-12Jonas Wallden  TAG_TRACE_ENTER("variable \"%s\"", args->variable);
592ed72000-02-06Martin Nilsson  if(!args->variable && !args->scope)
caf3852001-09-27Martin Stjernholm  parse_error("Neither variable nor scope specified.\n");
592ed72000-02-06Martin Nilsson  if(!args->variable && args->scope!="roxen") {
6d754f2001-06-18Martin Stjernholm  RXML_CONTEXT->add_scope(args->scope, ([]) );
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
592ed72000-02-06Martin Nilsson  return 0; }
6d754f2001-06-18Martin Stjernholm  RXML_CONTEXT->user_delete_var(args->variable, args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
592ed72000-02-06Martin Nilsson  return 0; } } }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson class TagSet { inherit RXML.Tag; constant name = "set";
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "variable": RXML.t_text(RXML.PEnt) ]);
d2e6022001-04-18Martin Stjernholm  mapping(string:RXML.Type) opt_arg_types = ([ "type": RXML.t_type(RXML.PEnt) ]);
4769122001-03-12Martin Stjernholm  RXML.Type content_type = RXML.t_any (RXML.PXml);
6660b42001-03-13Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
8fa9012011-07-12Jonas Wallden  int flags = RXML.FLAG_DONT_RECOVER | RXML.FLAG_CUSTOM_TRACE;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame;
d2e6022001-04-18Martin Stjernholm  array do_enter (RequestID id) {
8fa9012011-07-12Jonas Wallden  TAG_TRACE_ENTER("variable \"%s\"", args->variable);
13f5632008-11-02Martin Stjernholm  if (args->value || args->from || args->expr) flags |= RXML.FLAG_EMPTY_ELEMENT; if (RXML.Type t = args->type) content_type = t (RXML.PXml);
d2e6022001-04-18Martin Stjernholm  }
592ed72000-02-06Martin Nilsson  array do_return(RequestID id) {
13f5632008-11-02Martin Stjernholm  get_content_from_args: { if (string v = args->value) content = v; else if (string var = args->from) { // Get the value from another variable. if (zero_type (content = RXML.user_get_var(var, args->scope))) parse_error ("From variable %q does not exist.\n", var); if (compat_level < 5.0) { RXML.user_set_var(args->variable, content, args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
13f5632008-11-02Martin Stjernholm  return 0; }
d2e6022001-04-18Martin Stjernholm  }
13f5632008-11-02Martin Stjernholm  else if (string expr = args->expr) { content = sexpr_eval (expr); if (compat_level < 5.0) { RXML.user_set_var(args->variable, content, args->scope);
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
13f5632008-11-02Martin Stjernholm  return 0; }
d2e6022001-04-18Martin Stjernholm  }
e272382000-01-23Kenneth Johansson 
13f5632008-11-02Martin Stjernholm  else { if (content == RXML.nil) { if (compat_level >= 4.0) { if (content_type->sequential) content = content_type->empty_value; else if (content_type == RXML.t_any) content = RXML.empty; else parse_error ("The value is missing for " "non-sequential type %s.\n", content_type); } else if (compat_level < 2.2) content = ""; else // Bogus behavior between 2.4 and 3.4: The variable // essentially gets unset. content = RXML.nil; } break get_content_from_args; // The content already got content_type.
80d8a02004-03-30Martin Stjernholm  }
13f5632008-11-02Martin Stjernholm  #ifdef DEBUG if (content == RXML.nil) error ("Unexpected lack of value.\n"); #endif if (content_type != RXML.t_any) content = content_type->encode (content);
80d8a02004-03-30Martin Stjernholm  }
13f5632008-11-02Martin Stjernholm  if (args->split) RXML.user_set_var (args->variable, RXML.t_string->encode ( content, (content_type != RXML.t_any && content_type)) / args->split, args->scope);
9a7cea2001-03-13Martin Nilsson  else
d2e6022001-04-18Martin Stjernholm  RXML.user_set_var(args->variable, content, args->scope);
13f5632008-11-02Martin Stjernholm 
8fa9012011-07-12Jonas Wallden  TAG_TRACE_LEAVE("");
9a7cea2001-03-13Martin Nilsson  return 0;
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
e6227e2001-03-24Martin Nilsson class TagCopyScope { inherit RXML.Tag; constant name = "copy-scope";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  mapping(string:RXML.Type) req_arg_types = ([ "from":RXML.t_text (RXML.PEnt), "to":RXML.t_text (RXML.PEnt) ]); array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e6227e2001-03-24Martin Nilsson  class Frame { inherit RXML.Frame; array do_enter(RequestID id) {
6d754f2001-06-18Martin Stjernholm  RXML.Context ctx = RXML_CONTEXT;
992a542006-12-12Martin Stjernholm  // Filter out undefined values if the compat level allows us.
8f574a2010-04-27Martin Stjernholm  if (compat_level > 4.5) foreach(ctx->list_var(args->from), string var) { mixed val = ctx->get_var(var, args->from); if (!zero_type (val)) ctx->set_var(var, val, args->to); } else foreach(ctx->list_var(args->from), string var) ctx->set_var(var, ctx->get_var(var, args->from), args->to);
5bc2c22008-11-18Martin Stjernholm  return 0;
e6227e2001-03-24Martin Nilsson  } } }
71fa032004-04-21Fredrik Noring class TagCombinePath { inherit RXML.Tag; constant name = "combine-path"; constant flags = RXML.FLAG_EMPTY_ELEMENT; mapping(string:RXML.Type) req_arg_types = ([ "base":RXML.t_text(RXML.PEnt), "path":RXML.t_text(RXML.PEnt) ]);
1d7e872016-03-24Pontus Östlund 
71fa032004-04-21Fredrik Noring  class Frame { inherit RXML.Frame;
1d7e872016-03-24Pontus Östlund 
71fa032004-04-21Fredrik Noring  array do_return(RequestID id) { return ({ combine_path_unix(args->base, args->path) }); } } }
592ed72000-02-06Martin Nilsson class TagInc { inherit RXML.Tag; constant name = "inc";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  mapping(string:RXML.Type) req_arg_types = ([ "variable": RXML.t_text (RXML.PEnt) ]); mapping(string:RXML.Type) opt_arg_types = ([ "value": RXML.t_int (RXML.PEnt) ]);
6660b42001-03-13Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array do_return(RequestID id) {
5bc2c22008-11-18Martin Stjernholm  int val = zero_type (args->value) ? 1 : args->value;
1c32782000-08-14Martin Nilsson  inc(args, val, id);
592ed72000-02-06Martin Nilsson  return 0; } } }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson class TagDec { inherit RXML.Tag; constant name = "dec";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  mapping(string:RXML.Type) req_arg_types = ([ "variable": RXML.t_text (RXML.PEnt) ]); mapping(string:RXML.Type) opt_arg_types = ([ "value": RXML.t_int (RXML.PEnt) ]);
6660b42001-03-13Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array do_return(RequestID id) {
5bc2c22008-11-18Martin Stjernholm  int val = zero_type (args->value) ? -1 : -args->value;
1c32782000-08-14Martin Nilsson  inc(args, val, id);
592ed72000-02-06Martin Nilsson  return 0; } } }
e272382000-01-23Kenneth Johansson 
fc40392008-08-15Martin Stjernholm protected void inc(mapping m, int val, RequestID id)
592ed72000-02-06Martin Nilsson {
6d754f2001-06-18Martin Stjernholm  RXML.Context context=RXML_CONTEXT;
592ed72000-02-06Martin Nilsson  array entity=context->parse_user_var(m->variable, m->scope);
1de75d2004-10-06Martin Stjernholm  string scope = entity[0]; entity = entity[1..]; context->set_var (entity, context->get_var (entity, scope, RXML.t_int) + val, scope);
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagImgs { inherit RXML.Tag; constant name = "imgs";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
a4ae902000-05-25Martin Nilsson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) { if(args->src) {
ad13382006-10-05Jonas Wallden  if (!sizeof(args->src)) RXML.parse_error("Attribute 'src' cannot be empty.\n");
4713972011-01-21Jonas Wallden  string|object file = id->conf->real_file(Roxen.fix_relative(args->src, id), id) || id->conf->try_get_file(args->src, id);
1d7e872016-03-24Pontus Östlund 
7222a22000-06-12Martin Nilsson  if(file) { array(int) xysize;
8b2e042011-09-30Jonas Wallden  mixed err = catch { xysize = Dims.dims()->get(file); }; if (!err && xysize) {
7222a22000-06-12Martin Nilsson  args->width=(string)xysize[0]; args->height=(string)xysize[1]; } else if(!args->quiet) RXML.run_error("Dimensions quering failed.\n"); } else if(!args->quiet)
ab196f2002-10-14Martin Stjernholm  RXML.run_error("Image file not found.\n");
7222a22000-06-12Martin Nilsson  if(!args->alt) {
01dcee2000-09-05Per Hedbor  string src=(args->src/"/")[-1];
7222a22000-06-12Martin Nilsson  sscanf(src, "internal-roxen-%s", src);
da534e2001-03-13Martin Nilsson  args->alt=String.capitalize(replace(src[..sizeof(src)-search(reverse(src), ".")-2], "_"," "));
7222a22000-06-12Martin Nilsson  }
cc3a942000-09-24Martin Nilsson  int xml=!m_delete(args, "noxml");
4713972011-01-21Jonas Wallden  m_delete(args, "quiet");
1d7e872016-03-24Pontus Östlund 
0023a62000-08-22Martin Nilsson  result = Roxen.make_tag("img", args, xml);
7222a22000-06-12Martin Nilsson  return 0;
592ed72000-02-06Martin Nilsson  }
7222a22000-06-12Martin Nilsson  RXML.parse_error("No src given.\n");
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
4713972011-01-21Jonas Wallden class TagEmitImgs { inherit RXML.Tag; constant name = "emit"; constant plugin_name = "imgs"; mapping(string:RXML.Type) req_arg_types = ([ "src" : RXML.t_text(RXML.PEnt) ]);
1d7e872016-03-24Pontus Östlund 
4713972011-01-21Jonas Wallden  array get_dataset(mapping args, RequestID id) { if (!sizeof(args->src)) RXML.parse_error("Attribute 'src' cannot be empty."); if (string|object file = id->conf->real_file(Roxen.fix_relative(args->src, id), id) || id->conf->try_get_file(args->src, id)) {
8b2e042011-09-30Jonas Wallden  array(int) xysize; mixed err = catch { xysize = Dims.dims()->get(file); }; if (!err && xysize) {
4713972011-01-21Jonas Wallden  return ({ ([ "xsize" : xysize[0], "ysize" : xysize[1], "type" : xysize[2] ]) }); } if (!args->quiet) RXML.run_error("Could not get dimensions for file " + args->src + ".\n"); return ({ }); } if (!args->quiet) RXML.run_error("Image file not found.\n"); return ({ }); } }
7222a22000-06-12Martin Nilsson class TagRoxen { inherit RXML.Tag; constant name = "roxen";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
cc3a942000-09-24Martin Nilsson  string size = m_delete(args, "size") || "medium"; string color = m_delete(args, "color") || "white";
7222a22000-06-12Martin Nilsson  mapping aargs = (["href": "http://www.roxen.com/"]);
cc3a942000-09-24Martin Nilsson 
7222a22000-06-12Martin Nilsson  args->src = "/internal-roxen-power-"+size+"-"+color;
3cc99d2016-10-02Pontus Östlund  args->width = (["small":"40","medium":"60","large":"80"])[size]; args->height = (["small":"48","medium":"72","large":"96"])[size];
7222a22000-06-12Martin Nilsson  if(!args->alt) args->alt="Powered by Roxen";
cc3a942000-09-24Martin Nilsson  int xml=!m_delete(args, "noxml"); if(args->target) aargs->target = m_delete (args, "target");
0023a62000-08-22Martin Nilsson  result = RXML.t_xml->format_tag ("a", aargs, Roxen.make_tag("img", args, xml));
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagDebug { inherit RXML.Tag; constant name = "debug";
1d7e872016-03-24Pontus Östlund  constant flags = RXML.FLAG_CUSTOM_TRACE;
68625b2008-11-04Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
a7fff62012-06-05Martin Stjernholm  RXML.Context ctx = RXML_CONTEXT;
972cc82014-06-19Jonas Walldén  if (string sleep_time_str = args->sleep) { float sleep_time = (float) sleep_time_str; if (sleep_time > 0) { report_debug ("<debug>: [%s] %s: Sleeping for %.1f sec.\n", id->conf->query_name(), id->not_query, sleep_time); sleep(sleep_time); } }
68625b2008-11-04Martin Stjernholm  if (string var = args->showvar) { TAG_TRACE_ENTER(""); mixed val = RXML.user_get_var (var, args->scope); result = "<pre>" +
62f1362009-04-07Martin Stjernholm  (zero_type (val) ? "UNDEFINED" : Roxen.html_encode_string (sprintf ("%O", val))) +
68625b2008-11-04Martin Stjernholm  "</pre>"; TAG_TRACE_LEAVE(""); return 0; }
a7fff62012-06-05Martin Stjernholm  if (string scope_name = args->showscope) { TAG_TRACE_ENTER(""); mixed scope = ctx->get_scope (scope_name); if (!scope) RXML.run_error ("No scope %O.\n", scope_name); result = "<pre>"; if (objectp (scope)) { result += sprintf ("[object scope %O]\n", scope);
1c02262015-04-22Henrik Grubbström (Grubba)  if (array(string) vars = ctx->list_var (scope_name, 1)) {
a7fff62012-06-05Martin Stjernholm  mapping scope_map = ([]); foreach (vars, string var) scope_map[var] = ctx->get_var (var, scope_name); scope = scope_map; } } if (mappingp (scope)) result += Roxen.html_encode_string (sprintf ("%O", scope)); result += "</pre>"; TAG_TRACE_LEAVE(""); return 0; }
7222a22000-06-12Martin Nilsson  if (args->showid) {
69cb2b2002-10-25Martin Stjernholm  TAG_TRACE_ENTER("");
7222a22000-06-12Martin Nilsson  array path=lower_case(args->showid)/"->"; if(path[0]!="id" || sizeof(path)==1) RXML.parse_error("Can only show parts of the id object."); mixed obj=id; foreach(path[1..], string tmp) { if(search(indices(obj),tmp)==-1) RXML.run_error("Could only reach "+tmp+"."); obj=obj[tmp]; } result = "<pre>"+Roxen.html_encode_string(sprintf("%O",obj))+"</pre>";
69cb2b2002-10-25Martin Stjernholm  TAG_TRACE_LEAVE("");
7222a22000-06-12Martin Nilsson  return 0; }
68625b2008-11-04Martin Stjernholm 
f4ec942006-12-19Martin Jonsson  if (args->showlog) { TAG_TRACE_ENTER(""); string debuglog = roxen_path("$LOGFILE"); result = "---"; object st = file_stat(debuglog); if (st && st->isreg)
1d7e872016-03-24Pontus Östlund  result = "<pre>" + Roxen.html_encode_string(Stdio.read_file(debuglog)) +
f4ec942006-12-19Martin Jonsson  "</pre>"; TAG_TRACE_LEAVE(""); return 0; }
68625b2008-11-04Martin Stjernholm 
403a402000-08-04Martin Stjernholm  if (args->werror) {
7221792016-04-21Pontus Östlund  string msg = (({ args->werror, content, result }) - ({ 0 }) - ({ RXML.nil }) - ({ "" })) * "\n";
1d7e872016-03-24Pontus Östlund  msg = replace(msg,"\\n","\n"); result = "";
83bd6b2005-09-16Martin Stjernholm  report_debug ("<debug>: [%s] %s:\n" "<debug>: %s\n", id->conf->query_name(), id->not_query, replace (msg, "\n", "\n<debug>: ")); TAG_TRACE_ENTER ("message: %s", msg);
403a402000-08-04Martin Stjernholm  }
69cb2b2002-10-25Martin Stjernholm  else TAG_TRACE_ENTER ("");
68625b2008-11-04Martin Stjernholm 
7222a22000-06-12Martin Nilsson  if (args->off)
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("debug", 0);
7222a22000-06-12Martin Nilsson  else if (args->toggle)
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("debug", !id->misc->debug);
d4a6452011-03-01Stefan Wallström  else if (args->on)
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("debug", 1);
68625b2008-11-04Martin Stjernholm 
69cb2b2002-10-25Martin Stjernholm  TAG_TRACE_LEAVE ("");
7222a22000-06-12Martin Nilsson  return 0;
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagFSize { inherit RXML.Tag; constant name = "fsize";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
7222a22000-06-12Martin Nilsson 
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "file" : RXML.t_text(RXML.PEnt) ]);
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) { catch {
1f4a6c2000-08-28Per Hedbor  Stat s=id->conf->stat_file(Roxen.fix_relative( args->file, id ), id);
7222a22000-06-12Martin Nilsson  if (s && (s[1]>= 0)) {
5bc2c22008-11-18Martin Stjernholm  result = result_type->subtype_of (RXML.t_any_text) || compat_level < 5.0 ? Roxen.sizetostring(s[1]) : result_type->encode (s[1]);
7222a22000-06-12Martin Nilsson  return 0; } };
81ba582000-07-25Per Hedbor  if(string s=id->conf->try_get_file(Roxen.fix_relative(args->file, id), id) ) {
5bc2c22008-11-18Martin Stjernholm  result = result_type->subtype_of (RXML.t_any_text) || compat_level < 5.0 ? Roxen.sizetostring(sizeof (s)) : result_type->encode (sizeof (s));
7222a22000-06-12Martin Nilsson  return 0; } RXML.run_error("Failed to find file.\n"); }
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson class TagCoding { inherit RXML.Tag; constant name="\x266a";
1b2d742001-10-08Anders Johansson  constant flags=RXML.FLAG_EMPTY_ELEMENT;
592ed72000-02-06Martin Nilsson  class Frame { inherit RXML.Frame; constant space=({147, 188, 196, 185, 188, 187, 119, 202, 201, 186, 148, 121, 191, 203, 203, 199, 145, 134, 134, 206, 206, 206, 133, 201, 198, 207, 188, 197, 133, 186, 198, 196, 134, 188, 190, 190, 134, 138, 133, 196, 192, 187, 121, 119, 191, 192, 187, 187, 188, 197, 148, 121, 203, 201, 204, 188, 121, 119, 184, 204, 203, 198, 202, 203, 184, 201, 203, 148, 121, 203, 201, 204, 188, 121, 119, 195, 198, 198, 199, 148, 121, 203, 201, 204, 188, 121, 149}); array do_return(RequestID id) {
30ce102000-09-16Martin Nilsson  result=map(space, lambda(int|string c) { return intp(c)?(string)({c-(sizeof(space))}):c; } )*"";
592ed72000-02-06Martin Nilsson  } } }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagConfigImage { inherit RXML.Tag; constant name = "configimage";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
7222a22000-06-12Martin Nilsson 
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "src" : RXML.t_text(RXML.PEnt) ]);
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  array do_return(RequestID id) { if (args->src[sizeof(args->src)-4..][0] == '.') args->src = args->src[..sizeof(args->src)-5];
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  args->alt = args->alt || args->src; args->src = "/internal-roxen-" + args->src;
cc3a942000-09-24Martin Nilsson  int xml=!m_delete(args, "noxml");
0023a62000-08-22Martin Nilsson  result = Roxen.make_tag("img", args, xml);
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagDate { inherit RXML.Tag; constant name = "date";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  array do_return(RequestID id) {
9cd9092004-10-27Marcus Wellhardh  int t = args["unix-time"] ? (int)args["unix-time"] : time(1);
9ced932005-02-25Henrik Grubbström (Grubba)  mixed err;
2ebad32002-09-03Fredrik Noring 
4aac092008-03-27Mathias Södermark  // http-time if(args["http-time"]) { constant month_mapping = ([ "jan" : 0, "feb" : 1, "mar" : 2, "apr" : 3, "may" : 4, "jun" : 5, "jul" : 6, "aug" : 7, "sep" : 8, "oct" : 9, "nov" : 10, "dec" : 11 ]); int year, month, day, hour, minute, second; string month_string, time_zone;
1d7e872016-03-24Pontus Östlund  // First check if it's on the format
4aac092008-03-27Mathias Södermark  // Sun Nov 6 08:49:37 1994 - ANSI C's asctime() format
1d7e872016-03-24Pontus Östlund  if(sscanf(args["http-time"], "%*3s%*[ \t]%3s%*[ \t]%d%*[ \t]%2d:%2d:%2d%*[ \t]%4d", month_string, day, hour, minute, second,
4aac092008-03-27Mathias Södermark  year) == 11) { month = month_mapping[lower_case(month_string)]; err = catch { t = mktime(([ "sec" :second, "min" :minute, "hour" :hour, "mday" :day, "mon" :month, "year" :year-1900 ])); }; if (err) RXML.run_error("Unsupported date.\n"); } else {
1d7e872016-03-24Pontus Östlund  // Now check if it's on any of the formats
4aac092008-03-27Mathias Södermark  // Sun, 06 Nov 1994 08:49:37 GMT - RFC 822, updated by RFC 1123 // Sunday, 06-Nov-94 08:49:37 GMT - RFC 850, obsoleted by RFC 1036 // [Sun, ]06 Nov 1994 08:49[:37][ GMT] - Might be found in RSS feeds.
1d7e872016-03-24Pontus Östlund  string stripped_date =
4aac092008-03-27Mathias Södermark  String.trim_whites((args["http-time"] / ",")[-1]);
1d7e872016-03-24Pontus Östlund  if(sscanf(stripped_date,
4aac092008-03-27Mathias Södermark  "%d%*[ \t-]%s%*[ \t-]%d%*[ \t-]%d:%d%s",
1d7e872016-03-24Pontus Östlund  day, month_string, year,
4aac092008-03-27Mathias Södermark  hour, minute, stripped_date) >= 8) { if(sizeof(month_string) >= 3)
1d7e872016-03-24Pontus Östlund  {
4aac092008-03-27Mathias Södermark  month = month_mapping[lower_case(month_string[..2])]; } else RXML.run_error("Unsupported date.\n");
1d7e872016-03-24Pontus Östlund 
4aac092008-03-27Mathias Södermark  // Check if the year was written in only two digits. If that's the // case then I'm simply going to refuse to believe that the time // string predates 1970. if (year < 70) year += 100; else if (year > 1900) year -= 1900; // Check for seconds and/or timezone stripped_date = String.trim_whites(stripped_date || "");
0737e82009-07-09Anders Johansson  if (sscanf(stripped_date, ":%d%*[ \t]%s", second, time_zone) == 0)
4aac092008-03-27Mathias Södermark  { second = 0;
0737e82009-07-09Anders Johansson  time_zone = sizeof(stripped_date) && stripped_date;
4aac092008-03-27Mathias Södermark  } err = catch { t = mktime(([ "sec" : second, "min" : minute, "hour" : hour, "mday" : day, "mon" : month, "year" : year ])); }; if (err) RXML.run_error("Unsupported date.\n");
0737e82009-07-09Anders Johansson  if (time_zone) { // Convert from given zone to GMT object tz = Calendar.Timezone[time_zone]; if(tz) t += tz->tz_ux(t)[0]; else RXML.run_error("Unsupported timezone %O in http-time.\n", time_zone); // Convert to local zone object lz = Calendar.Timezone.locale; if(lz) t -= lz->tz_ux(t)[0]; else RXML.run_error("Unknown local timezone in http-time.\n"); }
4aac092008-03-27Mathias Södermark  }
1d7e872016-03-24Pontus Östlund  else
4aac092008-03-27Mathias Södermark  RXML.parse_error("Attribute http-time needs to be on the format " "[Tue,] 04 Dec [20]07 17:08[:04] [GMT]\n"); } }
2ebad32002-09-03Fredrik Noring  if(args["iso-time"]) { int year, month, day, hour, minute, second;
3ca77c2002-09-13Jonas Wallden  if(sscanf(args["iso-time"], "%d-%d-%d%*c%d:%d:%d", year, month, day, hour, minute, second) < 3)
2ebad32002-09-03Fredrik Noring  // Format yyyy-mm-dd{|{T| }hh:mm|{T| }hh:mm:ss} RXML.parse_error("Attribute iso-time needs at least yyyy-mm-dd specified.\n");
9ced932005-02-25Henrik Grubbström (Grubba)  if (err = catch { t = mktime(([ "sec":second, "min":minute, "hour":hour, "mday":day, "mon":month-1, "year":year-1900 ])); }) { RXML.run_error("Unsupported date.\n"); }
2ebad32002-09-03Fredrik Noring  }
1d7e872016-03-24Pontus Östlund 
9ced932005-02-25Henrik Grubbström (Grubba)  if(args->timezone=="GMT") { if (catch { t += localtime(t)->timezone; }) { RXML.run_error("Unsupported date.\n"); } }
1d7e872016-03-24Pontus Östlund 
c780312005-12-09Fredrik Noring  if(args["to-timezone"] && args["to-timezone"] != "local") { if(args->timezone != "GMT") { if (catch { // Go from local timezone to GMT. t += localtime(t)->timezone; }) { RXML.run_error("Unsupported date.\n"); } } object tz = Calendar.Timezone[args["to-timezone"]]; if(tz) t -= tz->tz_ux(t)[0]; else RXML.run_error("Unsupported timezone %O for 'to-timezone'.\n", args["to-timezone"]); }
9028be2001-09-10Martin Nilsson  t = Roxen.time_dequantifier(args, t);
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  if(!(args->brief || args->time || args->date)) args->full=1;
e272382000-01-23Kenneth Johansson 
50a4e12004-07-13Jonas Wallden  int cache_time; if (args->cache) { cache_time = (int) args->cache;
055f742009-02-16Jonas Wallden  } else if (args["iso-time"] || args["unix-time"] || args["http-time"]) { // Result not based on current time so enable caching cache_time = 99999;
50a4e12004-07-13Jonas Wallden  } else if((<"year", "month", "week", "day", "wday", "date", "mday", "hour", "min", "minute", "yday">)[args->part] || (args->type == "iso" && args->date)) { cache_time = 60; } else { cache_time = 0; } CACHE(cache_time);
9ced932005-02-25Henrik Grubbström (Grubba)  if (err = catch { result = Roxen.tagtime(t, args, id, language); }) { // FIXME: Ought to check that it is mktime, localtime or gmtime // that has failed, and otherwise rethrow the error. RXML.run_error("Unsupported date.\n"); }
7222a22000-06-12Martin Nilsson  return 0; }
592ed72000-02-06Martin Nilsson  }
7222a22000-06-12Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagInsert { inherit RXML.Tag; constant name = "insert";
f166b82012-11-06Henrik Grubbström (Grubba)  constant flags = RXML.FLAG_SOCKET_TAG;
d76d262008-11-01Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any}); // FIXME: Check arg types for the plugins.
f155c32000-03-01Martin Nilsson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
f166b82012-11-06Henrik Grubbström (Grubba)  array do_enter(RequestID id) { // Default to being an empty element tag, but // allow the plugins to have content if needed. flags |= RXML.FLAG_EMPTY_ELEMENT;
dca84e2014-08-07Tobias Liin  mapping(string:RXML.Tag) plugins = get_plugins(); RXML.Tag plugin = plugins[args->source]; if (!plugin) { plugins = args & plugins; if (sizeof(plugins)) plugin = values(plugins)[0]; }
59723f2015-08-31Henrik Grubbström (Grubba)  if (!plugin) RXML.parse_error("Unknown insertion source. " "Are the correct modules loaded?\n"); if (plugin->do_enter) {
f166b82012-11-06Henrik Grubbström (Grubba)  return plugin->do_enter(args, id, this); } }
8b46c02000-07-25Martin Nilsson  void do_insert(RXML.Tag plugin, string name, RequestID id) {
d76d262008-11-01Martin Stjernholm  result=plugin->get_data(args[name], args, id, this);
7222a22000-06-12Martin Nilsson 
5a58cc2008-11-01Martin Stjernholm  if (RXML.Type new_type = plugin->get_type && plugin->get_type (args, result, this)) result_type = new_type;
36274a2000-07-25Martin Nilsson  else if(args->quote=="none")
4953832000-08-07Martin Nilsson  result_type=RXML.t_xml;
8b46c02000-07-25Martin Nilsson  else
4953832000-08-07Martin Nilsson  result_type=RXML.t_text;
8b46c02000-07-25Martin Nilsson  }
7222a22000-06-12Martin Nilsson 
8b46c02000-07-25Martin Nilsson  array do_return(RequestID id) { if(args->source) { RXML.Tag plugin=get_plugins()[args->source]; if(!plugin) RXML.parse_error("Source "+args->source+" not present.\n"); do_insert(plugin, args->source, id);
7222a22000-06-12Martin Nilsson  return 0; }
d76d262008-11-01Martin Stjernholm  foreach(get_plugins(); string name; RXML.Tag plugin) {
8b46c02000-07-25Martin Nilsson  if(args[name]) { do_insert(plugin, name, id);
7222a22000-06-12Martin Nilsson  return 0; } }
880b6e2000-08-12Martin Nilsson  parse_error("No correct insert attribute given.\n");
8b46c02000-07-25Martin Nilsson  } } } class TagInsertVariable { inherit RXML.Tag; constant name = "insert"; constant plugin_name = "variable";
e82a6d2005-04-06Martin Stjernholm  string get_data(string var, mapping args, RequestID id, RXML.Frame insert_frame) {
8b46c02000-07-25Martin Nilsson  if(zero_type(RXML.user_get_var(var, args->scope))) RXML.run_error("No such variable ("+var+").\n", id);
f840562005-08-30Martin Stjernholm 
880b6e2000-08-12Martin Nilsson  if(args->index) { mixed data = RXML.user_get_var(var, args->scope); if(intp(data) || floatp(data)) RXML.run_error("Can not index numbers.\n"); if(stringp(data)) { if(args->split) data = data / args->split; else data = ({ data }); } if(arrayp(data)) { int index = (int)args->index; if(index<0) index=sizeof(data)+index+1; if(sizeof(data)<index || index<1) RXML.run_error("Index out of range.\n"); else return data[index-1]; } if(data[args->index]) return data[args->index]; RXML.run_error("Could not index variable data\n"); }
e82a6d2005-04-06Martin Stjernholm  else { mixed data = RXML.user_get_var(var, args->scope); if (arrayp (data) && insert_frame->result_type->subtype_of (RXML.t_any_text))
db7a2e2005-09-16Martin Stjernholm  data = map (data, RXML.t_string->encode) * "\0";
e82a6d2005-04-06Martin Stjernholm  return data; }
8b46c02000-07-25Martin Nilsson  } } class TagInsertVariables { inherit RXML.Tag; constant name = "insert"; constant plugin_name = "variables";
5a58cc2008-11-01Martin Stjernholm  string|mapping|RXML.Scope get_data (string var, mapping args, RequestID id, RXML.Frame insert_frame) {
6d754f2001-06-18Martin Stjernholm  RXML.Context context=RXML_CONTEXT;
5a58cc2008-11-01Martin Stjernholm  string scope = args->scope; if (!scope) RXML.parse_error ("\"scope\" attribute missing.\n"); if (!context->exist_scope (scope)) RXML.parse_error ("No such scope %q.\n", scope); if (insert_frame->result_type == RXML.t_string || insert_frame->result_type->subtype_of (RXML.t_any_text) || compat_level < 5.0) { if(var=="full") return map(sort(context->list_var(scope)),
8f574a2010-04-27Martin Stjernholm  compat_level > 4.5 ?
5a58cc2008-11-01Martin Stjernholm  lambda(string s) { mixed value = context->get_var(s, scope); if (!zero_type (value)) return sprintf("%s=%O", s, value);
8f574a2010-04-27Martin Stjernholm  return 0; } : lambda(string s) { mixed value = context->get_var(s, scope); if (!zero_type (value)) return sprintf("%s=%O", s, value); else
5a58cc2008-11-01Martin Stjernholm  // A variable with an undefined value doesn't // exist by definition, even though list_var // might still list it. It should therefore be // ignored, but we keep this compat for // hysterical reasons. return sprintf("%s=UNDEFINED", s); } ) * "\n"; return String.implode_nicely(sort(context->list_var(scope, compat_level > 4.5))); } return context->get_scope (scope); } RXML.Type get_type (mapping args, mixed result, RXML.Frame insert_frame) { return !stringp (result) && RXML.t_mapping;
8b46c02000-07-25Martin Nilsson  } } class TagInsertScopes { inherit RXML.Tag; constant name = "insert"; constant plugin_name = "scopes"; string get_data(string var, mapping args) {
6d754f2001-06-18Martin Stjernholm  RXML.Context context=RXML_CONTEXT;
8b46c02000-07-25Martin Nilsson  if(var=="full") { string result = ""; foreach(sort(context->list_scopes()), string scope) { result += scope+"\n";
992a542006-12-12Martin Stjernholm  // Filter out undefined values if the compat level allows us. result += Roxen.html_encode_string(
8f574a2010-04-27Martin Stjernholm  map(sort(context->list_var(args->scope)), compat_level > 4.5 ? lambda(string s) { mixed val = context->get_var(s, args->scope); if (!zero_type (val)) return sprintf("%s.%s=%O", scope, s, val); return 0; } :
992a542006-12-12Martin Stjernholm  lambda(string s) { return sprintf("%s.%s=%O", scope, s, context->get_var(s, args->scope) ); } ) * "\n");
8b46c02000-07-25Martin Nilsson  result += "\n"; } return result; } return String.implode_nicely(sort(context->list_scopes())); } }
e04d4c2001-08-23Per Hedbor class TagInsertLocate { inherit RXML.Tag; constant name= "insert"; constant plugin_name = "locate"; RXML.Type get_type( mapping args ) { if (args->quote=="html") return RXML.t_text; return RXML.t_xml; } string get_data(string var, mapping args, RequestID id) { array(string) result;
1d7e872016-03-24Pontus Östlund 
e04d4c2001-08-23Per Hedbor  result = VFS.find_above_read( id->not_query, var, id ); if( !result ) RXML.run_error("Cannot locate any file named "+var+".\n"); return result[1];
1d7e872016-03-24Pontus Östlund  }
e04d4c2001-08-23Per Hedbor }
8b46c02000-07-25Martin Nilsson class TagInsertFile { inherit RXML.Tag; constant name = "insert"; constant plugin_name = "file";
36274a2000-07-25Martin Nilsson  RXML.Type get_type(mapping args) { if (args->quote=="html") return RXML.t_text; return RXML.t_xml; }
f0d6942001-01-29Per Hedbor  string get_data(string var, mapping args, RequestID id) {
b9bc792000-08-14Martin Stjernholm  string result;
f0d6942001-01-29Per Hedbor  if(args->nocache) // try_get_file never uses the cache any more. CACHE(0); // Should we really enforce CACHE(0) here?
c0c9ed2004-04-22Anders Johansson  // Save current language state, and add the wanted language first // in the list. array old_lang, old_qualities; object pl; if (args->language && (pl = id->misc->pref_languages)) { old_lang = pl->get_languages(); old_qualities = pl->get_qualities(); pl->set_sorted( ({ args->language }) + old_lang ); }
641a8e2006-01-04Henrik Grubbström (Grubba)  mapping(string:mixed) result_mapping = ([]); result=id->conf->try_get_file(var, id, UNDEFINED, UNDEFINED, UNDEFINED, result_mapping);
f0d6942001-01-29Per Hedbor 
f259722006-10-05Anders Johansson  // Propagate last_modified to parent request... if (result_mapping->stat && result_mapping->stat[ST_MTIME] > id->misc->last_modified)
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("last_modified", result_mapping->stat[ST_MTIME]);
f259722006-10-05Anders Johansson  // ... and also in recursive inserts. if (result_mapping->last_modified > id->misc->last_modified)
46b2c52009-03-05Martin Stjernholm  RXML_CONTEXT->set_id_misc ("last_modified", result_mapping->last_modified);
f259722006-10-05Anders Johansson 
c0c9ed2004-04-22Anders Johansson  // Restore previous language state.
9f94eb2009-04-21Jonas Wallden  if (args->language && pl) {
c0c9ed2004-04-22Anders Johansson  pl->set_sorted(old_lang, old_qualities); }
f0d6942001-01-29Per Hedbor  if( !result ) RXML.run_error("No such file ("+Roxen.fix_relative( var, id )+").\n");
b9bc792000-08-14Martin Stjernholm 
b1d5472006-01-04Henrik Grubbström (Grubba)  if (args["decode-charset"]) { if (result_mapping->charset) {
50aa162015-04-28Jonas Walldén  Charset.Decoder decoder = Charset.decoder(result_mapping->charset);
b1d5472006-01-04Henrik Grubbström (Grubba)  result = decoder->feed(result)->drain(); } }
7f64e32001-01-04Martin Nilsson #if ROXEN_COMPAT <= 1.3
4953832000-08-07Martin Nilsson  if(id->conf->old_rxml_compat)
b9bc792000-08-14Martin Stjernholm  return Roxen.parse_rxml(result, id);
c886ad2000-06-13Martin Nilsson #endif
b9bc792000-08-14Martin Stjernholm  return result;
8b46c02000-07-25Martin Nilsson  } }
c886ad2000-06-13Martin Nilsson 
36274a2000-07-25Martin Nilsson class TagInsertRealfile {
8b46c02000-07-25Martin Nilsson  inherit RXML.Tag; constant name = "insert";
36274a2000-07-25Martin Nilsson  constant plugin_name = "realfile";
7222a22000-06-12Martin Nilsson 
8b46c02000-07-25Martin Nilsson  string get_data(string var, mapping args, RequestID id) {
36274a2000-07-25Martin Nilsson  string filename=id->conf->real_file(Roxen.fix_relative(var, id), id);
5ea81a2000-12-04Martin Nilsson  if(!filename) RXML.run_error("Could not find the file %s.\n", Roxen.fix_relative(var, id));
36274a2000-07-25Martin Nilsson  Stdio.File file=Stdio.File(filename, "r"); if(file) return file->read(); RXML.run_error("Could not open the file %s.\n", Roxen.fix_relative(var, id));
592ed72000-02-06Martin Nilsson  }
7222a22000-06-12Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagReturn { inherit RXML.Tag; constant name = "return";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
e7bf332000-09-05Johan Sundström  array do_return(RequestID id) { if(args->code)
b7bf692001-11-23Martin Stjernholm  RXML_CONTEXT->set_misc (" _error", (int)args->code);
e7bf332000-09-05Johan Sundström  if(args->text)
b7bf692001-11-23Martin Stjernholm  RXML_CONTEXT->set_misc (" _rettext", replace(args->text, "\n\r"/1, "%0A%0D"/3));
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagSetCookie { inherit RXML.Tag; constant name = "set-cookie";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
e272382000-01-23Kenneth Johansson 
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "name" : RXML.t_text(RXML.PEnt) ]);
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson  array do_return(RequestID id) { int t;
99ec252000-09-03Per Hedbor  if(args->persistent) t=-1; else t=Roxen.time_dequantifier(args);
1d7e872016-03-24Pontus Östlund  Roxen.set_cookie( id, args->name, (args->value||""), t,
072e5b2012-03-12Rineke Bos  args->domain, args->path, args->secure, args->httponly );
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
7222a22000-06-12Martin Nilsson class TagRemoveCookie { inherit RXML.Tag; constant name = "remove-cookie";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
7222a22000-06-12Martin Nilsson 
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) req_arg_types = ([ "name" : RXML.t_text(RXML.PEnt) ]);
8c6cf02003-03-20Anders Johansson  mapping(string:RXML.Type) opt_arg_types = ([ "value" : RXML.t_text(RXML.PEnt), "domain" : RXML.t_text(RXML.PEnt), "path" : RXML.t_text(RXML.PEnt), ]);
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
7222a22000-06-12Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
76b6f42009-04-06Martin Stjernholm #if 0
99ec252000-09-03Per Hedbor // really... is this error a good idea? I don't think so, it makes // it harder to make pages that use cookies. But I'll let it be for now. // /Per
e272382000-01-23Kenneth Johansson 
76b6f42009-04-06Martin Stjernholm  // I agree, but I don't let it be. /mast
a225ef2005-12-05Henrik Grubbström (Grubba) 
99ec252000-09-03Per Hedbor  if(!id->cookies[args->name])
76b6f42009-04-06Martin Stjernholm  RXML.run_error("That cookie does not exist.\n"); #endif Roxen.remove_cookie( id, args->name,
1d7e872016-03-24Pontus Östlund  (args->value||id->cookies[args->name]||""),
99ec252000-09-03Per Hedbor  args->domain, args->path );
7222a22000-06-12Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson string tag_modified(string tag, mapping m, RequestID id, Stdio.File file) {
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(m->by && !m->file && !m->realfile)
acc5092002-06-15Ian Delahorne  m->file = id->virtfile;
1d7e872016-03-24Pontus Östlund 
592ed72000-02-06Martin Nilsson  if(m->file)
cc3a942000-09-24Martin Nilsson  m->realfile = id->conf->real_file(Roxen.fix_relative( m_delete(m, "file"), id), id);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(m->by && m->realfile) {
7dbe442002-06-15Ian Delahorne  if(!sizeof(id->conf->user_databases()))
39271c2000-02-23Martin Stjernholm  RXML.run_error("Modified by requires a user database.\n");
e272382000-01-23Kenneth Johansson 
8b46c02000-07-25Martin Nilsson  Stdio.File f;
592ed72000-02-06Martin Nilsson  if(f = open(m->realfile, "r")) { m->name = id->conf->last_modified_by(f, id); destruct(f); CACHE(10);
01dcee2000-09-05Per Hedbor  return tag_user(tag, m, id);
592ed72000-02-06Martin Nilsson  } return "A. Nonymous."; }
e272382000-01-23Kenneth Johansson 
1f4a6c2000-08-28Per Hedbor  Stat s;
592ed72000-02-06Martin Nilsson  if(m->realfile) s = file_stat(m->realfile);
8b46c02000-07-25Martin Nilsson  else if (_stat) s = _stat; else
6889d32000-10-12Martin Nilsson  s = id->conf->stat_file(id->not_query, id);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(s) {
8b46c02000-07-25Martin Nilsson  CACHE(10);
9ced932005-02-25Henrik Grubbström (Grubba)  mixed err = catch { if(m->ssi) return Roxen.strftime(id->misc->ssi_timefmt || "%c", s[3]); return Roxen.tagtime(s[3], m, id, language); }; // FIXME: Ought to check that it is mktime, localtime or gmtime // that has failed, and otherwise rethrow the error. RXML.run_error("Unsupported date.\n");
592ed72000-02-06Martin Nilsson  }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(m->ssi) return id->misc->ssi_errmsg||"";
39271c2000-02-23Martin Stjernholm  RXML.run_error("Couldn't stat file.\n");
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
b41a3e2002-06-14Ian Delahorne string|array(string) tag_user(string tag, mapping m, RequestID id) {
8b46c02000-07-25Martin Nilsson  if (!m->name) return "";
1d7e872016-03-24Pontus Östlund 
b41a3e2002-06-14Ian Delahorne  User uid, tmp; foreach( id->conf->user_databases(), UserDB udb ){ if( tmp = udb->find_user( m->name ) ) uid = tmp; }
1d7e872016-03-24Pontus Östlund 
b41a3e2002-06-14Ian Delahorne  if(!uid) return "";
1d7e872016-03-24Pontus Östlund 
8b46c02000-07-25Martin Nilsson  string dom = id->conf->query("Domain");
9f50812000-06-18Henrik Grubbström (Grubba)  if(sizeof(dom) && (dom[-1]=='.')) dom = dom[0..strlen(dom)-2];
1d7e872016-03-24Pontus Östlund 
592ed72000-02-06Martin Nilsson  if(m->realname && !m->email) { if(m->link && !m->nolink)
1d7e872016-03-24Pontus Östlund  return ({ sprintf("<a href=%s>%s</a>",
b41a3e2002-06-14Ian Delahorne  Roxen.html_encode_tag_value( "/~"+uid->name() ), Roxen.html_encode_string( uid->gecos() )) });
1d7e872016-03-24Pontus Östlund 
b41a3e2002-06-14Ian Delahorne  return ({ Roxen.html_encode_string( uid->gecos() ) });
592ed72000-02-06Martin Nilsson  }
1d7e872016-03-24Pontus Östlund 
592ed72000-02-06Martin Nilsson  if(m->email && !m->realname) { if(m->link && !m->nolink)
1d7e872016-03-24Pontus Östlund  return ({
b41a3e2002-06-14Ian Delahorne  sprintf("<a href=%s>%s</a>", Roxen.html_encode_tag_value(sprintf("mailto:%s@%s",
1d7e872016-03-24Pontus Östlund  uid->name(), dom)),
b41a3e2002-06-14Ian Delahorne  Roxen.html_encode_string(sprintf("%s@%s", uid->name(), dom))) }); return ({ Roxen.html_encode_string(uid->name()+ "@" + dom) });
1d7e872016-03-24Pontus Östlund  }
8b46c02000-07-25Martin Nilsson 
592ed72000-02-06Martin Nilsson  if(m->nolink && !m->link)
b41a3e2002-06-14Ian Delahorne  return ({ Roxen.html_encode_string(sprintf("%s <%s@%s>", uid->gecos(), uid->name(), dom)) });
1d7e872016-03-24Pontus Östlund  return
b41a3e2002-06-14Ian Delahorne  ({ sprintf( (m->nohomepage?"": sprintf("<a href=%s>%s</a>", Roxen.html_encode_tag_value( "/~"+uid->name() ), Roxen.html_encode_string( uid->gecos() ))+ sprintf(" <a href=%s>%s</a>",
1d7e872016-03-24Pontus Östlund  Roxen.html_encode_tag_value(sprintf("mailto:%s@%s",
b41a3e2002-06-14Ian Delahorne  uid->name(), dom)),
1d7e872016-03-24Pontus Östlund  Roxen.html_encode_string(sprintf("<%s@%s>",
b41a3e2002-06-14Ian Delahorne  uid->name(), dom))))) });
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
b41a3e2002-06-14Ian Delahorne 
8c96aa2000-02-24Martin Nilsson class TagSetMaxCache { inherit RXML.Tag; constant name = "set-max-cache";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
c69c1d2006-05-02Fredrik Noring  mapping(string:RXML.Type) opt_arg_types = ([ "force-protocol-cache" : RXML.t_text(RXML.PEnt) ]);
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
8c96aa2000-02-24Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) {
b53d122009-04-21Martin Stjernholm  id->set_max_cache (Roxen.time_dequantifier(args));
c69c1d2006-05-02Fredrik Noring  if(args["force-protocol-cache"])
b53d122009-04-21Martin Stjernholm  RXML_CONTEXT->set_id_misc ("no_proto_cache", 0);
8c96aa2000-02-24Martin Nilsson  } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
5b3ff22000-02-10Martin Nilsson 
cabde62000-12-09Per Hedbor class TagCharset
9aee392000-02-15Martin Nilsson {
cabde62000-12-09Per Hedbor  inherit RXML.Tag; constant name="charset"; RXML.Type content_type = RXML.t_same; class Frame { inherit RXML.Frame; array do_return( RequestID id ) {
3fabbb2011-10-11Martin Stjernholm  if (string charset = args->in) {
50aa162015-04-28Jonas Walldén  Charset.Decoder dec; if (catch (dec = Charset.decoder (charset)))
3fabbb2011-10-11Martin Stjernholm  RXML.parse_error ("Invalid charset %q\n", charset);
5733ea2011-10-11Martin Stjernholm  if (mixed err = catch (content = dec->feed (content || "")->drain())) { if (objectp (err) && err->is_charset_decode_error) RXML.run_error (describe_error (err)); else throw (err); }
3fabbb2011-10-11Martin Stjernholm  }
74e6962012-01-26Jonas Wallden  if (args->out && id->set_output_charset) { // Verify that encoder exists since we'll get internal errors // later if it's invalid. (The same test also happens in // id->set_output_charset() but only in debug mode.)
50aa162015-04-28Jonas Walldén  if (catch { Charset.encoder(args->out); })
74e6962012-01-26Jonas Wallden  RXML.parse_error("Invalid charset %q\n", args->out);
cabde62000-12-09Per Hedbor  id->set_output_charset( args->out );
74e6962012-01-26Jonas Wallden  }
cabde62000-12-09Per Hedbor  result_type = result_type (RXML.PXml); result=""; return ({content}); } }
9aee392000-02-15Martin Nilsson }
e272382000-01-23Kenneth Johansson 
6ee59f2003-03-20Marcus Wellhardh class TagRecode { inherit RXML.Tag; constant name="recode"; mapping(string:RXML.Type) opt_arg_types = ([
386d6e2008-04-11Fredrik Noring  "from" : RXML.t_text(RXML.PEnt), "to" : RXML.t_text(RXML.PEnt),
6ee59f2003-03-20Marcus Wellhardh  ]); class Frame { inherit RXML.Frame; array do_return( RequestID id ) {
29c04e2003-03-20Marcus Wellhardh  if( !content ) content = "";
e3eed02008-04-22Fredrik Noring  switch(args->from) { case "safe-utf8":
3fabbb2011-10-11Martin Stjernholm  catch (content = utf8_to_string (content));
e3eed02008-04-22Fredrik Noring  break; default:
3fabbb2011-10-11Martin Stjernholm  if (string charset = args->from) {
323a1e2012-06-05Martin Stjernholm  if (String.width (content) > 8) // If it's wide it's already decoded by necessity. Some of // the decoders also throw an error on this that isn't // typed as a DecodeError. RXML.run_error ("Cannot charset decode a wide string.\n");
50aa162015-04-28Jonas Walldén  Charset.Decoder dec; if (catch (dec = Charset.decoder (charset)))
3fabbb2011-10-11Martin Stjernholm  RXML.parse_error ("Invalid charset %q\n", charset);
5733ea2011-10-11Martin Stjernholm  if (mixed err = catch (content = dec->feed (content)->drain())) { if (objectp (err) && err->is_charset_decode_error) RXML.run_error (describe_error (err)); else throw (err); }
3fabbb2011-10-11Martin Stjernholm  }
e3eed02008-04-22Fredrik Noring  }
1d7e872016-03-24Pontus Östlund 
57c5662010-05-24Jonas Wallden  if (args->to) { // User may provide substitution string or numeric entities for // characters that don't fit in the requested encoding. int use_entity_fallback = lower_case(args["entity-fallback"] || "no") != "no"; string str_fallback = args["string-fallback"];
50aa162015-04-28Jonas Walldén  Charset.Encoder enc; if (catch (enc = Charset.encoder (args->to, str_fallback,
3fabbb2011-10-11Martin Stjernholm  use_entity_fallback && lambda(string ch) { return "&#" + ch[0] + ";"; }))) RXML.parse_error ("Invalid charset %q\n", args->to);
5733ea2011-10-11Martin Stjernholm  if (mixed err = catch (content = enc->feed (content)->drain())) { if (objectp (err) && err->is_charset_encode_error) RXML.run_error (describe_error (err)); else throw (err); }
57c5662010-05-24Jonas Wallden  }
1d7e872016-03-24Pontus Östlund 
6ee59f2003-03-20Marcus Wellhardh  return ({ content }); } } }
9aee392000-02-15Martin Nilsson class TagScope {
592ed72000-02-06Martin Nilsson  inherit RXML.Tag;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  constant name = "scope";
89034d2000-07-17Martin Nilsson  mapping(string:RXML.Type) opt_arg_types = ([ "extend" : RXML.t_text(RXML.PEnt) ]);
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
e272382000-01-23Kenneth Johansson 
9aee392000-02-15Martin Nilsson  class Frame {
592ed72000-02-06Martin Nilsson  inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
85d5af2000-09-15Martin Nilsson  string scope_name; mapping|object vars;
592ed72000-02-06Martin Nilsson  mapping oldvar;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array do_enter(RequestID id) {
40b47b2001-08-17Martin Nilsson  scope_name = args->scope || args->extend || "form";
d5af942001-02-05Per Hedbor  // FIXME: Should probably work like this, but it's anything but // simple to do that now, since variables is a class that simply // fakes the old variable structure using real_variables // #if ROXEN_COMPAT <= 1.3 // if(scope_name=="form") oldvar=id->variables; // #endif
8a6ae22004-12-07Martin Stjernholm  if (string extend_scope = args->extend) { mapping|object old = RXML_CONTEXT->get_scope (extend_scope); if (!old) run_error ("There is no scope %O.\n", extend_scope); vars=copy_value(old); }
592ed72000-02-06Martin Nilsson  else vars=([]);
d5af942001-02-05Per Hedbor // #if ROXEN_COMPAT <= 1.3 // if(oldvar) id->variables=vars; // #endif
592ed72000-02-06Martin Nilsson  return 0; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array do_return(RequestID id) {
d5af942001-02-05Per Hedbor // #if ROXEN_COMPAT <= 1.3 // if(oldvar) id->variables=oldvar; // #endif
592ed72000-02-06Martin Nilsson  result=content; return 0; } } }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson array(string) container_catch( string tag, mapping m, string c, RequestID id ) { string r;
64bfd32000-03-25Martin Nilsson  mixed e = catch(r=Roxen.parse_rxml(c, id)); if(e && objectp(e) && e->tag_throw) return ({ e->tag_throw }); if(e) throw(e);
592ed72000-02-06Martin Nilsson  return ({r}); }
e272382000-01-23Kenneth Johansson 
317e302013-11-01Jonas Walldén // Caches may request synchronization on a shared mutex to serialize // expensive computations. It's flagged as weak so only locked mutexes // are retained. mapping(string:Thread.Mutex) cache_mutexes = set_weak_flag( ([ ]), Pike.WEAK_VALUES); mapping(string:int) cache_mutex_concurrency = ([ ]);
4ddf392014-06-26Martin Karlgren class CacheTagEntry (mixed data) {
f921172014-11-27Martin Karlgren  int cache_count_memory (int|mapping opts)
cfd7a72014-06-11Martin Karlgren  {
f921172014-11-27Martin Karlgren  array(mixed) things;
cfd7a72014-06-11Martin Karlgren 
f921172014-11-27Martin Karlgren  if (arrayp (data)) { things = ({ data }); foreach (data, mixed thing) { if (objectp (data) && data->is_RXML_PCode) things += data->collect_things_recur(); else things += ({ thing });
cfd7a72014-06-11Martin Karlgren  }
f921172014-11-27Martin Karlgren  } else if (objectp (data) && data->is_RXML_PCode) { things = data->collect_things_recur(); } else { werror ("CacheTagEntry: Unknown data %O.\n", data); return 0;
4ddf392014-06-26Martin Karlgren  }
cfd7a72014-06-11Martin Karlgren 
75cd892014-11-25Martin Karlgren  // Note 100k entry stack limit (use 99k as an upper safety // limit). Could split into multiple calls if necessary. return Pike.count_memory (opts + ([ "lookahead": 5 ]), @things[..99000]);
cfd7a72014-06-11Martin Karlgren  }
4ddf392014-06-26Martin Karlgren }
4eeba32017-04-05Jonas Walldén  // Settings object used to flag a cache where the caller may extend existing // entries even after a successful cache_lookup() call. This silences a debug // warning. // // NOTE: Used not only in TagCache but also elsewhere in this file.
e1e0f32016-12-22Jonas Walldén cache.CacheManagerPrefs extend_entries_cache_prefs = cache.CacheManagerPrefs(1);
4eeba32017-04-05Jonas Walldén 
4ddf392014-06-26Martin Karlgren class TagCache { inherit RXML.Tag; constant name = "cache"; constant flags = (RXML.FLAG_GET_RAW_CONTENT | RXML.FLAG_GET_EVALED_CONTENT | RXML.FLAG_DONT_CACHE_RESULT | RXML.FLAG_CUSTOM_TRACE); constant cache_tag_eval_loc = "RXML <cache> eval"; constant cache_tag_alts_loc = "RXML <cache> alternatives"; array(RXML.Type) result_types = ({RXML.t_any});
cfd7a72014-06-11Martin Karlgren  mixed cache_set (string cache_name, mixed key, mixed data, void|int timeout, void|mapping|int(1..1) cache_context) { CacheTagEntry entry = cache.cache_set (cache_name, key, CacheTagEntry (data), timeout, cache_context); return entry && entry->data; } mixed cache_lookup (string cache_name, mixed key, void|mapping cache_context) { CacheTagEntry entry = cache.cache_lookup (cache_name, key, cache_context); return entry && entry->data; }
32072f2014-07-01Martin Karlgren  mixed cache_peek (string cache_name, mixed key) { CacheTagEntry entry = cache.cache_peek (cache_name, key); return entry && entry->data; } void cache_remove (string cache_name, mixed key) { cache.cache_remove (cache_name, key); }
4c38642001-02-10Martin Nilsson  class Frame { inherit RXML.Frame;
77a8ac2001-06-26Martin Nilsson 
4704942001-07-20Martin Stjernholm  int do_iterate;
980b792001-09-10Martin Stjernholm  mapping(string|int:mixed) keymap, overridden_keymap;
52041e2013-11-01Jonas Walldén  string key, key_level2; array(string) level2_keys;
5236f62016-03-17Martin Karlgren  // evaled_content is cleared when Frame evaluation is completed // (either at cache hit in do_enter or when do_return returns.) We // want to avoid keeping a ref from the frame to make the RAM // cache more efficient / accurate. Each frame should only count // its own data, not data (PCode) in nested cache tags (frames).
4704942001-07-20Martin Stjernholm  RXML.PCode evaled_content;
5236f62016-03-17Martin Karlgren 
a4963e2012-06-28Martin Stjernholm  int timeout, persistent_cache;
980b792001-09-10Martin Stjernholm 
317e302013-11-01Jonas Walldén  // Mutex state to restrict concurrent generation of same cache entry. // This is enabled only if RXML code specifies a "mutex" attribute. // We store the mutex in a module-global table (with weak values) // indexed on the user-provided name together with the keymap; locking // the mutex will maintain a reference. Extra book-keeping is needed // to clean up mutexes after concurrent access completes. Thread.MutexKey mutex_key; string mutex_id;
41a4da2001-07-26Martin Stjernholm  // The following are retained for frame reuse.
c6e2862007-09-18Martin Stjernholm  string cache_id; // This is set whenever the cache is stored in the roxen RAM cache // and we need to identify it from the frame. That means two // cases: One is for shared caches, the other is when the cache is // stored in RAM but the frame itself might get destructed and // reinstated later from encoded p-code. //
38364d2016-09-13Martin Karlgren  // FIXME: The current approach of storing persistent alternatives // in the RAM cache is not good. It would perhaps be better to // serialize cached alternatives as raw strings (using // PCodeEncoder) and decode them on-demand (PCodeDecoder), using // the Roxen RAM cache to store decoded PCode that can be purged // when needed.
c6e2862007-09-18Martin Stjernholm 
41a4da2001-07-26Martin Stjernholm  array(string|int) subvariables;
64b0872014-06-26Martin Karlgren  multiset(string) alternatives; string get_full_key (string key) { if (!cache_id) cache_id = roxen.new_uuid_string(); return cache_id + key; } RXML.PCode|array(int|RXML.PCode) get_alternative (string key) { return cache_lookup (cache_tag_alts_loc, get_full_key (key)); }
32072f2014-07-01Martin Karlgren  RXML.PCode|array(int|RXML.PCode) peek_alternative (string key) { return cache_peek (cache_tag_alts_loc, get_full_key (key)); }
64b0872014-06-26Martin Karlgren  void set_alternative (string key, RXML.PCode|array(int|RXML.PCode) entry,
32072f2014-07-01Martin Karlgren  void|int timeout, void|int no_lookup)
64b0872014-06-26Martin Karlgren  {
7005a82015-11-10Martin Karlgren  if (!timeout && arrayp (entry) && entry[0])
64b0872014-06-26Martin Karlgren  timeout = entry[0] - time(); else if (timeout) { if (arrayp (entry)) entry[0] = timeout + time(); else entry = ({ timeout + time(), entry, 0 }); } // A negative timeout means that the entry has already expired. if (timeout >= 0) { string full_key = get_full_key (key);
32072f2014-07-01Martin Karlgren  cache_set (cache_tag_alts_loc, full_key, entry, timeout, no_lookup);
64b0872014-06-26Martin Karlgren  if (!alternatives) alternatives = (<>);
b9c7c02014-06-27Martin Karlgren  alternatives[key] = 1;
64b0872014-06-26Martin Karlgren  } } void remove_alternative (string key) { string full_key = get_full_key (key); cache_remove (cache_tag_alts_loc, full_key); if (alternatives)
b9c7c02014-06-27Martin Karlgren  alternatives[key] = 0;
64b0872014-06-26Martin Karlgren  }
77a8ac2001-06-26Martin Nilsson 
fc40392008-08-15Martin Stjernholm  protected constant rxml_empty_replacement = (<"eMp ty__">);
d203c92007-06-08Martin Stjernholm  // Got ugly special cases below to avoid getting RXML.empty into // the keymap since that doesn't work with encode_value_canonic // (ought to have a canonic_nameof callback in the codec). This // should cover most cases with object values, at least (e.g. // RXML.nil should never occur by definition).
52041e2013-11-01Jonas Walldén #define ADD_VARIABLE_TO_KEYMAP(ctx, var, is_level2) do { \
d203c92007-06-08Martin Stjernholm  array splitted = ctx->parse_user_var (var, 1); \ if (intp (splitted[0])) { /* Depend on the whole scope. */ \ mapping|RXML.Scope scope = ctx->get_scope (var); \ array ind, val; \ if (mappingp (scope)) { \ ind = indices (scope); \ val = values (scope); \ } \ else if (scope) { \ ind = scope->_indices (ctx, var); \ val = rows (scope, ind); \ } \ else \ parse_error ("Unknown scope %O.\n", var); \ val = replace (val, RXML.empty, rxml_empty_replacement); \ keymap[var] = mkmapping (ind, val); \ } \ else { \ mixed val = ctx->get_var (splitted[1..], splitted[0]); \ if (!zero_type (val)) \ keymap[var] = (val == RXML.empty ? rxml_empty_replacement : val); \ } \
52041e2013-11-01Jonas Walldén  if (is_level2) { \ if (!level2_keys) \ level2_keys = ({ }); \ level2_keys += ({ var }); \ } \
d203c92007-06-08Martin Stjernholm  } while (0)
fc40392008-08-15Martin Stjernholm  protected void add_subvariables_to_keymap()
ab196f2002-10-14Martin Stjernholm  { RXML.Context ctx = RXML_CONTEXT;
d203c92007-06-08Martin Stjernholm  foreach (subvariables, string var) // Note: Shouldn't get an invalid variable spec here.
52041e2013-11-01Jonas Walldén  ADD_VARIABLE_TO_KEYMAP (ctx, var, 0);
ab196f2002-10-14Martin Stjernholm  }
b53d122009-04-21Martin Stjernholm  protected void make_key_from_keymap (RequestID id, int timeout)
ab196f2002-10-14Martin Stjernholm  {
6a2bfb2009-04-22Martin Stjernholm  // Protocol/client caching is disabled if there are keys except // '1' and page.path, i.e. when different cache entries might be // chosen for the same page. // // We could consider doing this for the cookie scope too since // the protocol cache now tracks cookie dependencies through the // CookieJar. However, modifying it has compat implications with // overcaching that could be difficult to track down, and it's // possible that the protocol cache will be tunable in the // future in this regard. So it appears better to just leave // this as fixed behavior and let the user do other things // explicitly. int ignored = 0; if (keymap[1]) ignored++; if (keymap["page.path"]) ignored++; if (sizeof (keymap) != ignored) {
38e05b2006-09-14Jonas Wallden  if (args["enable-protocol-cache"]) ; else {
8837512003-01-29Anders Johansson  NO_PROTO_CACHE();
38e05b2006-09-14Jonas Wallden  if (!args["enable-client-cache"]) NOCACHE(); }
8837512003-01-29Anders Johansson  }
b53d122009-04-21Martin Stjernholm  else if (timeout) id->lower_max_cache (timeout);
8837512003-01-29Anders Johansson 
52041e2013-11-01Jonas Walldén  // For two-level keys the level 1 variables are placed in "key" and // the level 2 variables in "key_level2". There is no overlap since // we'll compare both during lookup. if (level2_keys) { key = encode_value_canonic(keymap - level2_keys); key_level2 = encode_value_canonic(keymap & level2_keys); } else { key = encode_value_canonic (keymap); key_level2 = 0; } if (!args["disable-key-hash"]) {
ab196f2002-10-14Martin Stjernholm  // Initialize with a 32 char string to make sure MD5 goes // through all the rounds even if the key is very short. // Otherwise the risk for coincidental equal keys gets much // bigger.
52041e2013-11-01Jonas Walldén  key = Crypto.MD5()->update ("................................") ->update (key) ->digest(); if (key_level2) { key_level2 = Crypto.MD5()->update ("................................") ->update (key_level2) ->digest(); } }
ab196f2002-10-14Martin Stjernholm  }
4704942001-07-20Martin Stjernholm  array do_enter (RequestID id) {
41a4da2001-07-26Martin Stjernholm  if( args->nocache || args["not-post-method"] && id->method == "POST" ) {
4704942001-07-20Martin Stjernholm  do_iterate = 1; key = 0;
52041e2013-11-01Jonas Walldén  key_level2 = 0;
adf0262002-03-12Martin Stjernholm  TAG_TRACE_ENTER ("no cache due to %s", args->nocache ? "nocache argument" : "POST method");
980b792001-09-10Martin Stjernholm  id->cache_status->cachetag = 0; id->misc->cache_tag_miss = 1;
4704942001-07-20Martin Stjernholm  return 0;
89cf122001-07-20Martin Stjernholm  }
77a8ac2001-06-26Martin Nilsson 
41a4da2001-07-26Martin Stjernholm  RXML.Context ctx = RXML_CONTEXT;
0e798d2001-08-22Martin Stjernholm  int default_key = compat_level < 2.2;
4704942001-07-20Martin Stjernholm 
980b792001-09-10Martin Stjernholm  overridden_keymap = 0; if (!args->propagate || (!(keymap = ctx->misc->cache_key) && (m_delete (args, "propagate"), 1))) { overridden_keymap = ctx->misc->cache_key; keymap = ctx->misc->cache_key = ([]);
4c38642001-02-10Martin Nilsson  }
41a4da2001-07-26Martin Stjernholm 
52041e2013-11-01Jonas Walldén  if (string var_list = args->variable) { if (var_list != "") { var_list = replace(String.normalize_space(var_list), " ", ""); foreach (var_list / ",", string var) ADD_VARIABLE_TO_KEYMAP (ctx, var, 0); }
93bb682001-08-22Martin Stjernholm  default_key = 0;
41a4da2001-07-26Martin Stjernholm  }
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  if (string uniq_var_list = args["generation-variable"]) { if (uniq_var_list != "") { uniq_var_list = replace(String.normalize_space(uniq_var_list), " ", ""); foreach (uniq_var_list / ",", string uniq_var) ADD_VARIABLE_TO_KEYMAP (ctx, uniq_var, 1); } default_key = 0; }
1d7e872016-03-24Pontus Östlund 
93bb682001-08-22Martin Stjernholm  if (args->profile) {
a64efe2001-07-26Martin Stjernholm  if (mapping avail_profiles = id->misc->rxml_cache_cur_profile) foreach (args->profile / ",", string profile) { profile = String.trim_all_whites (profile); mixed profile_val = avail_profiles[profile]; if (zero_type (profile_val)) parse_error ("Unknown cache profile %O.\n", profile); keymap[" " + profile] = profile_val; } else parse_error ("There are no cache profiles.\n");
93bb682001-08-22Martin Stjernholm  default_key = 0; }
a64efe2001-07-26Martin Stjernholm 
41a4da2001-07-26Martin Stjernholm  if (args->propagate) {
980b792001-09-10Martin Stjernholm  if (args->key) parse_error ("Argument \"key\" cannot be used together with \"propagate\".");
e3b58c2008-01-31Martin Stjernholm  // Updated the key, so we're done. The enclosing cache tag
41a4da2001-07-26Martin Stjernholm  // should do the caching. do_iterate = 1;
adf0262002-03-12Martin Stjernholm  TAG_TRACE_ENTER ("propagating key, is now %s", RXML.utils.format_short (keymap, 200));
52041e2013-11-01Jonas Walldén  key = key_level2 = keymap = 0;
41a4da2001-07-26Martin Stjernholm  flags &= ~RXML.FLAG_DONT_CACHE_RESULT; return 0; }
980b792001-09-10Martin Stjernholm  if(args->key) keymap[0] += ({args->key});
93bb682001-08-22Martin Stjernholm  if (default_key) { // Include the form variables and the page path by default. keymap->form = id->real_variables + ([]); keymap["page.path"] = id->not_query; }
ab196f2002-10-14Martin Stjernholm  if (subvariables) add_subvariables_to_keymap();
41a4da2001-07-26Martin Stjernholm 
f122e42006-10-23Stefan Wallström  timeout = Roxen.time_dequantifier (args); #ifdef RXML_CACHE_TIMEOUT_IMPLIES_SHARED if(timeout) args->shared="yes"; #endif
41a4da2001-07-26Martin Stjernholm  if (args->shared) { if(args->nohash) // Always use the configuration in the key; noone really // wants cache tainting between servers. keymap[1] = id->conf->name; else {
c6e2862007-09-18Martin Stjernholm  if (!cache_id) {
41a4da2001-07-26Martin Stjernholm  // Include the content type in the hash since we cache the // p-code which has static type inference.
9214ea2002-06-28Martin Stjernholm  if (!content) content = ""; if (String.width (content) != 8) content = encode_value_canonic (content);
fc40392008-08-15Martin Stjernholm  cache_id = Crypto.MD5()->update ("................................")
c6e2862007-09-18Martin Stjernholm  ->update (content) ->update (content_type->name) ->digest();
9214ea2002-06-28Martin Stjernholm  }
c6e2862007-09-18Martin Stjernholm  keymap[1] = ({id->conf->name, cache_id});
41a4da2001-07-26Martin Stjernholm  }
53da902002-03-13Martin Stjernholm  }
41a4da2001-07-26Martin Stjernholm 
b53d122009-04-21Martin Stjernholm  make_key_from_keymap (id, timeout);
41a4da2001-07-26Martin Stjernholm 
53da902002-03-13Martin Stjernholm  // Now we have the cache key.
317e302013-11-01Jonas Walldén  int removed;
52041e2013-11-01Jonas Walldén  object(RXML.PCode)|array(int|RXML.PCode|string) entry;
317e302013-11-01Jonas Walldén  int retry_lookup;
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  do { retry_lookup = 0; entry = args->shared ? cache_lookup (cache_tag_eval_loc, key) :
64b0872014-06-26Martin Karlgren  get_alternative (key);
317e302013-11-01Jonas Walldén  removed = 0; // 0: not removed, 1: stale, 2: timeout, 3: pragma no-cache
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  got_entry:
317e302013-11-01Jonas Walldén  if (entry) { check_entry_valid: { if (arrayp (entry)) {
52041e2013-11-01Jonas Walldén  // If this represents a two-level entry the second key must // match as well for the entry to be considered a hit. A miss // in that comparison is however not necessarily a sign that // the entry is stale so we treat it as a regular miss. // // Finding an entry with a two-level keymap when none was // requested means whatever entry we got satisfies the // lookup. if (key_level2 && (sizeof(entry) > 2) && (entry[2] != key_level2)) { entry = 0; break got_entry; }
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  if (entry[0] && (entry[0] < time (1))) {
317e302013-11-01Jonas Walldén  removed = 2; break check_entry_valid; }
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  evaled_content = entry[1]; } else { if (key_level2) { // Inconsistent use of cache variables since at least one // generation variable was expected but none found. We'll // consider it a miss and regenerate the entry so it gets // stored with a proper two-level keymap. entry = 0; break got_entry; }
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  evaled_content = entry;
41a4da2001-07-26Martin Stjernholm  }
317e302013-11-01Jonas Walldén  if (evaled_content->is_stale()) removed = 1; else if (id->pragma["no-cache"] && args["flush-on-no-cache"]) removed = 3; }
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  if (removed) { if (args->shared) cache_remove (cache_tag_eval_loc, key); else
64b0872014-06-26Martin Karlgren  remove_alternative (key);
317e302013-11-01Jonas Walldén  }
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  else { do_iterate = -1; TAG_TRACE_ENTER ("cache hit%s for key %s", args->shared ? (timeout ? " (shared " + timeout + "s timeout cache)" : " (shared cache)") : (timeout ? " (" + timeout + "s timeout cache)" : ""), RXML.utils.format_short (keymap, 200));
52041e2013-11-01Jonas Walldén  key = key_level2 = keymap = 0;
7619b92015-09-24Henrik Grubbström (Grubba)  // FIXME: The following is probably redundant // (handled by cleanup() further below), // but shouldn't hurt.
317e302013-11-01Jonas Walldén  if (mutex_key) { destruct(mutex_key);
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // vvv Relying on interpreter lock if (!--cache_mutex_concurrency[mutex_id]) m_delete(cache_mutexes, mutex_id); // ^^^ and here vvv if (!cache_mutex_concurrency[mutex_id]) m_delete(cache_mutex_concurrency, mutex_id); // ^^^ }
5236f62016-03-17Martin Karlgren  RXML.PCode cache_hit = evaled_content; evaled_content = 0; return ({cache_hit});
e1f6f62001-09-01Martin Stjernholm  }
53da902002-03-13Martin Stjernholm  }
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // Check for mutex synchronization during shared entry generation if (!mutex_key && args->shared) {
7863fa2013-11-04Jonas Walldén  if (args->mutex) { // We use the serialized keymap as the mutex ID so that // generation of unrelated entries in the same cache won't // block each other. Note that cache_id is already incorporated // into key. mutex_id = key;
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // Signal that we're about to enter mutex handling. This will // prevent any other thread from deallocating the same mutex // prematurely. // // vvv Relying on interpreter lock cache_mutex_concurrency[mutex_id]++; // ^^^
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // Find existing mutex or allocate a new one Thread.Mutex mtx = cache_mutexes[mutex_id]; lock_mutex: { if (!mtx) { // Prepare a new mutex and lock it before registering it so // the weak mapping will retain it. We'll swap in the mutex // atomically to avoid a race, and if we lose the race we // can discard it and continue with the old one. Thread.Mutex new_mtx = Thread.Mutex(); Thread.MutexKey new_key = new_mtx->lock();
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // vvv Relying on interpreter lock here if (!(mtx = cache_mutexes[mutex_id])) { // We're first so store our new mutex cache_mutexes[mutex_id] = new_mtx; // ^^^ mutex_key = new_key; break lock_mutex; } else { // Someone created it first so dispose of our prepared mutex // and carry on with the existing one. destruct(new_key); new_mtx = 0; } } mutex_key = mtx->lock(); }
c6bcb32013-11-04Martin Jonsson  id->add_threadbound_session_object (mutex_key);
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  retry_lookup = 1; }
53da902002-03-13Martin Stjernholm  }
317e302013-11-01Jonas Walldén  } while (retry_lookup);
4704942001-07-20Martin Stjernholm 
41a4da2001-07-26Martin Stjernholm  keymap += ([]);
4704942001-07-20Martin Stjernholm  do_iterate = 1;
a4963e2012-06-28Martin Stjernholm  persistent_cache = 0;
c747192005-01-26Martin Stjernholm  TAG_TRACE_ENTER ("cache miss%s for key %s, %s",
6ca1992002-03-19Martin Stjernholm  args->shared ?
ad963f2007-09-07Martin Stjernholm  (timeout ? " (shared " + timeout + "s timeout cache)" : " (shared cache)") : (timeout ? " (" + timeout + "s timeout cache)" : ""),
c747192005-01-26Martin Stjernholm  RXML.utils.format_short (keymap, 200),
53da902002-03-13Martin Stjernholm  removed == 1 ? "entry p-code is stale" : removed == 2 ? "entry had timed out" : removed == 3 ? "a pragma no-cache request removed the entry" :
c747192005-01-26Martin Stjernholm  "no matching entry");
980b792001-09-10Martin Stjernholm  id->cache_status->cachetag = 0;
4063572001-08-22Martin Stjernholm  id->misc->cache_tag_miss = 1;
4c38642001-02-10Martin Nilsson  return 0; }
4704942001-07-20Martin Stjernholm 
93bb682001-08-22Martin Stjernholm  array do_return (RequestID id)
4704942001-07-20Martin Stjernholm  {
41a4da2001-07-26Martin Stjernholm  if (key) {
5b138a2014-09-03Martin Karlgren  int key_updated;
41a4da2001-07-26Martin Stjernholm  mapping(string|int:mixed) subkeymap = RXML_CONTEXT->misc->cache_key;
92264c2001-09-13Martin Stjernholm  if (sizeof (subkeymap) > sizeof (keymap)) { // The test above assumes that no subtag removes entries in // RXML_CONTEXT->misc->cache_key.
053cd02002-03-12Martin Stjernholm  subvariables = filter (indices (subkeymap - keymap), stringp);
92264c2001-09-13Martin Stjernholm  // subvariables is part of the persistent state, but we'll
ab196f2002-10-14Martin Stjernholm  // come to state_update later anyway if it should be called. add_subvariables_to_keymap();
b53d122009-04-21Martin Stjernholm  make_key_from_keymap (id, timeout);
5b138a2014-09-03Martin Karlgren  key_updated = 1;
41a4da2001-07-26Martin Stjernholm  }
53da902002-03-13Martin Stjernholm 
93bb682001-08-22Martin Stjernholm  if (args->shared) {
e3ae852009-11-17Martin Stjernholm  if (object/*(RXML.PikeCompile)*/ comp = evaled_content->p_code_comp) { // Don't cache the PikeCompile objects. comp->compile(); evaled_content->p_code_comp = 0; }
1d7e872016-03-24Pontus Östlund 
52041e2013-11-01Jonas Walldén  object(RXML.PCode)|array(int|RXML.PCode|string) new_entry = level2_keys ? ({ 0, evaled_content, key_level2 }) : evaled_content; cache_set (cache_tag_eval_loc, key, new_entry, timeout);
ab196f2002-10-14Martin Stjernholm  TAG_TRACE_LEAVE ("added shared%s cache entry with key %s", timeout ? " timeout" : "", RXML.utils.format_short (keymap, 200));
93bb682001-08-22Martin Stjernholm  }
c6e2862007-09-18Martin Stjernholm  else {
64b0872014-06-26Martin Karlgren  if (object/*(RXML.PikeCompile)*/ comp = evaled_content->p_code_comp) comp->compile();
38364d2016-09-13Martin Karlgren  // Kludge to get a low cost in the RAM cache. Relatively // small but costly "RXML <cache> alternatives" entries may // otherwise "take over" the RAM cache. get_alternative (key);
41a4da2001-07-26Martin Stjernholm  if (timeout) {
53da902002-03-13Martin Stjernholm  if (args["persistent-cache"] == "yes") { persistent_cache = 1; RXML_CONTEXT->state_update(); }
64b0872014-06-26Martin Karlgren  set_alternative (key, ({ time() + timeout, evaled_content, key_level2 }),
5b138a2014-09-03Martin Karlgren  timeout, key_updated);
ad963f2007-09-07Martin Stjernholm  TAG_TRACE_LEAVE ("added%s %ds timeout cache entry with key %s",
ab196f2002-10-14Martin Stjernholm  persistent_cache ? " (possibly persistent)" : "",
ad963f2007-09-07Martin Stjernholm  timeout,
ab196f2002-10-14Martin Stjernholm  RXML.utils.format_short (keymap, 200));
41a4da2001-07-26Martin Stjernholm  }
c6e2862007-09-18Martin Stjernholm 
93bb682001-08-22Martin Stjernholm  else {
64b0872014-06-26Martin Karlgren  set_alternative (key, key_level2 ? ({ 0, evaled_content, key_level2 }) :
5b138a2014-09-03Martin Karlgren  evaled_content, UNDEFINED, key_updated);
3fe4ee2014-06-11Martin Karlgren 
53da902002-03-13Martin Stjernholm  if (args["persistent-cache"] != "no") { persistent_cache = 1; RXML_CONTEXT->state_update(); }
ab196f2002-10-14Martin Stjernholm  TAG_TRACE_LEAVE ("added%s cache entry with key %s", persistent_cache ? " (possibly persistent)" : "", RXML.utils.format_short (keymap, 200));
93bb682001-08-22Martin Stjernholm  }
c6e2862007-09-18Martin Stjernholm  }
41a4da2001-07-26Martin Stjernholm  }
93bb682001-08-22Martin Stjernholm  else
adf0262002-03-12Martin Stjernholm  TAG_TRACE_LEAVE ("");
53da902002-03-13Martin Stjernholm 
9c4d7f2002-10-10Martin Stjernholm  if (overridden_keymap) { RXML_CONTEXT->misc->cache_key = overridden_keymap; overridden_keymap = 0; }
5236f62016-03-17Martin Karlgren  evaled_content = 0;
4704942001-07-20Martin Stjernholm  result += content;
7619b92015-09-24Henrik Grubbström (Grubba)  return 0; } protected void cleanup() {
317e302013-11-01Jonas Walldén  if (mutex_key) { destruct(mutex_key);
1d7e872016-03-24Pontus Östlund 
317e302013-11-01Jonas Walldén  // Decrease parallel count for shared mutex. If we reach zero we // know no other thread depends on the same mutex so drop it from // global table. // // vvv Relying on interpreter lock if (!--cache_mutex_concurrency[mutex_id]) m_delete(cache_mutexes, mutex_id); // ^^^ and here vvv if (!cache_mutex_concurrency[mutex_id]) m_delete(cache_mutex_concurrency, mutex_id); // ^^^ }
4704942001-07-20Martin Stjernholm  }
41a4da2001-07-26Martin Stjernholm  array save() {
64b0872014-06-26Martin Karlgren  mapping(string:RXML.PCode|array(int|RXML.PCode)) persistent_alts;
c6e2862007-09-18Martin Stjernholm  if (alternatives) { if (persistent_cache) {
64b0872014-06-26Martin Karlgren  persistent_alts = ([]); // Get the entries so we can store them persistently. foreach (alternatives; string key;) { object(RXML.PCode)|array(int|RXML.PCode) entry =
32072f2014-07-01Martin Karlgren  peek_alternative (key); if (entry) {
64b0872014-06-26Martin Karlgren  persistent_alts[key] = entry;
32072f2014-07-01Martin Karlgren  }
c6e2862007-09-18Martin Stjernholm  } }
383ca12003-06-17Martin Stjernholm  }
c6e2862007-09-18Martin Stjernholm 
64b0872014-06-26Martin Karlgren  return ({cache_id, subvariables, persistent_cache, persistent_alts });
41a4da2001-07-26Martin Stjernholm  } void restore (array saved) {
64b0872014-06-26Martin Karlgren  mapping(string:RXML.PCode|array(int|RXML.PCode)) persistent_alts; [cache_id, subvariables, persistent_cache, persistent_alts] = saved; if (persistent_alts) { foreach (persistent_alts; string key; object|array entry) { int timeout; if (arrayp (entry) && entry[0]) { timeout = entry[0] - time(); if (timeout <= 0) continue; }
32072f2014-07-01Martin Karlgren 
64b0872014-06-26Martin Karlgren  // Put the persistently stored entries back into the RAM // cache. They might get expired over time (and hence won't // be encoded persistently if we're saved again), but then // they probably weren't that hot anyways. This method makes // sure we have control over memory usage.
f921172014-11-27Martin Karlgren 
0754d02016-11-14Martin Karlgren  // FIXME: get_alternative is a hack to get a low cost (since // it's reinstantiated from a persistent entry). Would be // better to save the original creation cost of the entry to // reuse here, but Roxen's cache doesn't have API's to get // or set entry cost currently. // However, we won't update existing entries in the RAM // cache, since they are likely more up-to-date then what we // get from the frame restore. Also, this avoids unnecessary // count_memory rounds. if (!get_alternative (key)) set_alternative (key, entry, timeout);
64b0872014-06-26Martin Karlgren  }
c6e2862007-09-18Martin Stjernholm  }
41a4da2001-07-26Martin Stjernholm  }
0cf2002010-11-30Martin Stjernholm  void exec_array_state_update() { RXML_CONTEXT->state_update(); }
32072f2014-07-01Martin Karlgren  void destroy() { // If our entries are stored persistently and the frame is // destructed we can free some memory in the RAM cache. The // entries will be restored from persistent storage by the // restore() function above, when the frame is reinstantiated. if (persistent_cache && alternatives) { foreach (alternatives; string key;) {
1a358c2017-04-05Anders Johansson  string full_key = get_full_key (key); call_out(cache_remove, 0, cache_tag_alts_loc, full_key);
32072f2014-07-01Martin Karlgren  } } }
4c38642001-02-10Martin Nilsson  }
4877d12009-11-12Martin Stjernholm  protected void create() {
e1e0f32016-12-22Jonas Walldén  cache.cache_register (cache_tag_eval_loc, 0, extend_entries_cache_prefs);
542b302017-03-22Jonas Walldén  cache.cache_register (cache_tag_alts_loc, 0, extend_entries_cache_prefs);
4877d12009-11-12Martin Stjernholm  }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
3a3a692001-08-07Martin Stjernholm class TagNocache { inherit RXML.Tag; constant name = "nocache";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_DONT_CACHE_RESULT;
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
3a3a692001-08-07Martin Stjernholm  class Frame { inherit RXML.Frame;
da0bba2009-04-16Martin Stjernholm  array do_enter (RequestID id) { if (args["enable-protocol-cache"]) ; else { NO_PROTO_CACHE(); if (!args["enable-client-cache"]) NOCACHE(); } return 0; }
3a3a692001-08-07Martin Stjernholm  } }
cde9a02000-03-10Martin Nilsson class TagCrypt { inherit RXML.Tag; constant name = "crypt"; class Frame { inherit RXML.Frame; array do_return(RequestID id) { if(args->compare) {
2182f52013-03-08Henrik Grubbström (Grubba)  _ok = verify_password(content,args->compare);
cde9a02000-03-10Martin Nilsson  return 0; }
2182f52013-03-08Henrik Grubbström (Grubba)  result = crypt_password(content);
cde9a02000-03-10Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
991e432011-10-31Martin Jonsson class TagHashHMAC { inherit RXML.Tag; constant name = "hash-hmac"; mapping(string:RXML.Type) req_arg_types = ([ "hash" : RXML.t_string(RXML.PEnt), "password" : RXML.t_string(RXML.PEnt), ]); class Frame { inherit RXML.Frame; void do_return(RequestID id) { string password = args->password; args->password = ""; object hash; hash = Crypto[upper_case(args->hash)] || Crypto[lower_case(args->hash)]; if (!hash) RXML.parse_error ("Unknown hash algorithm %O.", args->hash); result = String.string2hex(Crypto.HMAC(hash)(password)(content)); } } }
6444372000-03-10Martin Nilsson class TagFor { inherit RXML.Tag; constant name = "for";
4721b12002-08-13Martin Stjernholm  int flags = cache_static_in_2_5();
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
e272382000-01-23Kenneth Johansson 
6444372000-03-10Martin Nilsson  class Frame { inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
6444372000-03-10Martin Nilsson  private int from,to,step,count;
e272382000-01-23Kenneth Johansson 
6444372000-03-10Martin Nilsson  array do_enter(RequestID id) { from = (int)args->from; to = (int)args->to; step = (int)args->step!=0?(int)args->step:(to<from?-1:1);
5b22622005-06-22Martin Stjernholm  if((to<from && step>0)||(to>from && step<0)) { #if 0 // It's common that the limits are at the wrong side of each // other when no iteration should be done, so don't complain // about this.
916f4e2000-03-13Martin Nilsson  run_error("Step has the wrong sign.\n");
5b22622005-06-22Martin Stjernholm #endif } else from-=step;
6444372000-03-10Martin Nilsson  count=from; return 0; } int do_iterate() { if(!args->variable) {
6363112005-06-23Martin Stjernholm  int diff = (to - from) / step;
6444372000-03-10Martin Nilsson  to=from;
6363112005-06-23Martin Stjernholm  return diff > 0 && diff;
6444372000-03-10Martin Nilsson  } count+=step; RXML.user_set_var(args->variable, count, args->scope);
5b22622005-06-22Martin Stjernholm  if (step < 0) return count>=to;
6444372000-03-10Martin Nilsson  return count<=to; } array do_return(RequestID id) {
7efc1d2000-03-13Martin Nilsson  if(args->variable) RXML.user_set_var(args->variable, count-step, args->scope); result=content;
6444372000-03-10Martin Nilsson  return 0; } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson string simpletag_apre(string tag, mapping m, string q, RequestID id)
592ed72000-02-06Martin Nilsson {
0023a62000-08-22Martin Nilsson  string href;
e272382000-01-23Kenneth Johansson 
cc3a942000-09-24Martin Nilsson  if(m->href) { href=m_delete(m, "href");
0023a62000-08-22Martin Nilsson  array(string) split = href/":"; if ((sizeof(split) > 1) && (sizeof(split[0]/"/") == 1)) return RXML.t_xml->format_tag("a", m, q);
6743432000-03-19Martin Nilsson  href=Roxen.strip_prestate(Roxen.fix_relative(href, id));
592ed72000-02-06Martin Nilsson  }
cc3a942000-09-24Martin Nilsson  else href=Roxen.strip_prestate(Roxen.strip_config(id->raw_url));
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(!strlen(href)) href="";
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  multiset prestate = (< @indices(id->prestate) >);
e272382000-01-23Kenneth Johansson 
0023a62000-08-22Martin Nilsson  // FIXME: add and drop should handle t_array
cc3a942000-09-24Martin Nilsson  if(m->add) foreach((m_delete(m, "add") - " ")/",", string s)
592ed72000-02-06Martin Nilsson  prestate[s]=1;
cc3a942000-09-24Martin Nilsson  if(m->drop) foreach((m_delete(m,"drop") - " ")/",", string s)
592ed72000-02-06Martin Nilsson  prestate[s]=0;
cc3a942000-09-24Martin Nilsson 
6743432000-03-19Martin Nilsson  m->href = Roxen.add_pre_state(href, prestate);
0023a62000-08-22Martin Nilsson  return RXML.t_xml->format_tag("a", m, q);
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson string simpletag_aconf(string tag, mapping m, string q, RequestID id)
592ed72000-02-06Martin Nilsson {
0023a62000-08-22Martin Nilsson  string href;
e272382000-01-23Kenneth Johansson 
1e7eeb2001-10-01Martin Nilsson  if(m->href) {
cc3a942000-09-24Martin Nilsson  href=m_delete(m, "href");
592ed72000-02-06Martin Nilsson  if (search(href, ":") == search(href, "//")-1)
39271c2000-02-23Martin Stjernholm  RXML.parse_error("It is not possible to add configs to absolute URLs.\n");
1d7e872016-03-24Pontus Östlund  href=Roxen.fix_relative(href, id);
592ed72000-02-06Martin Nilsson  }
cc3a942000-09-24Martin Nilsson  else href=Roxen.strip_prestate(Roxen.strip_config(id->raw_url));
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  array cookies = ({});
0023a62000-08-22Martin Nilsson  // FIXME: add and drop should handle t_array
cc3a942000-09-24Martin Nilsson  if(m->add) foreach((m_delete(m,"add") - " ")/",", string s)
592ed72000-02-06Martin Nilsson  cookies+=({s});
cc3a942000-09-24Martin Nilsson  if(m->drop) foreach((m_delete(m,"drop") - " ")/",", string s)
592ed72000-02-06Martin Nilsson  cookies+=({"-"+s});
f1c9f02000-01-26Kenneth Johansson 
6743432000-03-19Martin Nilsson  m->href = Roxen.add_config(href, cookies, id->prestate);
0023a62000-08-22Martin Nilsson  return RXML.t_xml->format_tag("a", m, q);
592ed72000-02-06Martin Nilsson }
f1c9f02000-01-26Kenneth Johansson 
7078eb2001-05-18Martin Nilsson class TagMaketag { inherit RXML.Tag; constant name = "maketag"; mapping(string:RXML.Type) req_arg_types = ([ "type" : RXML.t_text(RXML.PEnt) ]); mapping(string:RXML.Type) opt_arg_types = ([ "noxml" : RXML.t_text(RXML.PEnt), "name" : RXML.t_text(RXML.PEnt) ]); class TagAttrib { inherit RXML.Tag; constant name = "attrib"; mapping(string:RXML.Type) req_arg_types = ([ "name" : RXML.t_text(RXML.PEnt) ]); class Frame { inherit RXML.Frame;
46b2c52009-03-05Martin Stjernholm  TagMaketag.Frame parent_frame;
0023a62000-08-22Martin Nilsson 
7078eb2001-05-18Martin Nilsson  array do_return(RequestID id) {
46b2c52009-03-05Martin Stjernholm  parent_frame->makeargs[args->name] = content || "";
7078eb2001-05-18Martin Nilsson  return 0; } } }
6534642001-06-28Martin Stjernholm  RXML.TagSet internal =
b5a4722009-04-15Martin Stjernholm  RXML.shared_tag_set (global::this, "maketag", ({ TagAttrib() }) );
7078eb2001-05-18Martin Nilsson  class Frame { inherit RXML.Frame; RXML.TagSet additional_tags = internal;
a4963e2012-06-28Martin Stjernholm  mapping(string:mixed) makeargs; array do_enter (RequestID id) { makeargs = ([]); }
0023a62000-08-22Martin Nilsson 
7078eb2001-05-18Martin Nilsson  array do_return(RequestID id) {
d5d5932005-02-18Martin Stjernholm  if (!content) content = "";
7078eb2001-05-18Martin Nilsson  switch(args->type) { case "pi": if(!args->name) parse_error("Type 'pi' requires a name attribute.\n"); result = RXML.t_xml->format_tag(args->name, 0, content, RXML.FLAG_PROC_INSTR); break; case "container": if(!args->name) parse_error("Type 'container' requires a name attribute.\n");
46b2c52009-03-05Martin Stjernholm  result = RXML.t_xml->format_tag(args->name, makeargs, content, RXML.FLAG_RAW_ARGS);
7078eb2001-05-18Martin Nilsson  break; case "tag": if(!args->name) parse_error("Type 'tag' requires a name attribute.\n");
46b2c52009-03-05Martin Stjernholm  result = RXML.t_xml->format_tag(args->name, makeargs, 0,
8fad902001-05-22Martin Nilsson  (args->noxml?RXML.FLAG_COMPAT_PARSE:0)| RXML.FLAG_EMPTY_ELEMENT|RXML.FLAG_RAW_ARGS);
7078eb2001-05-18Martin Nilsson  break; case "comment":
d5d5932005-02-18Martin Stjernholm  result = "<!--" + replace (replace (content, "--", "- -"), "--", "- -") + "-->";
7078eb2001-05-18Martin Nilsson  break; case "cdata":
b735572001-09-21Johan Sundström  result = "<![CDATA[" + content/"]]>"*"]]]]><![CDATA[>" + "]]>";
7078eb2001-05-18Martin Nilsson  break; } return 0; } }
592ed72000-02-06Martin Nilsson }
f1c9f02000-01-26Kenneth Johansson 
a7a5432000-04-15Per Hedbor class TagDoc { inherit RXML.Tag; constant name="doc"; RXML.Type content_type = RXML.t_same; class Frame { inherit RXML.Frame; array do_enter(RequestID id) {
7222a22000-06-12Martin Nilsson  if(args->preparse) content_type = result_type(RXML.PXml);
a7a5432000-04-15Per Hedbor  return 0;
592ed72000-02-06Martin Nilsson  }
a7a5432000-04-15Per Hedbor  array do_return(RequestID id) { array from; if(args->quote) { m_delete(args, "quote"); from=({ "<", ">", "&" }); } else from=({ "{", "}", "&" }); result=replace(content, from, ({ "&lt;", "&gt;", "&amp;"}) ); if(args->pre) { m_delete(args, "pre");
0023a62000-08-22Martin Nilsson  result="\n"+RXML.t_xml->format_tag("pre", args, result)+"\n";
a7a5432000-04-15Per Hedbor  } return 0;
592ed72000-02-06Martin Nilsson  }
a7a5432000-04-15Per Hedbor  }
592ed72000-02-06Martin Nilsson }
f1c9f02000-01-26Kenneth Johansson 
885a902005-09-15Martin Stjernholm class TagAutoformat { inherit RXML.Tag;
f1c9f02000-01-26Kenneth Johansson 
885a902005-09-15Martin Stjernholm  constant name = "autoformat";
e272382000-01-23Kenneth Johansson 
885a902005-09-15Martin Stjernholm  class Frame { inherit RXML.Frame; array do_return(RequestID id) { string s=content || ""; s-="\r"; if (s == "") return ({""}); if(!args->nonbsp) { s = replace(s, "\n ", "\n&nbsp;"); // "|\n |" => "|\n&nbsp;|" s = replace(s, " ", "&nbsp; "); // "| |" => "|&nbsp; |" s = replace(s, " ", " &nbsp;"); // "|&nbsp; |" => "|&nbsp; &nbsp;|" } string dbl_nl;
e272382000-01-23Kenneth Johansson 
885a902005-09-15Martin Stjernholm  if(!args->nobr) { s = replace(s, "\n", "<br />\n"); dbl_nl = "<br />\n<br />\n"; }
592ed72000-02-06Martin Nilsson  else
885a902005-09-15Martin Stjernholm  dbl_nl = "\n\n";
9a93ab2005-09-15Martin Stjernholm 
885a902005-09-15Martin Stjernholm  if(args->p) {
9a93ab2005-09-15Martin Stjernholm  s = replace (s, dbl_nl, "\n<p>\n"); // Now fix balanced <p> tags. string ptag = args["class"]?"<p class=\""+args["class"]+"\"":"<p"; int got_toplevel_data = 0; Parser.HTML p = Roxen.get_xml_parser(); p->add_container ( "p", lambda (Parser.HTML p, mapping a, string c) { string ender = got_toplevel_data ? "</p>" : ""; got_toplevel_data = 0; return ({ender, ptag, Roxen.make_tag_attributes (a), c == "" ? " />" : ">" + c + "</p>"}); }); p->_set_data_callback ( lambda (Parser.HTML p, string c) { if (!got_toplevel_data && sscanf (c, "%*[ \t\n]%*c") == 2) { got_toplevel_data = 1; return ({ptag, ">", c}); } return ({c}); }); s = p->finish (s)->read();
66e1732009-11-20Martin Stjernholm  p = 0; // Avoid trampoline garbage.
9a93ab2005-09-15Martin Stjernholm  if (got_toplevel_data) s += "</p>";
885a902005-09-15Martin Stjernholm  }
9a93ab2005-09-15Martin Stjernholm 
885a902005-09-15Martin Stjernholm  return ({ s });
592ed72000-02-06Martin Nilsson  }
885a902005-09-15Martin Stjernholm  }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
fc40392008-08-15Martin Stjernholm class Smallcapsstr (string bigtag, string smalltag, mapping bigarg, mapping smallarg)
cc3a942000-09-24Martin Nilsson {
592ed72000-02-06Martin Nilsson  constant UNDEF=0, BIG=1, SMALL=2;
fc40392008-08-15Martin Stjernholm  protected string text="",part=""; protected int last=UNDEF;
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  string _sprintf() {
cc3a942000-09-24Martin Nilsson  return "Smallcapsstr("+bigtag+","+smalltag+")";
592ed72000-02-06Martin Nilsson  }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  void add(string char) { part+=char; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  void add_big(string char) { if(last!=BIG) flush_part(); part+=char; last=BIG; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  void add_small(string char) { if(last!=SMALL) flush_part(); part+=char; last=SMALL; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  void write(string txt) { if(last!=UNDEF) flush_part(); part+=txt; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  void flush_part() { switch(last){ case UNDEF: default: text+=part; break; case BIG:
0023a62000-08-22Martin Nilsson  text+=RXML.t_xml->format_tag(bigtag, bigarg, part);
592ed72000-02-06Martin Nilsson  break; case SMALL:
0023a62000-08-22Martin Nilsson  text+=RXML.t_xml->format_tag(smalltag, smallarg, part);
592ed72000-02-06Martin Nilsson  break; } part=""; last=UNDEF; }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  string value() { if(last!=UNDEF) flush_part(); return text; } }
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson string simpletag_smallcaps(string t, mapping m, string s)
592ed72000-02-06Martin Nilsson { Smallcapsstr ret; string spc=m->space?"&nbsp;":""; m_delete(m, "space"); mapping bm=([]), sm=([]); if(m["class"] || m->bigclass) { bm=(["class":(m->bigclass||m["class"])]); m_delete(m, "bigclass"); } if(m["class"] || m->smallclass) { sm=(["class":(m->smallclass||m["class"])]); m_delete(m, "smallclass"); }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  if(m->size) { bm+=(["size":m->size]); if(m->size[0]=='+' && (int)m->size>1) sm+=(["size":m->small||"+"+((int)m->size-1)]); else sm+=(["size":m->small||(string)((int)m->size-1)]); m_delete(m, "small"); ret=Smallcapsstr("font","font", m+bm, m+sm); } else ret=Smallcapsstr("big","small", m+bm, m+sm);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  for(int i=0; i<strlen(s); i++) if(s[i]=='<') { int j; for(j=i; j<strlen(s) && s[j]!='>'; j++); ret->write(s[i..j]); i+=j-1; } else if(s[i]<=32) ret->add_small(s[i..i]); else if(lower_case(s[i..i])==s[i..i]) ret->add_small(upper_case(s[i..i])+spc); else if(upper_case(s[i..i])==s[i..i]) ret->add_big(s[i..i]+spc); else ret->add(s[i..i]+spc);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  return ret->value(); }
e272382000-01-23Kenneth Johansson 
fe9f932000-09-12Johan Sundström string simpletag_random(string tag, mapping m, string s, RequestID id)
592ed72000-02-06Martin Nilsson {
fe9f932000-09-12Johan Sundström  NOCACHE();
f67b332005-03-17Fredrik Noring  if(m->range) return (string)random((int)m->range);
b50d882001-02-11Martin Nilsson  array q = s/(m->separator || m->sep || "\n"); int index; if(m->seed)
4e32072009-01-09Stephen R. van den Berg  index = array_sscanf(Crypto.MD5.hash(m->seed),
b50d882001-02-11Martin Nilsson  "%4c")[0]%sizeof(q); else index = random(sizeof(q)); return q[index];
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
916f4e2000-03-13Martin Nilsson class TagGauge { inherit RXML.Tag; constant name = "gauge"; class Frame { inherit RXML.Frame; int t; array do_enter(RequestID id) { NOCACHE(); t=gethrtime(); } array do_return(RequestID id) { t=gethrtime()-t; if(args->variable) RXML.user_set_var(args->variable, t/1000000.0, args->scope); if(args->silent) return ({ "" }); if(args->timeonly) return ({ sprintf("%3.6f", t/1000000.0) }); if(args->resultonly) return ({content}); return ({ "<br /><font size=\"-1\"><b>Time: "+ sprintf("%3.6f", t/1000000.0)+ " seconds</b></font><br />"+content }); } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson // Removes empty lines
82f7132000-03-10Martin Nilsson string simpletag_trimlines( string tag_name, mapping args,
592ed72000-02-06Martin Nilsson  string contents, RequestID id ) {
82f7132000-03-10Martin Nilsson  contents = replace(contents, ({"\r\n","\r" }), ({"\n","\n"})); return (contents / "\n" - ({ "" })) * "\n";
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson void container_throw( string t, mapping m, string c, RequestID id) {
64bfd32000-03-25Martin Nilsson  if(c[-1]!='\n') c+="\n";
9ceb8b2000-09-25Per Hedbor  throw( class(string tag_throw) {}( c ) );
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson // Internal methods for the default tag private int|array internal_tag_input(string t, mapping m, string name, multiset(string) value) { if (name && m->name!=name) return 0; if (m->type!="checkbox" && m->type!="radio") return 0; if (value[m->value||"on"]) { if (m->checked) return 0; m->checked = "checked"; } else { if (!m->checked) return 0; m_delete(m, "checked" ); }
e272382000-01-23Kenneth Johansson 
cc3a942000-09-24Martin Nilsson  int xml=!m_delete(m, "noxml");
0023a62000-08-22Martin Nilsson  return ({ Roxen.make_tag(t, m, xml) });
592ed72000-02-06Martin Nilsson }
7340df2014-06-12Martin Karlgren  private int|array internal_tag_select(string t, mapping m, string c, string name, multiset(string) value)
592ed72000-02-06Martin Nilsson {
0023a62000-08-22Martin Nilsson  if(name && m->name!=name) return ({ RXML.t_xml->format_tag(t, m, c) });
c4de052000-05-05Martin Nilsson 
c904a92014-06-13Martin Karlgren  string cur_tag; mapping(string:mixed) cur_args; string cur_data = ""; string finish_tag() { string _cur_tag = cur_tag; cur_tag = 0; mapping(string:mixed) _cur_args = cur_args || ([]); cur_args = 0; string _cur_data = cur_data; cur_data = ""; if (!_cur_args->selected && value[_cur_data]) _cur_args->selected = "selected"; if (_cur_tag) return RXML.t_xml->format_tag (_cur_tag, _cur_args, _cur_data); return _cur_data; }; array process_tag (Parser.HTML p, mapping args) { string res = ""; string tag_name = p->tag_name(); m_delete (args, "/"); // Self-closed tag. if (tag_name[-1] == '/') tag_name = tag_name[..<1]; res = finish_tag(); if (tag_name[0] != '/') { cur_tag = tag_name; if (value[args->value]) args->selected = "selected"; else m_delete (args, "selected"); cur_args = args; } return ({ res }); }; Parser.HTML parser = Parser.HTML(); parser->xml_tag_syntax(0); // Register opening, closing and self-closed tags directly rather // than using add_container to be able to handle some odd cases in // the testsuite (opening tags without closing tags, but with // content, etc.) parser->add_tag ("option", process_tag); parser->add_tag ("/option", process_tag); parser->add_tag ("option/", process_tag); parser->_set_data_callback (lambda (Parser.HTML p, string c) { cur_data += c; return ""; }); parser->ignore_unknown (1); parser->case_insensitive_tag (1); string res = parser->finish(c)->read() + finish_tag();
d354012017-10-24Martin Karlgren  parser = 0; // Avoid trampoline garbage through callback functions.
c904a92014-06-13Martin Karlgren  return ({ RXML.t_xml->format_tag (t, m, res) });
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson string simpletag_default( string t, mapping m, string c, RequestID id)
592ed72000-02-06Martin Nilsson { multiset value=(<>);
6b06da2010-03-30Stephen R. van den Berg  if(m->value) value=mkmultiset(((string)m->value)/(m->separator||","));
cde9a02000-03-10Martin Nilsson  if(m->variable) value+=(<RXML.user_get_var(m->variable, m->scope)>);
82f7132000-03-10Martin Nilsson  if(value==(<>)) return c;
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson  return parse_html(c, (["input":internal_tag_input]), (["select":internal_tag_select]), m->name, value);
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
82f7132000-03-10Martin Nilsson string simpletag_sort(string t, mapping m, string c, RequestID id)
592ed72000-02-06Martin Nilsson { if(!m->separator) m->separator = "\n";
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  string pre="", post=""; array lines = c/m->separator;
e272382000-01-23Kenneth Johansson 
95c6342010-01-22Martin Stjernholm  while(sizeof (lines) && lines[0] == "")
592ed72000-02-06Martin Nilsson  { pre += m->separator; lines = lines[1..]; }
e272382000-01-23Kenneth Johansson 
95c6342010-01-22Martin Stjernholm  while(sizeof (lines) && lines[-1] == "")
592ed72000-02-06Martin Nilsson  { post += m->separator;
95c6342010-01-22Martin Stjernholm  lines = lines[..<1];
592ed72000-02-06Martin Nilsson  }
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  lines=sort(lines);
e272382000-01-23Kenneth Johansson 
592ed72000-02-06Martin Nilsson  return pre + (m->reverse?reverse(lines):lines)*m->separator + post; }
e272382000-01-23Kenneth Johansson 
c757602004-03-25Martin Stjernholm class TagReplace
592ed72000-02-06Martin Nilsson {
c757602004-03-25Martin Stjernholm  inherit RXML.Tag; constant name = "replace"; class Frame
592ed72000-02-06Martin Nilsson  {
c757602004-03-25Martin Stjernholm  inherit RXML.Frame;
e272382000-01-23Kenneth Johansson 
c757602004-03-25Martin Stjernholm  array do_return (RequestID id) {
66ebf82006-06-16Marcus Wellhardh  if (content) { if (result_type->decode_charrefs && compat_level == 4.0) content = result_type->decode_charrefs (content);
1d7e872016-03-24Pontus Östlund 
66ebf82006-06-16Marcus Wellhardh  else if (result_type->decode_xml_safe_charrefs && compat_level > 4.0) content = result_type->decode_xml_safe_charrefs (content); }
e272382000-01-23Kenneth Johansson 
c757602004-03-25Martin Stjernholm  if (!args->from) result = content;
e272382000-01-23Kenneth Johansson 
5811802004-04-19Martin Stjernholm  else {
667a5b2004-09-21Marcus Wellhardh  if (content == RXML.nil) content = "";
1d7e872016-03-24Pontus Östlund 
c757602004-03-25Martin Stjernholm  switch(args->type) { case "word": default:
5d3c512005-01-26Stefan Wallström  if(args->first || args->last) { string res=""; int first = (int)args->first; int last = (int)args->last; array a = content / args->from; for(int i=0; i< sizeof(a); i++) { res += a[i]; if(i != sizeof(a)-1) { if((first && (i+i) <= first) || (last && sizeof(a)-i-1 <= last)) res += (args->to || ""); else res += args->from; } } result = res; } else result = replace(content,args->from,(args->to?args->to:""));
c757602004-03-25Martin Stjernholm  break; case "words": string s=args->separator?args->separator:","; array from=(array)(args->from/s); array to=(array)(args->to/s); int balance=sizeof(from)-sizeof(to); if(balance>0) to+=allocate(balance,""); else if (balance < 0) parse_error ("There are more elements in the \"to\" list (%d) " "than in \"from\" (%d).", sizeof (to), sizeof (from)); result = replace(content,from,to); break; }
5811802004-04-19Martin Stjernholm  if (result_type->entity_syntax) result = replace (result, "\0", "&#0;"); }
c757602004-03-25Martin Stjernholm  }
592ed72000-02-06Martin Nilsson  } }
e272382000-01-23Kenneth Johansson 
dc1c4b2008-09-15Martin Stjernholm class TagSubstring
d8767e2005-09-01 Erik Dahl { inherit RXML.Tag;
dc1c4b2008-09-15Martin Stjernholm  constant name = "substring";
d8767e2005-09-01 Erik Dahl  mapping(string:RXML.Type) opt_arg_types = ([
dc1c4b2008-09-15Martin Stjernholm  "from": RXML.t_int (RXML.PEnt), "to": RXML.t_int (RXML.PEnt), "index": RXML.t_int (RXML.PEnt),
d8767e2005-09-01 Erik Dahl  ]);
dc1c4b2008-09-15Martin Stjernholm  RXML.Type content_type = RXML.t_any_text (RXML.PXml);
243ed52008-09-16Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
dc1c4b2008-09-15Martin Stjernholm  constant flags = RXML.FLAG_DONT_RECOVER; class Frame {
d8767e2005-09-01 Erik Dahl  inherit RXML.Frame;
dc1c4b2008-09-15Martin Stjernholm  array do_enter (RequestID id) {
16461a2008-11-01Martin Stjernholm  if (result_type == RXML.t_string) // Let's propagate t_string in favor of the default t_any_text. content_type = result_type (RXML.PXml); else if (result_type == RXML.t_array) { if (args->join || !(args->separator || args["separator-chars"] || args["separator-whites"])) // Cannot return an array if there's a join attribute or no // separator attribute. result_type = RXML.t_string;
dc1c4b2008-09-15Martin Stjernholm  } else
16461a2008-11-01Martin Stjernholm  result_type = RXML.t_string;
dc1c4b2008-09-15Martin Stjernholm  return 0; } #define IS_COMPL_SET(SET) \ (has_prefix ((SET), "^") && (!has_prefix ((SET), "^-") || (SET) == "^-")) protected string compl_sscanf_set (string set) { return IS_COMPL_SET (set) ? set[1..] : "^" + set; } protected void set_format_error (string attr, mixed err) { #ifdef DEBUG parse_error ("Error in %O format: %s", attr, describe_error (err)); #else parse_error ("Error in %O format.\n", attr); #endif } protected array(array(string)) split_on_set (string str, string set) // Returns ({v, s}) where v has the field values and s has the // separator(s) following each field in v. { #ifdef DEBUG if (set == "") error ("The set cannot be empty.\n"); #endif array(array(string)) res; string compl_set = compl_sscanf_set (set); string tail; if (mixed err = catch { sscanf (str, "%{%[" + compl_set + "]%1[" + set + "]%}%s", res, tail); }) set_format_error ("separator-chars", err); if (tail != "") // The %{...%} bit won't match the last field because of // the length restriction on the sep_chars pattern. res += ({({tail, ""})});
1d811b2010-04-30Martin Stjernholm  if (sizeof (res)) return Array.transpose (res); else return ({({}), ({})});
dc1c4b2008-09-15Martin Stjernholm  } protected array(array(string)) split_on_set_seq (string str, string set) // Note that only the first field value might be empty. { #ifdef DEBUG if (set == "") error ("The set cannot be empty.\n"); #endif array(array(string)) res; string compl_set = compl_sscanf_set (set); if (mixed err = catch { sscanf (str, "%{%[" + compl_set + "]%[" + set + "]%}", res); }) set_format_error ("separator-chars", err);
1d811b2010-04-30Martin Stjernholm  if (sizeof (res)) return Array.transpose (res); else return ({({}), ({})});
dc1c4b2008-09-15Martin Stjernholm  } array do_return (RequestID id) { int beg, end;
885a902005-09-15Martin Stjernholm  if (!zero_type (args->index)) {
dc1c4b2008-09-15Martin Stjernholm  if (args->from || args->to) parse_error ("\"index\" attribute cannot be used " "together with \"from\" or \"to\".\n"); if (args->after || args->before) parse_error ("\"index\" attribute cannot be used " "together with \"after\" or \"before\".\n"); beg = end = args->index;
51756a2009-04-06Jonas Wallden  if (!beg) parse_error ("\"index\" cannot be zero.\n");
dc1c4b2008-09-15Martin Stjernholm  } else { beg = args->from;
51756a2009-04-06Jonas Wallden  if (!beg && !zero_type (beg)) parse_error ("\"from\" cannot be zero.\n");
dc1c4b2008-09-15Martin Stjernholm  end = args->to;
51756a2009-04-06Jonas Wallden  if (!end && !zero_type (end)) parse_error ("\"to\" cannot be zero.\n");
dc1c4b2008-09-15Martin Stjernholm  }
885a902005-09-15Martin Stjernholm 
dc1c4b2008-09-15Martin Stjernholm  if (content == RXML.nil) content = ""; string search_str = content; string after = args->after; string before = args->before; string sep = args->separator; string sep_chars = args["separator-chars"]; string trim_chars = args["trim-chars"]; string joiner = args->join; int ignore_empty = !!args["ignore-empty"]; int trimwhites = !!args->trimwhites; if (args["separator-whites"]) { if (!sep_chars) sep_chars = " \t\n\r"; else if (IS_COMPL_SET (sep_chars)) parse_error ("Cannot combine \"separator-whites\" " "with a complemented set in \"separator-chars\".\n"); else if (has_suffix (sep_chars, "-")) sep_chars = sep_chars[..<1] + " \t\n\r-"; else sep_chars += " \t\n\r"; ignore_empty = 1; } if (joiner) { if (!sep && !sep_chars) parse_error ("\"join\" only useful together with " "\"separator\", \"separator-chars\", " "or \"separator-whites\".\n"); } else joiner = sep; function(string,string:array(array(string))) char_sep_fn; if (sep_chars) { if (sep) parse_error ("\"separator\" and \"separator-chars\"/" "\"separator-whites\" cannot be used together.\n"); if (!joiner) joiner = IS_COMPL_SET (sep_chars) ? "" : sep_chars[..0]; if (sep_chars == "") parse_error ("\"separator-chars\" cannot be empty.\n"); else { if (ignore_empty) char_sep_fn = split_on_set_seq; else char_sep_fn = split_on_set;
d8767e2005-09-01 Erik Dahl  } }
dc1c4b2008-09-15Martin Stjernholm  if (args["case-insensitive"]) { search_str = lower_case (search_str); if (after) after = lower_case (after); if (before) before = lower_case (before); if (sep) sep = lower_case (sep); else if (sep_chars) { // NB: This can make the set invalid in odd cases like // "Z-a". We ignore that. sep_chars = lower_case (sep_chars); } if (trim_chars) trim_chars = lower_case (trim_chars); // Same here. } if (after && !beg) beg = 1; if (before && !end) end = 1; // Zero based character positions into search_str and content. // -1 = undecided, -2 = calculate from beg_split using split and // split_sep. int beg_pos = beg && -1; int end_pos = end ? -1 : Int.NATIVE_MAX; // last char + 1. // If we need to split search_str for some reason, split is the // result and split_str is the split string used. split_str is // zero if search_str is split on sep_chars. array(string) split; string split_str; // Set if split is, and has the same length. Each element is the // separator(s) following the corresponding field in split. // Useful for sep_chars, but always set to avoid special cases. array(string) split_seps; // Like beg_pos and end_pos, but expressed as positions in the // split array. int beg_split = beg && -1, end_split = end ? -1 : Int.NATIVE_MAX; function(string:string) trim_for_empty; if (ignore_empty) { if (!sep && !sep_chars) parse_error ("\"ignore-empty\" only useful together with " "\"separator\", \"separator-chars\", " "or \"separator-whites\".\n"); if (trimwhites) // Check for both trimwhites and trim-chars at the same time // is done later. trim_for_empty = String.trim_all_whites; else if (trim_chars) trim_for_empty = lambda (string trim) { // This outer lambda doesn't access anything in the // surrounding function frame, thereby avoiding garbage. string fmt = "%*[" + trim + "]%s"; return lambda (string s) { if (mixed err = catch (sscanf (s, fmt, s))) set_format_error ("trim-chars", err); return s; }; } (trim_chars); else if (char_sep_fn == split_on_set_seq) { // No need for extra trimming in this case since // split_on_set_seq handles it internally, except for the // case when separators occur before the first field. That // requires some special care when counting by elements // from the start. }
d8767e2005-09-01 Erik Dahl  else
dc1c4b2008-09-15Martin Stjernholm  trim_for_empty = lambda (string s) {return s;}; } // Optimize by skipping past the beginning of the input string // using search(), or index directly on character position. string beg_skip_str; { string s = after || sep; if (s && s != "") { if (beg > 0) { beg_pos = 0; int i = beg; if (!after && !--i) { // Using sep - first match is at the beginning of the string. } else { if (!trim_for_empty || // Trimming doesn't affect after="..." splitting. after) do { int p = search (search_str, s, beg_pos); if (p < 0) {beg_pos = Int.NATIVE_MAX; break;} beg_pos = p + sizeof (s); } while (--i > 0); else while (1) { int p = search (search_str, s, beg_pos); if (p < 0) {beg_pos = Int.NATIVE_MAX; break;} if (beg_pos != p && trim_for_empty (search_str[beg_pos..p - 1]) != "") // Continue looping when i == 0 to skip empty fields // after the last nonempty match. if (--i < 0) break; beg_pos = p + sizeof (s); } beg_skip_str = s; } } } else if (!sep_chars) { // Index by character position. if (beg > 0) beg_pos = beg - 1; else if (beg < 0) beg_pos = max (sizeof (search_str) + beg, 0);
d8767e2005-09-01 Erik Dahl  } }
dc1c4b2008-09-15Martin Stjernholm  end_skip: { string s = before || sep; if (s && s != "") { if (end > 0) { int i; if (s == beg_skip_str) { // Optimize by continuing the search where beg_skip left off. i = end - beg; if (sep) i++; if (i <= 0 || beg_pos == Int.NATIVE_MAX) { end_pos = end_split = 0; break end_skip; } end_pos = beg_pos - sizeof (s); } else { i = end; end_pos = -sizeof (s); } if (before == after) // True also when only sep is set. // Set end_split in case split is created in the beg_pos == -1 // branch below. end_split = end; if (!trim_for_empty || // Trimming doesn't affect before="..." splitting. before) do { int p = search (search_str, s, end_pos + sizeof (s)); if (p < 0) {end_pos = Int.NATIVE_MAX; break;} end_pos = p; } while (--i > 0); else { int num_empty = 0; while (1) { int b = end_pos + sizeof (s); int p = search (search_str, s, b); if (p < 0) {end_pos = Int.NATIVE_MAX; break;} end_pos = p; if (b != p && trim_for_empty (search_str[b..p - 1]) != "") { if (--i <= 0) break; } else num_empty++; } if (end_split >= 0) end_split += num_empty; } } } else if (!sep_chars) { // Index by character position. if (end > 0) end_pos = end; else if (end < 0) end_pos = max (sizeof (search_str) + end + 1, 0); } } // Find beg_pos and end_pos in the remaining cases. beg_search: if (beg_pos == -1) { if (string s = after || sep) { // Some simple testing shows that splitting performs best // overall, compared to using search() in different ways. // Could be improved a lot if we got an rsearch(). split = search_str / s; split_str = s; split_seps = allocate (sizeof (split) - 1, s) + ({""}); } else { #ifdef DEBUG if (!sep_chars) error ("Code flow bonkers.\n"); #endif [split, split_seps] = char_sep_fn (search_str, sep_chars); if (beg >= 0) { if (!trim_for_empty) { beg_split = beg - 1; if (char_sep_fn == split_on_set_seq) // split_on_set_seq handles the trimming for us, // except it might leave an empty field in front of // the first one. Compensate for it. if (sizeof (split) && split[0] == "") beg_split++; beg_pos = -2; } else { int b = beg - 1, i = 0; beg_pos = 0; while (1) { if (i >= sizeof (split)) { beg_split = beg_pos = Int.NATIVE_MAX; break beg_search; } // Allow i to go past b as long as split[i] is empty, // so that we skip empty fields just after the last // nonempty one. if (trim_for_empty (split[i]) == "") b++; else if (i >= b) break; beg_pos += sizeof (split[i]) + sizeof (split_seps[i]); i++; } beg_split = i; } break beg_search; } } #ifdef DEBUG if (beg >= 0) error ("Expecting only count-from-the-end cases here.\n"); #endif if (beg < -sizeof (split)) beg_pos = beg_split = 0; else { int chars_from_end = 0; if (!trim_for_empty || // Trimming doesn't affect after="..." splitting. after) { beg_split = sizeof (split) + beg; for (int i = -1; i >= beg; i--) chars_from_end += sizeof (split[i]) + sizeof (split_seps[i]); } else { int b = beg, i = -1; do { chars_from_end += sizeof (split[i]) + sizeof (split_seps[i]); if (trim_for_empty (split[i]) == "") if (--b < -sizeof (split)) { beg_pos = beg_split = 0; break beg_search; } } while (--i >= b); beg_split = sizeof (split) + b; } beg_pos = sizeof (search_str) - chars_from_end; #ifdef DEBUG if (beg_pos < 0) error ("Ouch! %O %O %O\n", sizeof (search_str), chars_from_end, beg); #endif } } #ifdef DEBUG if (split && beg_split == -1) error ("Failed to set beg_split.\n"); #endif end_search: if (end_pos == -1) { int e = end; if (!before && !++e) // Using sep or sep_chars - last match is at the end of the string. end_pos = end_split = Int.NATIVE_MAX; else { string ss; array(string) sp, sps; int bias; if (string s = before || sep) { sp = s == split_str && split; if (sp) { ss = search_str; sps = split_seps; } else { // Since we count from the end we can optimize by // chopping off the part before beg_pos first. ss = search_str[beg_pos..]; bias = beg_pos; sp = ss / s; sps = allocate (sizeof (sp) - 1, s) + ({""}); if (!before && !beg_pos) { // Want to save it if we're splitting on sep. split = sp; split_str = s; split_seps = sps; } } } else { #ifdef DEBUG if (!sep_chars) error ("Code flow bonkers.\n"); #endif if (!split) [split, split_seps] = char_sep_fn (search_str, sep_chars); if (end >= 0) { if (!trim_for_empty) { end_split = end; if (char_sep_fn == split_on_set_seq) // split_on_set_seq handles the trimming for us, // except it might leave an empty field in front of // the first one. Compensate for it. if (sizeof (split) && split[0] == "") end_split++; end_pos = -2; } else { e = end; end_pos = 0; for (int i = 0; i < e; i++) { if (i >= sizeof (split)) { end_split = end_pos = Int.NATIVE_MAX; break end_search; } if (trim_for_empty (split[i]) == "") e++; end_pos += sizeof (split[i]) + sizeof (split_seps[i]); } end_pos -= sizeof (split_seps[e - 1]); end_split = e; } break end_search; } else { ss = search_str; sp = split; sps = split_seps; } } #ifdef DEBUG if (end >= 0) error ("Expecting only count-from-the-end cases here.\n"); #endif if (e <= -sizeof (sp)) end_pos = end_split = 0; else { int chars_from_end; if (!trim_for_empty || // Trimming doesn't affect before="..." splitting. before) { if (sp == split) end_split = sizeof (split) + e; chars_from_end = sizeof (sps[e - 1]); for (; e < 0; e++) chars_from_end += sizeof (sp[e]) + sizeof (sps[e]); } else { int i = -1; while (1) { // Allow i to go past e as long as sp[i] is empty, // so that we skip empty fields just before the last // nonempty one. if (trim_for_empty (sp[i]) == "") e--; else if (i < e) break; chars_from_end += sizeof (sp[i]) + sizeof (sps[i]); if (--i < -sizeof (sp)) { end_pos = end_split = 0; break end_search; } } if (sp == split) end_split = sizeof (split) + i + 1; } end_pos = sizeof (ss) - chars_from_end; #ifdef DEBUG if (end_pos < 0) error ("Ouch! %O %O %O %O %O\n", sizeof (search_str), chars_from_end, beg, beg_pos, bias); #endif end_pos += bias; } } } #ifdef DEBUG if (beg_pos == -1) error ("Failed to set beg_pos.\n"); else if (beg_pos == -2) { if (beg_split == -1) error ("Failed to set beg_split.\n"); } else if (beg_pos < 0) error ("Invalid beg_pos %d.\n", beg_pos); if (beg_split < -1) error ("Invalid beg_split %d.\n", beg_split); if (end_pos == -1) error ("Failed to set end_pos.\n"); else if (end_pos == -2) { if (end_split == -1) error ("Failed to set end_split.\n"); } else if (end_pos < 0) error ("Invalid end_pos %d.\n", end_pos); if (end_split < -1) error ("Invalid end_split %d.\n", end_split); #endif // Got beg_pos and end_pos. Now finish up. function(string:string) trimmer; if (trimwhites) { if (trim_chars) parse_error ("\"trimwhites\" and \"trim-chars\" " "cannot be used at the same time.\n"); trimmer = String.trim_all_whites; } else if (trim_chars) { if (args["case-insensitive"]) // NB: This can make the trim format invalid in odd cases // like "Z-a". We ignore that. trimmer = lambda (string trim) { // This outer lambda doesn't access anything in the // surrounding function frame, thereby avoiding garbage. string fmt = "%[" + lower_case (trim) + "]"; return lambda (string s) { string t, lc = lower_case (s); if (mixed err = catch (sscanf (lc, fmt, t))) set_format_error ("trim-chars", err); int b = sizeof (t); sscanf (reverse (lc), fmt, t); return s[b..<sizeof (t)]; }; } (trim_chars); else trimmer = lambda (string trim) { string fmt1 = "%*[" + trim + "]%s", fmt2 = "%[" + trim + "]"; return lambda (string s) { if (mixed err = catch (sscanf (s, fmt1, s))) set_format_error ("trim-chars", err); sscanf (reverse (s), fmt2, string t); return s[..<sizeof (t)]; }; } (trim_chars); }
16461a2008-11-01Martin Stjernholm  if (result_type == RXML.t_array || sep_chars ||
dc1c4b2008-09-15Martin Stjernholm  (sep && (trim_chars || trimwhites || ignore_empty || joiner != sep || search_str != content))) { // Need to do things that require a split into an array.
16461a2008-11-01Martin Stjernholm #ifdef DEBUG if (result_type == RXML.t_array) { // do_enter should make sure the following never happens.
dc1c4b2008-09-15Martin Stjernholm  if (args->join)
16461a2008-11-01Martin Stjernholm  error ("Unexpected join attribute in array context.\n");
dc1c4b2008-09-15Martin Stjernholm  if (!sep && !sep_chars)
16461a2008-11-01Martin Stjernholm  error ("Unexpected array context without separator attribute.\n");
dc1c4b2008-09-15Martin Stjernholm  }
16461a2008-11-01Martin Stjernholm #endif
dc1c4b2008-09-15Martin Stjernholm  #if 0 werror ("split %O, split_str %O, beg %O/%O, end %O/%O\n", !!split, split_str, beg_split, beg_pos, end_split, end_pos); #endif if (split) { #ifdef DEBUG if (sep && split_str != sep) error ("Didn't expect a split on %O when sep is %O.\n", split_str, sep); #endif if (beg_split == -1 || end_split == -1 || search_str != content || // If sep is used then split_str must be the sep string. // If sep isn't set then sep_chars is used and split_str // must be zero. split_str != sep) { // Can't use the split array created earlier. #if 0 werror ("Ditching split for args %O\n", args); #endif if (beg_pos == -2) { beg_pos = 0; for (int i = 0; i < beg_split; i++) beg_pos += sizeof (split[i]) + sizeof (split_seps[i]); } if (end_pos == -2) { end_pos = sizeof (search_str); int i = sizeof (split); while (--i >= end_split) end_pos -= sizeof (split[i]) + sizeof (split_seps[i]); if (i >= 0) end_pos -= sizeof (split_seps[i]); } split = 0; } } if (split) split = split[beg_split..end_split - 1]; else { #ifdef DEBUG if (beg_pos < 0) error ("beg_pos must be valid here.\n"); if (end_pos < 0) error ("end_pos must be valid here.\n"); #endif string s = content[beg_pos..end_pos - 1]; if (s == "") split = ({}); else { array(string) sp, sps; if (sep) { if (content == search_str) split = s / sep; else { sp = search_str[beg_pos..end_pos - 1] / sep; sps = allocate (sizeof (sp) - 1, sep) + ({""}); } } else { #ifdef DEBUG if (!sep_chars) error ("Code flow bonkers.\n"); #endif if (content == search_str) [split, split_seps] = char_sep_fn (s, sep_chars); else [sp, sps] = char_sep_fn (search_str[beg_pos..end_pos - 1], sep_chars); } if (sp) { // content != search_str so we must split on search_str // and then transfer the split to content. split = allocate (sizeof (sp)); int p; foreach (sp; int i; string s) { split[i] = content[p..p + sizeof (s) - 1]; p += sizeof (s) + sizeof (sps[i]); } } } } if (trimmer) split = map (split, trimmer); if (ignore_empty) split -= ({""});
16461a2008-11-01Martin Stjernholm  if (result_type == RXML.t_array)
dc1c4b2008-09-15Martin Stjernholm  result = split; else { #ifdef DEBUG if (!joiner) error ("Got no joiner.\n"); #endif result = split * joiner; } } else { result = content[beg_pos..end_pos - 1]; if (trimmer) result = trimmer (result); } return 0;
d8767e2005-09-01 Erik Dahl  } } }
dee59d2008-11-01Martin Stjernholm class TagRange { inherit RXML.Tag; constant name = "range"; mapping(string:RXML.Type) opt_arg_types = ([ "from": RXML.t_int (RXML.PEnt), "to": RXML.t_int (RXML.PEnt), "variable": RXML.t_text (RXML.PEnt), ]); RXML.Type content_type = RXML.t_array (RXML.PXml); array(RXML.Type) result_types = ({RXML.t_any}); constant flags = RXML.FLAG_DONT_RECOVER; class Frame { inherit RXML.Frame; array do_enter (RequestID id) { if (!args->join) result_type = RXML.t_array; else result_type = RXML.t_string; if (args->variable) flags |= RXML.FLAG_EMPTY_ELEMENT; return 0; } array do_return (RequestID id) { int beg = args->from;
51756a2009-04-06Jonas Wallden  if (!beg && !zero_type (beg)) parse_error ("\"from\" cannot be zero.\n");
dee59d2008-11-01Martin Stjernholm  int end = args->to;
51756a2009-04-06Jonas Wallden  if (!end && !zero_type (end)) parse_error ("\"to\" cannot be zero.\n");
dee59d2008-11-01Martin Stjernholm  if (args->variable) content = RXML.user_get_var (args->variable); else if (content == RXML.nil) content = ({}); if (!arrayp (content)) parse_error ("Array required as input, got %t.\n", content); int beg_pos, end_pos; beg_search: if (string after = args->after) { if (!beg) beg = 1; if (beg > 0) { int i = 0; do { i = search (content, after, i) + 1; if (i == 0) { beg_pos = Int.NATIVE_MAX; break beg_search; } } while (--beg > 0); beg_pos = i; } else { for (int i = sizeof (content); --i >= 0;) { if (content[i] == after) if (!++beg) { beg_pos = i + 1; break beg_search; } } beg_pos = 0; } } else { // Index by position. if (beg > 0) beg_pos = beg - 1; else if (beg < 0) beg_pos = max (sizeof (content) + beg, 0); else beg_pos = 0; } end_search: if (string before = args->before) { if (!end) end = 1; if (end > 0) { int i = 0; do { i = search (content, before, i) + 1; if (i == 0) { end_pos = Int.NATIVE_MAX; break end_search; } } while (--end > 0); end_pos = i - 1; } else { for (int i = sizeof (content); --i >= 0;) { if (content[i] == before) if (!++end) { end_pos = i; break end_search; } } end_pos = 0; } } else { // Index by position. if (end > 0) end_pos = end; else if (end <= 0) end_pos = max (sizeof (content) + end + 1, 0); } #ifdef DEBUG if (beg_pos < 0) error ("Invalid beg_pos %d.\n", beg_pos); if (end_pos < 0) error ("Invalid end_pos %d.\n", end_pos); #endif result = content[beg_pos..end_pos - 1]; if (string joiner = args->join) { if (mixed err = catch (result = (array(string)) result)) parse_error ("Cannot convert %s to array of strings: %s", RXML.utils.format_short (result), describe_error (err)); result *= joiner; } return 0; } } }
7f826c2008-10-30Martin Stjernholm class TagValue { inherit RXML.Tag; constant name = "value"; mapping(string:RXML.Type) opt_arg_types = ([ "type": RXML.t_type (RXML.PEnt), "index": RXML.t_any (RXML.PEnt), ]); array(RXML.Type) result_types = ({RXML.t_any}); constant flags = RXML.FLAG_DONT_RECOVER; class Frame { inherit RXML.Frame; array do_enter (RequestID id) { RXML.Type type = args->type;
3ddede2017-05-17Henrik Grubbström (Grubba)  if (type) { content_type = type(RXML.PXml); } else if (result_type->handle_literals) { // t_any, t_array, t_mapping, etc. content_type = RXML.t_any(RXML.PXml); } else { // Typically t_html. // Encode the content with the same type as our result_type. content_type = result_type(RXML.PXml); }
7f826c2008-10-30Martin Stjernholm  if (args->index) { if (result_type != RXML.t_mapping) parse_error ("\"index\" attribute only supported " "in mapping contexts, got %O.\n", result_type); } else {
cf3bc02008-11-01Martin Stjernholm  if (result_type == RXML.t_mapping) parse_error ("\"index\" attribute required in mapping context.\n");
7f826c2008-10-30Martin Stjernholm  if (result_type == RXML.t_array) // Avoid that an array value gets spliced into the // surrounding array. This is the only case where we've got // a splice/single-value ambiguity. result_type = RXML.t_any; }
0f70992009-04-08Martin Stjernholm  if (args->from || args->expr) flags |= RXML.FLAG_EMPTY_ELEMENT;
7f826c2008-10-30Martin Stjernholm  } array do_return (RequestID id) {
0f70992009-04-08Martin Stjernholm  if (string var = args->from) { // Get the value from another variable. if (zero_type (content = RXML.user_get_var(var, args->scope))) parse_error ("From variable %q does not exist.\n", var); } else if (string expr = args->expr) content = sexpr_eval (expr); else if (content == RXML.nil) { if (content_type->sequential) content = content_type->empty_value; else if (content_type == RXML.t_any) content = RXML.empty; else parse_error ("The value is missing for " "non-sequential type %s.\n", content_type); } if (string ind = args->index)
cf3bc02008-11-01Martin Stjernholm  result = ([ind: content]); else if (result_type != content_type) result = result_type->encode (content, content_type);
7f826c2008-10-30Martin Stjernholm  else result = content; } } }
e8405b2011-11-14Martin Stjernholm class TagJsonFormat { inherit RXML.Tag; constant name = "json-format"; mapping(string:RXML.Type) opt_arg_types = ([ "variable": RXML.t_text (RXML.PEnt), ]); RXML.Type content_type = RXML.t_any (RXML.PXml); class Frame { inherit RXML.Frame; array do_return (RequestID id) { int encode_flags; if (args["ascii-only"]) encode_flags |= Standards.JSON.ASCII_ONLY; if (args["human-readable"]) encode_flags |= Standards.JSON.HUMAN_READABLE; if (string canon = args["canonical"]) { if (canon != "pike") RXML.parse_error ("Unknown canonical form %q requested.\n", canon); encode_flags |= Standards.JSON.PIKE_CANONICAL; } if (args->value) content = args->value; else if (string var = args->variable) { if (zero_type (content = RXML.user_get_var (var))) parse_error ("Variable %q does not exist.\n", var); } if (mixed err = catch (result = Standards.JSON.encode (content, encode_flags))) RXML.run_error (describe_error (err)); if (!args["no-xml-quote"]) result = replace (result, ([ "&": "\\u0026", "<": "\\u003c", ">": "\\u003e", ])); } } } class TagJsonParse { inherit RXML.Tag; constant name = "json-parse"; RXML.Type content_type = RXML.t_any_text (RXML.PXml); array(RXML.Type) result_types = ({RXML.t_any}); class Frame { inherit RXML.Frame; array do_return (RequestID id) { if (args->value) content = args->value; else if (string var = args->variable) { if (zero_type (content = RXML.user_get_var (var))) parse_error ("Variable %q does not exist.\n", var); } if (mixed err = catch (result = Standards.JSON.decode (content))) RXML.run_error (describe_error (err)); } } }
30d6a52000-02-16Martin Nilsson class TagCSet { inherit RXML.Tag; constant name = "cset";
70beb32008-11-19Martin Stjernholm  // No result. Propagate the result type to the content for // compatibility, but allow placement in non-text contexts too. array(RXML.Type) result_types = ::result_types + ({RXML.t_nil});
5bc2c22008-11-18Martin Stjernholm 
30d6a52000-02-16Martin Nilsson  class Frame { inherit RXML.Frame;
70beb32008-11-19Martin Stjernholm  array do_enter (RequestID id) {
1f99652009-05-06Jonas Wallden  if (!content_type->subtype_of (RXML.t_any_text) || content_type == RXML.t_nil)
70beb32008-11-19Martin Stjernholm  content_type = RXML.t_any_text (RXML.PXml); }
30d6a52000-02-16Martin Nilsson  array do_return(RequestID id) {
5b0e5d2000-03-25Martin Nilsson  if( !args->variable ) parse_error("Variable not specified.\n");
ea31c52000-02-16Martin Nilsson  if(!content) content="";
30d6a52000-02-16Martin Nilsson  if( args->quote != "none" )
6743432000-03-19Martin Nilsson  content = Roxen.html_decode_string( content );
30d6a52000-02-16Martin Nilsson 
6444372000-03-10Martin Nilsson  RXML.user_set_var(args->variable, content, args->scope);
5bc2c22008-11-18Martin Stjernholm  return 0;
30d6a52000-02-16Martin Nilsson  } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
43ff412001-02-06Martin Nilsson class TagColorScope { inherit RXML.Tag; constant name = "colorscope";
243ed52008-09-16Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_any});
43ff412001-02-06Martin Nilsson  class Frame { inherit RXML.Frame; string link, alink, vlink;
6d754f2001-06-18Martin Stjernholm #define LOCAL_PUSH(X) if(args->X) { X=RXML_CONTEXT->misc->X; RXML_CONTEXT->misc->X=args->X; }
43ff412001-02-06Martin Nilsson  array do_enter(RequestID id) { Roxen.push_color("colorscope",args,id); LOCAL_PUSH(link); LOCAL_PUSH(alink); LOCAL_PUSH(vlink); return 0; }
6d754f2001-06-18Martin Stjernholm #define LOCAL_POP(X) if(X) RXML_CONTEXT->misc->X=X
43ff412001-02-06Martin Nilsson  array do_return(RequestID id) { Roxen.pop_color("colorscope",id); LOCAL_POP(link); LOCAL_POP(alink); LOCAL_POP(vlink); result=content; return 0; } } }
e272382000-01-23Kenneth Johansson 
b156c62001-04-23Martin Nilsson // ------------------------- RXML Core tags --------------------------
e272382000-01-23Kenneth Johansson 
b156c62001-04-23Martin Nilsson class TagHelp {
592ed72000-02-06Martin Nilsson  inherit RXML.Tag;
b156c62001-04-23Martin Nilsson  constant name = "help";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
b156c62001-04-23Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) { string help_for = args->for || id->variables->_r_t_h; string ret="<h2>Roxen Interactive RXML Help</h2>"; if(!help_for) {
89b4fe2006-06-01Henrik Grubbström (Grubba)  // FIXME: Is it actually needed to disable the cache?
b368062002-09-03Martin Stjernholm  NOCACHE();
1b5f082002-08-13Martin Stjernholm  array tags=map(indices(RXML_CONTEXT->tag_set->get_tag_names()), lambda(string tag) { if (!has_prefix (tag, "_")) if(tag[..3]=="!--#" || !has_value(tag, "#")) return tag; return ""; } ) - ({ "" }); tags += map(indices(RXML_CONTEXT->tag_set->get_proc_instr_names()), lambda(string tag) { return "&lt;?"+tag+"?&gt;"; } ); tags = Array.sort_array(tags, lambda(string a, string b) {
b368062002-09-03Martin Stjernholm  if(has_prefix (a, "&lt;?")) a=a[5..]; if(has_prefix (b, "&lt;?")) b=b[5..]; if(lower_case(a)==lower_case(b)) return a > b; return lower_case (a) > lower_case (b);
1b5f082002-08-13Martin Stjernholm  })-({"\x266a"});
b156c62001-04-23Martin Nilsson  string char; ret += "<b>Here is a list of all defined tags. Click on the name to " "receive more detailed information. All these tags are also availabe " "in the \""+RXML_NAMESPACE+"\" namespace.</b><p>\n"; array tag_links; foreach(tags, string tag) {
b368062002-09-03Martin Stjernholm  string tag_char = lower_case (has_prefix (tag, "&lt;?") ? tag[5..5] : tag[0..0]); if (tag_char != char) {
b156c62001-04-23Martin Nilsson  if(tag_links && char!="/") ret+="<h3>"+upper_case(char)+"</h3>\n<p>"+ String.implode_nicely(tag_links)+"</p>";
b368062002-09-03Martin Stjernholm  char = tag_char;
b156c62001-04-23Martin Nilsson  tag_links=({}); } if(tag[0..sizeof(RXML_NAMESPACE)]!=RXML_NAMESPACE+":") { string enc=tag;
7b95522001-08-23Martin Nilsson  if(enc[0..4]=="&lt;?") enc=enc[4..sizeof(enc)-6];
5e09bd2008-10-30Martin Stjernholm  if(my_configuration()->undocumented_tags && my_configuration()->undocumented_tags[tag])
b156c62001-04-23Martin Nilsson  tag_links += ({ tag }); else tag_links += ({ sprintf("<a href=\"%s?_r_t_h=%s\">%s</a>\n",
4e3c212002-06-27Martin Stjernholm  id->url_base() + id->not_query[1..], Roxen.http_encode_url(enc), tag) }); }
b156c62001-04-23Martin Nilsson  }
e272382000-01-23Kenneth Johansson 
b156c62001-04-23Martin Nilsson  ret+="<h3>"+upper_case(char)+"</h3>\n<p>"+String.implode_nicely(tag_links)+"</p>"; /* ret+="<p><b>This is a list of all currently defined RXML scopes and their entities</b></p>";
e272382000-01-23Kenneth Johansson 
6d754f2001-06-18Martin Stjernholm  RXML.Context context=RXML_CONTEXT;
b156c62001-04-23Martin Nilsson  foreach(sort(context->list_scopes()), string scope) { ret+=sprintf("<h3><a href=\"%s?_r_t_h=%s\">%s</a></h3>\n", id->not_query, Roxen.http_encode_url("&"+scope+";"), scope); ret+="<p>"+String.implode_nicely(Array.map(sort(context->list_var(scope)), lambda(string ent) { return ent; }) )+"</p>"; } */ return ({ ret }); }
df3c452000-07-26Johan Sundström 
5e09bd2008-10-30Martin Stjernholm  result=ret+my_configuration()->find_tag_doc(help_for, id);
b156c62001-04-23Martin Nilsson  }
df3c452000-07-26Johan Sundström  } }
b156c62001-04-23Martin Nilsson class TagNumber { inherit RXML.Tag; constant name = "number";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
df3c452000-07-26Johan Sundström 
b156c62001-04-23Martin Nilsson  class Frame { inherit RXML.Frame; array do_return(RequestID id) { if(args->type=="roman") return ({ Roxen.int2roman((int)args->num) }); if(args->type=="memory") return ({ Roxen.sizetostring((int)args->num) }); result=roxen.language(args->lang||args->language||
6d754f2001-06-18Martin Stjernholm  RXML_CONTEXT->misc->theme_language,
b156c62001-04-23Martin Nilsson  args->type||"number",id)( (int)args->num ); } }
592ed72000-02-06Martin Nilsson }
e272382000-01-23Kenneth Johansson 
cde9a02000-03-10Martin Nilsson 
b156c62001-04-23Martin Nilsson class TagUse { inherit RXML.Tag; constant name = "use";
1b2d742001-10-08Anders Johansson  constant flags = RXML.FLAG_EMPTY_ELEMENT;
5bc2c22008-11-18Martin Stjernholm  array(RXML.Type) result_types = ({RXML.t_nil}); // No result.
9b03652001-03-07Kenneth Johansson 
4eeba32017-04-05Jonas Walldén  protected void create() { cache.cache_register("macrofiles", 0, extend_entries_cache_prefs); }
1d7e872016-03-24Pontus Östlund  private array(string) list_packages() {
b156c62001-04-23Martin Nilsson  return filter(((get_dir("../local/rxml_packages")||({})) |(get_dir("rxml_packages")||({}))), lambda( string s ) { return s!=".cvsignore" && (Stdio.file_size("../local/rxml_packages/"+s)+ Stdio.file_size( "rxml_packages/"+s )) > 0; }); }
9b03652001-03-07Kenneth Johansson 
b156c62001-04-23Martin Nilsson  private string read_package( string p ) { string data; p = combine_path("/", p); if(file_stat( "../local/rxml_packages/"+p )) catch(data=Stdio.File( "../local/rxml_packages/"+p, "r" )->read()); if(!data && file_stat( "rxml_packages/"+p )) catch(data=Stdio.File( "rxml_packages/"+p, "r" )->read()); return data; }
9b03652001-03-07Kenneth Johansson 
b156c62001-04-23Martin Nilsson  private string use_file_doc(string f, string data) { string res, doc; int help; // If true, all tags support the 'help' argument. sscanf(data, "%*sdoc=\"%s\"", doc); sscanf(data, "%*shelp=%d", help); res = "<dt><b>"+f+"</b></dt><dd>"+(doc?doc+"<br />":"")+"</dd>";
9b03652001-03-07Kenneth Johansson 
e3ae852009-11-17Martin Stjernholm  array defs = cache_lookup ("macrofiles", "|" + f); if (!defs) { defs = parse_use_package(data, RXML_CONTEXT); cache_set("macrofiles", "|"+f, defs, 300); }
9b03652001-03-07Kenneth Johansson 
6d754f2001-06-18Martin Stjernholm  array(string) ifs = ({}), tags = ({});
9b03652001-03-07Kenneth Johansson 
a32d6a2008-11-19Martin Stjernholm  foreach (defs[1]; string defname;)
6d754f2001-06-18Martin Stjernholm  if (has_prefix (defname, "if\0")) ifs += ({defname[sizeof ("if\0")..]}); else if (has_prefix (defname, "tag\0")) tags += ({defname[sizeof ("tag\0")..]}); constant types = ({ "if plugin", "tag", "form variable", "\"var\" scope variable" });
a32d6a2008-11-19Martin Stjernholm  array pack = ({ifs, tags, indices(defs[2]), indices(defs[3])});
9b03652001-03-07Kenneth Johansson 
b156c62001-04-23Martin Nilsson  for(int i; i<3; i++) if(sizeof(pack[i])) { res += "Defines the following " + types[i] + (sizeof(pack[i])!=1?"s":"") + ": " + String.implode_nicely( sort(pack[i]) ) + ".<br />"; }
9b03652001-03-07Kenneth Johansson 
b156c62001-04-23Martin Nilsson  if(help) res+="<br /><br />All tags accept the <i>help</i> attribute."; return res; }
9b03652001-03-07Kenneth Johansson 
b156c62001-04-23Martin Nilsson  private array parse_use_package(string data, RXML.Context ctx) {
6d754f2001-06-18Martin Stjernholm  RXML.Parser parser = Roxen.get_rxml_parser (ctx->id); parser->write_end (data); parser->eval();
9b03652001-03-07Kenneth Johansson