Roxen.git / server / modules / tags / additional_rxml.pike

version» Context lines:

Roxen.git/server/modules/tags/additional_rxml.pike:1:   // This is a roxen module. Copyright © 2000 - 2009, Roxen IS.   //      #include <module.h>   inherit "module";      #define _ok RXML_CONTEXT->misc[" _ok"]    - constant cvs_version = "$Id: additional_rxml.pike,v 1.59 2012/05/11 13:02:36 mast Exp $"; + constant cvs_version = "$Id$";   constant thread_safe = 1;   constant module_type = MODULE_TAG;   constant module_name = "Tags: Additional RXML tags";   constant module_doc = "This module provides some more complex and not as widely used RXML tags.";      // Cached copy of conf->query("compat_level").   float compat_level = (float) my_configuration()->query("compat_level");      void create() {    defvar("insert_href",
Roxen.git/server/modules/tags/additional_rxml.pike:57: Inside #if defined(THREADS)
   int status;    object con;    Standards.URI url;    string path, query, req_data,method;    mapping request_headers;       Thread.Queue queue = Thread.Queue();       void do_method(string _method,    string|Standards.URI _url, -  void|mapping post_variables, +  void|mapping post_put_variables,    void|mapping _request_headers,    void|Protocols.HTTP.Query _con, void|string _data)    {    if(!_con) {    con = Protocols.HTTP.Query();    }    else    con = _con;       method = _method;       if(!_request_headers)    request_headers = ([]);    else    request_headers = _request_headers;    -  if(post_variables) { +  if(post_put_variables) {    request_headers +=    (["content-type":    "application/x-www-form-urlencoded"]); -  _data = Protocols.HTTP.http_encode_query(post_variables); +  _data = Protocols.HTTP.http_encode_query(post_put_variables);    }       req_data = _data;       if(stringp(_url)) {    if (mixed err = catch (url=Standards.URI(_url)))    RXML.parse_error ("Invalid URL: %s\n", describe_error (err));    }    else    url = _url;    - #if constant(SSL.sslfile) + #if constant(SSL.File)    if(url->scheme!="http" && url->scheme!="https")    error("Protocols.HTTP can't handle %O or any other protocols than HTTP or HTTPS\n",    url->scheme);       con->https= (url->scheme=="https")? 1 : 0;   #else -  if(url->scheme!="http" ) +  if(url->scheme!="http")    error("Protocols.HTTP can't handle %O or any other protocol than HTTP\n",    url->scheme);      #endif       if(!request_headers)    request_headers = ([]);    mapping default_headers = ([    "user-agent" : "Mozilla/4.0 compatible (Pike HTTP client)",    "host" : url->host + ( (url->scheme=="http" && url->port != 80) ||    (url->scheme=="https" && url->port != 443) ?    ":"+ url->port : "")    ]);       if(url->user)    default_headers->authorization = "Basic "    + MIME.encode_base64(url->user + ":" + -  (url->password || "")); +  (url->password || ""), 1);    request_headers = default_headers | request_headers;       query=url->query;    /*    if(query_variables && sizeof(query_variables))    {    if(query)    query+="&"+Protocols.HTTP.http_encode_query(query_variables);    else    query=Protocols.HTTP.http_encode_query(query_variables);
Roxen.git/server/modules/tags/additional_rxml.pike:202:    status = con->status;    queue->read();    }       void destroy()    {    if(con)    destruct(con);    }    -  void create(string method, mapping args, mapping|void headers) { -  if(method == "POST") { -  mapping vars = ([ ]); -  string data; - #if constant(roxen) -  data = args["post-data"]; -  foreach( (args["post-variables"] || "") / ",", string var) { -  array a = var / "="; -  if(sizeof(a) == 2) -  vars[String.trim_whites(a[0])] = RXML.user_get_var(String.trim_whites(a[1])); +  void create(string method, mapping args, +  mapping|void headers, string|mapping|void data) +  { +  if(method == "POST" || method == "PUT") { +  if (mappingp(data)) { +  do_method(method, args->href, data, headers, 0, UNDEFINED); +  } else { +  do_method(method, args->href, UNDEFINED, headers, 0, data);    } -  if(data && sizeof(data) && sizeof(vars)) -  RXML.run_error("The 'post-variables' and the 'post-data' arguments " -  "are mutually exclusive."); - #endif -  do_method("POST", args->href, sizeof(vars) && vars, headers, -  0, data); +  } else if (method == "GET" || method == "DELETE") { +  do_method(method, args->href, 0, headers);    } -  else -  do_method("GET", args->href, 0, headers); +        if(args->timeout) {    con->timeout = (int) args->timeout;    con->data_timeout = (int) args->timeout;    } else if (int t = global::query ("default_timeout")) {    con->timeout = t;    con->data_timeout = t;    } else {    // There ought to be a way to disable the timeout in    // Protocols.HTTP.Query.
Roxen.git/server/modules/tags/additional_rxml.pike:244:    }    }      }   #endif      class TagInsertHref {    inherit RXML.Tag;    constant name = "insert";    constant plugin_name = "href"; +  RXML.Type content_type = RXML.t_any (RXML.PXml);    -  string get_data(string var, mapping args, RequestID id) { +  mapping(string:RXML.Type) opt_arg_types = ([ +  "method": RXML.t_text(RXML.PEnt), +  "soap-action": RXML.t_text(RXML.PEnt), +  "accept-status": RXML.t_text(RXML.PEnt), +  ]); +  +  array do_enter(mapping args, RequestID id, RXML.Frame frame) +  { +  if (args["soap-action"]) { +  frame->flags &= ~RXML.FLAG_EMPTY_ELEMENT; +  } +  } +  +  string get_data(string var, mapping args, RequestID id, RXML.Frame frame) +  {    if(!query("insert_href")) RXML.run_error("Insert href is not allowed.\n");       int recursion_depth = (int)id->request_headers["x-roxen-recursion-depth"];       if (query("recursion_limit") &&    (recursion_depth >= query("recursion_limit")))    RXML.run_error("Too deep insert href recursion.");       recursion_depth++;       if(args->nocache)    NOCACHE();    else    CACHE(60);    -  string method = "GET"; -  if(args->method && lower_case(args->method) == "post") -  method = "POST"; -  +     object /*Protocols.HTTP|AsyncHTTPClient*/ q;       mapping(string:string) headers = ([ "X-Roxen-Recursion-Depth":    (string)recursion_depth ]); -  if (args["request-headers"]) -  foreach (args["request-headers"] / ",", string header) +  if (args["request-headers"]) { +  string delim = args["header-delimiter"] || ","; +  foreach (args["request-headers"] / delim, string header)    if (sscanf (header, "%[^=]=%s", string name, string val) == 2)    headers[name] = val; -  +  }    - #ifdef THREADS -  q = AsyncHTTPClient(method, args, headers); -  q->run(); - #else -  mixed err; -  if(method == "POST") { -  mapping vars = ([ ]); -  foreach( (args["post-variables"] || "") / ",", string var) { +  string method = "GET"; +  string|mapping data; +  +  if (args["soap-action"]) { +  method = "POST"; +  headers["SOAPACTION"] = args["soap-action"]; +  headers["content-type"] = "text/xml; charset=utf-8"; +  // NB: frame->content has been RXML parsed. +  data = String.trim_all_whites(string_to_utf8(frame->content)); +  +  } else if (lower_case(args->method || "") == "post" || +  lower_case(args->method || "") == "put") { +  method = upper_case(args->method); +  string method_lc = lower_case(args->method); +  +  if (args[method_lc + "-data"] && args[method_lc + "-variables"]) { +  RXML.run_error("The '" + method_lc + "-variables' and '" + +  method_lc + "-data' attributes are mutually " +  "exclusive."); +  } +  +  data = args[method_lc + "-data"]; +  if (args[method_lc + "-variables"]) { +  data = ([]); +  foreach(args[method_lc + "-variables"] / ",", string var) {    array a = var / "=";    if(sizeof(a) == 2) -  vars[String.trim_whites(a[0])] = RXML.user_get_var(String.trim_whites(a[1])); +  data[String.trim_whites(a[0])] = RXML.user_get_var(String.trim_whites(a[1]));    } -  err = catch { -  q = Protocols.HTTP.post_url(args->href, vars, headers); -  }; +     } -  else -  err = catch { -  q = Protocols.HTTP.get_url(args->href, 0, headers); +  +  } else if (lower_case(args->method || "") == "delete") { +  method = "DELETE"; +  } +  + #ifdef THREADS +  q = AsyncHTTPClient(method, args, headers, data); +  q->run(); + #else +  mixed err = catch { +  q = Protocols.HTTP.post_url(args->href, data, headers);    }; -  +     if (err) {    string msg = describe_error (err);    if (has_prefix (msg, "Standards.URI:"))    RXML.parse_error ("Invalid URL: %s\n", msg);    }   #endif       _ok = 1;       if(args["status-variable"] && q && q->status)    RXML.user_set_var(args["status-variable"],q->status);    -  if(q && q->status>0 && q->status<400) { +  // Default to accepting statuses in the range 1-399. +  array(array(int)) accept_statuses = ({ ({ 1, 399 }) }); +  +  if (args["accept-status"]) { +  accept_statuses = ({}); +  foreach(args["accept-status"]/",", string range) { +  range = +  replace(range, ({ "...", "..", " ", "\t" }), ({ "-", "-", "", "" })); +  array(int) pair = (array(int))(range/"-"); +  if (sizeof(pair) == 1) { +  pair += pair; +  } else if ((sizeof(pair) != 2) || (pair[0] > pair[1])) { +  RXML.parse_error("Invalid status range: %s\n", args["accept-status"]); +  } +  accept_statuses += ({ pair }); +  } +  } +  +  string errmsg; +  if(q) { +  foreach(accept_statuses, array(int) pair) { +  if ((q->status >= pair[0]) && (q->status <= pair[1])) {    mapping headers = q->con->headers;    string data = q->data(); -  +  if (data) {    // Explicitly destruct the connection object to avoid garbage    // and CLOSE_WAIT sockets. Reported in [RT 18335].    destruct(q); -  return Roxen.low_parse_http_response (headers, data, 0, 1, +  return Roxen.low_parse_http_response(headers, data, 0, 1,    (int)args["ignore-unknown-ce"]);    } -  +  break; +  } +  } +  errmsg = q->status_desc; +  // Explicitly destruct the connection object to avoid garbage +  // and CLOSE_WAIT sockets. Reported in [RT 18335]. +  destruct(q); +  }       _ok = 0;       if(!args->silent) -  RXML.run_error((q && q->status_desc) || "No server response"); -  // Explicitly destruct the connection object to avoid garbage -  // and CLOSE_WAIT sockets. Reported in [RT 18335]. -  destruct(q); +  RXML.run_error(errmsg || "No server response");    return "";    }   }      class TagXmlRpcCall   // FIXME: This tag should also implement timeout using AsyncHTTPClient   // or similar.   {    inherit RXML.Tag;    constant name = "xml-rpc-call";
Roxen.git/server/modules/tags/additional_rxml.pike:682:   }      class TagSscanf {    inherit RXML.Tag;    constant name = "sscanf";       RXML.Type content_type = RXML.t_any_text (RXML.PXml);    array(RXML.Type) result_types =    compat_level < 5.2 ? ::result_types : ({RXML.t_nil}); // No result.    -  mapping(string:RXML.Type) req_arg_types = ([ "variables" : RXML.t_text(RXML.PEnt), -  "format" : RXML.t_text(RXML.PEnt) -  ]); -  mapping(string:RXML.Type) opt_arg_types = ([ "return" : RXML.t_text(RXML.PEnt), -  "scope" : RXML.t_text(RXML.PEnt) -  ]); +  mapping(string:RXML.Type) +  req_arg_types = ([ "variables" : RXML.t_text(RXML.PEnt), +  "format" : RXML.t_text(RXML.PEnt) ]); +  mapping(string:RXML.Type) +  opt_arg_types = ([ "variable" : RXML.t_text(RXML.PEnt), +  "return" : RXML.t_text(RXML.PEnt), +  "scope" : RXML.t_text(RXML.PEnt) ]);       class Frame {    inherit RXML.Frame;       string do_return(RequestID id) { -  +  if (string content_var = args->variable) { +  if (zero_type (content = RXML.user_get_var (content_var))) +  parse_error ("Variable %q does not exist.\n", content_var); +  } +     array(string) vars=args->variables/",";    vars=map(vars, String.trim_all_whites);    array(string) vals;    mixed err = catch {    vals = array_sscanf(content || "", args->format);    };    if(err) {    string msg = "Unknown error.\n";    if(arrayp(err) && sizeof(err) && stringp(err[0]))    msg = err[0];
Roxen.git/server/modules/tags/additional_rxml.pike:847:    "pattern" : RXML.t_text(RXML.PEnt)    ]);    mapping(string:RXML.Type) opt_arg_types =    ([ "decimal-separator" : RXML.t_text(RXML.PEnt),    "group-separator" : RXML.t_text(RXML.PEnt)    ]);       class Frame {    inherit RXML.Frame;    -  string do_return(RequestID req_id) { -  //constant PERCENT = 0x01; -  //constant PER_MILLE = 0x02; -  constant TRUE = 0x01; -  constant FALSE = 0x00; -  constant FRAC_PART = 1; -  constant INT_PART = 0; -  +  string do_return(RequestID req_id) +  {    string grp_sep = ",";    string dec_sep = ".";    if (args["group-separator"])    {    grp_sep = args["group-separator"];    }    if (args["decimal-separator"])    {    dec_sep = args["decimal-separator"];    }
Roxen.git/server/modules/tags/additional_rxml.pike:878:    ({ " ", "\t", "\r", "\n" }),    ({ "", "", "", "" }));       // Parse the number.    string sgn = "", int_part = "", frac_part = "", rest = "";    if (sscanf(content, "%[-+]%[0-9]%s", sgn, int_part, rest) <= 1) {    RXML.parse_error("No number to be formatted was given.");    }    sscanf(rest, ".%[0-9]%s", frac_part, rest);    -  if (!(sizeof(sgn/"-") & 1)) int_part = "-" + int_part; +  if (has_prefix(rest, "e") || has_prefix(rest, "E")) { +  // Exponent notation. +  int exponent = 0; +  sscanf(rest[1..], "%d%s", exponent, rest); +  if (exponent > 0) { +  if (exponent < sizeof(frac_part)) { +  int_part += frac_part[..exponent-1]; +  frac_part = frac_part[exponent..]; +  } else { +  exponent -= sizeof(frac_part); +  int_part += frac_part + "0" * exponent; +  frac_part = ""; +  } +  } else if (exponent < 0) { +  exponent = -exponent; +  if (exponent < sizeof(int_part)) { +  int off = sizeof(int_part) - exponent; +  frac_part = int_part[off..] + frac_part; +  int_part = int_part[..off-1]; +  } else { +  exponent -= sizeof(int_part); +  frac_part = "0" * exponent + int_part + frac_part; +  int_part = ""; +  } +  } +  }    -  +  if (!(sizeof(sgn/"-") & 1)) sgn = "-"; +  else sgn = ""; +     // FIXME: Consider checking whether rest contains junk.       /* We need to break down the pattern, first by ";" and make sure that ";" -  is not an escaped character. */ -  array(string) temp_pattern = args->pattern / ";"; -  array(string) pattern = ({ "", "" }); -  int is_pos = TRUE; -  for (int i = 0; i < sizeof(temp_pattern); i++) -  { -  pattern[is_pos] += temp_pattern[i]; -  if (sizeof(pattern[is_pos]) > 0 && -  pattern[is_pos][sizeof(pattern[is_pos]) - 1] == "'"[0]) -  { -  pattern[is_pos][sizeof(pattern[is_pos]) - 1] = ";"[0]; +  * is not an escaped character. +  */ +  string pattern = ""; +  int neg_pattern; +  foreach(args->pattern/";"; int i; string seg) { +  if (i) { +  if (!has_suffix(pattern, "'")) { +  if (sgn == "") break; +  // Start of negative pattern. +  pattern = ""; +  neg_pattern = 1; +  } else { +  // Escaped. +  pattern = pattern[..sizeof(pattern)-2] + ";";    } -  else if (is_pos) -  { -  is_pos = FALSE; +     } -  +  pattern += seg;    }    -  /* Now that we have extracted the patterns for positive and negative -  numbers, we can reuse the is_pos flag to reflect which pattern we -  should use. */ -  if (has_prefix(int_part, "-") && sizeof(pattern[0]) > 0) -  { -  is_pos = FALSE; -  // Trim away the minus sign. -  int_part = int_part[1..]; -  } -  else if (sizeof(pattern[1]) > 0) -  { -  is_pos = TRUE; -  } -  else -  { -  RXML.parse_error("The value was positive but no pattern " + -  "for positive values was defined.\n"); -  } -  +     // Now do the same thing as above to find the fraction delimiter. -  temp_pattern = pattern[is_pos] / "."; -  pattern = ({"", ""}); -  int is_frac = FALSE; -  -  pattern[INT_PART] = temp_pattern[0]; -  if (sizeof(pattern[INT_PART]) > 0 && -  pattern[INT_PART][-1] == "'"[0]) -  { -  pattern[INT_PART][sizeof(pattern[INT_PART]) - 1] = "."[0]; +  string int_pattern = ""; +  string frac_pattern; +  foreach(pattern/"."; int i; string seg) { +  if (i) { +  if (frac_pattern) { +  if (has_suffix(frac_pattern, "'")) { +  frac_pattern = frac_pattern[..sizeof(frac_pattern)-2];    } -  else -  { -  is_frac = TRUE; +  frac_pattern += "." + seg; +  } else if (has_suffix(int_pattern, "'")) { +  int_pattern = int_pattern[..sizeof(int_pattern)-2] + "." + seg; +  } else { +  frac_pattern = seg;    } -  -  if (sizeof(temp_pattern) > 1) -  { -  for (int i = 1; i < sizeof(temp_pattern); i++) -  { -  pattern[is_frac] += temp_pattern[i]; -  if (sizeof(pattern[is_frac]) > 0 && -  pattern[is_frac][-1] == "'"[0]) -  { -  pattern[is_frac][-1] = "."[0]; +  } else { +  int_pattern = seg;    } -  else if (!is_frac) -  { -  is_frac = TRUE; +     } -  else if (i < (sizeof(temp_pattern) - 1)) -  { -  pattern[is_frac] += "."; -  } -  } -  } +  if (!frac_pattern) frac_pattern = "";       // Handle percent and per-mille    int log_ten = 0; -  if (search(pattern[FRAC_PART], "%") >= 0 || -  search(pattern[INT_PART], "%") >= 0) +  if (has_value(pattern, "%"))    {    log_ten = 2;    } -  else if (search(pattern[FRAC_PART], "\x2030") >= 0 || -  search(pattern[INT_PART], "\x2030") >= 0) +  else if (has_value(pattern, "\x2030"))    {    log_ten = 3;    }       for (int i = log_ten; i > 0; i--)    {    if (frac_part != "")    {    int_part += frac_part[0..0];    frac_part = frac_part[1..];
Roxen.git/server/modules/tags/additional_rxml.pike:987:    else    {    int_part += "0";    }    }       // Trim away leading zeros    sscanf(int_part, "%*[0]%s", int_part);    if (int_part == "") int_part = "0";    +  // Keep track of any digits in case we truncate/round to zero. +  int digit_bits = 0; +     // Start with the fractional part.    int val_length = strlen(frac_part); -  int ptn_length = strlen(pattern[FRAC_PART]); +  int ptn_length = strlen(frac_pattern);    int vi = 0;    string frac_result = ""; -  -  foreach(pattern[FRAC_PART]/1, string s) +  foreach(frac_pattern/1, string s)    {    switch(s)    {    case "'":    break;    case "#":    if (vi < val_length)    { -  +  digit_bits |= frac_part[vi];    frac_result += frac_part[vi..vi];    vi++;    }    break;    case "0":    if (vi < val_length)    { -  +  digit_bits |= frac_part[vi];    frac_result += frac_part[vi..vi];    vi++;    }    else    {    frac_result += "0";    }    break;    default:    frac_result += s;    }    }       // Round the last digit.    if (vi < val_length && (frac_part[vi] > '5' || -  (frac_part[vi] == '5' && is_pos))) +  (frac_part[vi] == '5' && (sgn == ""))))    { -  +  digit_bits = 0;    vi = sizeof(frac_result);    while(vi > 0) {    if (frac_result[vi-1] == '9') {    // Carry.    vi--;    frac_result[vi] = '0';    continue;    }    frac_result[vi-1] += 1; -  +  digit_bits |= frac_part[vi-1];    break;    }    if (!vi)    {    int_part = (string)(((int)int_part) + 1);    }    }       // Now for the integral part. We traverse it in reverse.    val_length = strlen(int_part); -  ptn_length = strlen(pattern[INT_PART]); +  ptn_length = strlen(int_pattern);    vi = 0;    int num_wid = 0;    string rev_val = reverse(int_part);    string int_result = ""; -  string minus_sign = ""; -  foreach(reverse(pattern[INT_PART])/1, string s) +  foreach(reverse(int_pattern)/1, string s)    {    switch(s)    {    case "'":    break;    case "#":    if (vi < val_length)    { -  +  digit_bits |= rev_val[vi];    int_result += rev_val[vi..vi];    vi++;    }    if (num_wid <= 0)    {    num_wid--;    }    break;    case "0":    if (vi < val_length)    { -  +  digit_bits |= rev_val[vi];    int_result += rev_val[vi..vi];    vi++;    }    else    {    int_result += "0";    }    if (num_wid <= 0)    {    num_wid--;
Roxen.git/server/modules/tags/additional_rxml.pike:1093:    case ",":    if (num_wid <= 0)    {    num_wid *= -1;    }    if (vi < val_length)    {    int_result += grp_sep;    }    break; -  case "-": -  if (is_pos) -  { -  int_result += s; -  } -  else if (minus_sign = "") -  { -  minus_sign = "-"; -  } -  break; +     case "%":    case "\x2030":    frac_result = s;    break;    default:    int_result += s;    }    }    for (;vi < val_length;vi++)    {    if (num_wid > 0 && vi%num_wid == 0)    {    int_result += grp_sep;    }    int_result += rev_val[vi..vi];    }    -  +  // Insert the sign. +  if ((sgn == "-") && !(digit_bits & 0x0f)) { +  // Negative zero. +  sgn = ""; +  } +  foreach(int_result/1; int i; string s) { +  if (s == "-") { +  int_result = int_result[..i-1] + sgn + int_result[i+1..]; +  } else if (s == "+") { +  if (sgn == "") { +  sgn = "+"; +  } +  int_result = int_result[..i-1] + sgn + int_result[i+1..]; +  } else +  continue; +  sgn = ""; +  break; +  } +  if ((sgn != "") && !neg_pattern) { +  // No sign in generic pattern. +  if (has_suffix(int_result, "0")) { +  int_result = int_result[..sizeof(int_result)-2]; +  } +  int_result += sgn; +  } +     if (strlen(frac_result) == 0)    { -  result += minus_sign + reverse(int_result); +  result += reverse(int_result);    }    else if (frac_result[0..0] == "%" ||    frac_result[0..0] == "\x2030")    { -  result += minus_sign + reverse(int_result) + frac_result[0..0]; +  result += reverse(int_result) + frac_result[0..0];    }    else    { -  result += minus_sign + reverse(int_result) + dec_sep + frac_result; +  result += reverse(int_result) + dec_sep + frac_result;    }    }    }   }            TAGDOCUMENTATION;   #ifdef manual   constant tagdoc=([
Roxen.git/server/modules/tags/additional_rxml.pike:1171: Inside #if defined(manual)
   The URL to the page that should be inserted.</p>   </attr>      <attr name='nocache' value='string'><p>    If provided the resulting page will get a zero cache time in the RAM cache.    The default time is up to 60 seconds depending on the cache limit imposed by    other RXML tags on the same page.</p>   </attr>      <attr name='method' value='string' default='GET'><p> -  Method to use when requesting the page. GET or POST.</p> +  Method to use when requesting the page. GET, POST, PUT or DELETE.</p>   </attr>      <attr name='silent' value='string' ><p>    Do not print any error messages.</p>   </attr>      <attr name='status-variable' value='variable' ><p>    Prints the return code for the request in the specified varible.</p>    <ex-box>    <insert href='http://www.somesite.com/news/' status-variable='var.status-code' />
Roxen.git/server/modules/tags/additional_rxml.pike:1196: Inside #if defined(manual)
   Timeout for the request in seconds. This is not available if your run the server without threads.</p>   <ex-box>   <insert href='http://www.somesite.com/news/' silent='silent' timeout='10' />   <else>    Site did not respond within 10 seconds. Try again later.   </else>   </ex-box>   </attr>      <attr name='post-variables' value='\"variable=rxml variable[,variable2=rxml variable2,...]\"'><p> -  Comma separated list of variables to send in a POST request.</p> +  Comma-separated list of variables to send in a POST request.</p>   <ex-box>   <insert href='http://www.somesite.com/news/'    method='POST' post-variables='action=var.action,data=form.data' />   </ex-box>   </attr>      <attr name='post-data' value='string'><p>    String to send as the body content of the POST request. Mutually exclusive with the 'post-variables' argument.</p>   <ex-box>   <insert href='http://www.somesite.com/webservice/'    method='POST' post-data='&var.data;' />   </ex-box>   </attr>    -  + <attr name='put-variables' value='\"variable=rxml variable[,variable2=rxml variable2,...]\"'><p> +  Comma-separated list of variables to send in a PUT request.</p> + <ex-box> + <insert href='http://www.somesite.com/news/' +  method='PUT' put-variables='action=var.action,data=form.data' /> + </ex-box> + </attr> +  + <attr name='put-data' value='string'><p> +  String to send as the body content of the PUT request. Mutually exclusive with the 'put-variables' argument.</p> + <ex-box> + <insert href='http://www.somesite.com/webservice/' +  method='PUT' put-data='&var.data;' /> + </ex-box> + </attr> +    <attr name='request-headers' value='\"header=value[,header2=value2,...]\"'><p> -  Comma separated list of extra headers to send in the request.</p> +  List of extra headers to send in the request. Headers are separated by comma by default, but the delimiter can be changed using the 'header-delimiter' attribute.</p>   </attr>    -  + <attr name='header-delimiter' value='string'><p> +  Delimiter to use with 'request-headers', defaults to comma (\",\").</p> + </attr> +  + <attr name='accept-status' value='\"value|min_value-max_value[,...]\"'><p> +  Comma-separated list of status ranges to regard as successful fetches.</p> +  + <p>A range is represented by its minumum and maximum values (inclusive) + separated by a <tt>'-'</tt>. A range with a single value can alternatively + be represented by just the value by itself.</p> +  + <p>The default value is <tt>\"1-399\"</tt>.</p> + </attr> +    <attr name='ignore-unknown-ce' value='int'><p>    If set, unknown Content-Encoding headers in the response will be ignored. Some   servers specify the character set (e.g. 'UTF-8') in the Content-Encoding header   in addition to the Content-Type header. (The real purpose of the   Content-Encoding header is described here:   http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11)</p>   </attr>   ",       "xml-rpc-call":
Roxen.git/server/modules/tags/additional_rxml.pike:1304:   </attr>",    // FIXME: Document content tags.         "sscanf":#"<desc type='cont'><p><short>    Extract parts of a string and put them in other variables.</short> Refer to    the sscanf function in the Pike reference manual for a complete    description.</p>   </desc>    + <attr name='variable'><p> + Name of the variable holding the input data. If not provided the tag oeprates + on the contents of the <tag>sscanf</tag> element. + </p> + </attr> +    <attr name='format' value='pattern' required='required'><p>   The sscanf pattern.   </p>   </attr>      <attr name='variables' value='list' required='required'><p> -  A comma separated list with the name of the variables that should be set.</p> +  A comma-separated list with the name of the variables that should be set.</p>   <ex>   <sscanf variables='form.year,var.month,var.day'   format='%4d%2d%2d'>19771003</sscanf>   &form.year;-&var.month;-&var.day;   </ex>   </attr>      <attr name='scope' value='name' required='required'><p>    The name of the fallback scope to be used when no scope is given.</p>   <ex>