Roxen.git / server / etc / modules / Roxen.pmod

version» Context lines:

Roxen.git/server/etc/modules/Roxen.pmod:1:   // This is a roxen pike module. Copyright © 1999 - 2009, Roxen IS.   // - // $Id: Roxen.pmod,v 1.329 2012/04/17 09:06:40 erikd Exp $ + // $Id$      #include <roxen.h>   #include <config.h>   #include <version.h>   #include <module.h>   #include <stat.h>   #define roxen roxenp()      #ifdef HTTP_DEBUG   # define HTTP_WERR(X) report_debug("HTTP: "+X+"\n");
Roxen.git/server/etc/modules/Roxen.pmod:217:   }      string http_roxen_id_cookie(void|string unique_id)   {    return "RoxenUserID=" + (unique_id || roxen->create_unique_id()) + "; expires=" +    http_date (3600*24*365*2 + time (1)) + "; path=/";   }      protected mapping(string:function(string, RequestID:string)) cookie_callbacks =    ([]); +    protected class CookieChecker(string cookie)   {    string `()(string path, RequestID id)    {    if (!id->real_cookies) {    id->init_cookies();    }    // Note: Access the real_cookies directly to avoid registering callbacks.    return id->real_cookies[cookie];    }    string _sprintf(int c)    {    return c == 'O' && sprintf("CookieChecker(%O)", cookie);    }   } -  +    function(string, RequestID:string) get_cookie_callback(string cookie)   {    function(string, RequestID:string) cb = cookie_callbacks[cookie];    if (cb) return cb;    cb = CookieChecker(cookie);    return cookie_callbacks[cookie] = cb;   }      protected mapping(string:function(string, RequestID:string)) lang_callbacks = ([ ]);   
Roxen.git/server/etc/modules/Roxen.pmod:630:   //! new result mapping to @[RequestID.send_result()], but should you   //! want to glue together request headers and close the socket on your   //! own, you are free to do so. The method @[RequestID.connection()]   //! gives you the Stdio.File object for the current client connection.    mapping(string:mixed) http_pipe_in_progress()   {    HTTP_WERR("Pipe in progress");    return ([ "file":-1, "pipe":1, ]);   }    + //! Returns a response mapping indicating that the provided + //! @[Concurrent.Future] will be fulfilled with the request result. + //! + //! @param future + //! The @[Concurrent.Future] that will be fulfilled (by the caller). + //! It should be fulfilled with a response mapping, even for + //! non-fatal errors (such as 401, 404 etc.) On future failure, it + //! must fail with a Pike error (which will be handled the same + //! way as if a synchronous find_file call throws, i.e. "500 + //! Internal server error" sent to the client and so on.) + //! + //! @param id + //! The @[RequestID] that will be used to send the response. + mapping(string:mixed) http_future (Concurrent.Future future, RequestID id) + { +  future->on_success (id?->send_result); +  future->on_failure (id?->send_internal_error); +  return http_pipe_in_progress(); + } +    mapping(string:mixed) http_rxml_answer( string rxml, RequestID id,    void|Stdio.File file,    void|string type )   //! Convenience functions to use in Roxen modules. When you just want   //! to return a string of data, with an optional type, this is the   //! easiest way to do it if you don't want to worry about the internal   //! roxen structures.   {    rxml =    ([function(string,RequestID,Stdio.File:string)]id->conf->parse_rxml)
Roxen.git/server/etc/modules/Roxen.pmod:1035:    sscanf(body, "<?%s?>", string hdr)) {    hdr += "?";    if (sscanf(lower_case(hdr), "%*sencoding=%s%*[\n\r\t ?]",    string xml_enc) == 3)    charset = xml_enc - "'" - "\"";    }    }    }    }    +  // FIXME: Parse away BOM in xml documents also when the charset +  // already is known. +     if (charset) { -  Locale.Charset.Decoder decoder; -  if (mixed err = catch (decoder = Locale.Charset.decoder (charset))) { +  Charset.Decoder decoder; +  if (mixed err = catch (decoder = Charset.decoder (charset))) {    err_msg = sprintf ("Unrecognized charset %q.\n", charset);    break proc;    }    if (mixed err = catch (body = decoder->feed (body)->drain())) {    if (objectp (err) && err->is_charset_decode_error) {    err_msg = describe_error (err);    break proc;    }    throw (err);    }
Roxen.git/server/etc/modules/Roxen.pmod:1365:   //! eight bit chars and wider. All variable names and values in   //! @[variables] are thoroughly encoded using @[http_encode_url] so   //! they should not be encoded in any way to begin with.   {    // If the URL is a local relative URL we make it absolute.    url = fix_relative(url, id);       // Add protocol and host to local absolute URLs.    if (has_prefix (url, "/")) {    if(id) { -  url = id->url_base() + url[1..]; +  url = (string)Standards.URI(url, id->url_base());    if (!prestates) prestates = id->prestate;    }    else {    // Ok, no domain present in the URL and no ID object given.    // Perhaps one should dare throw an error here, but since most    // UA can handle the redirect it is nicer no to.    }    }       if(prestates && sizeof(prestates))
Roxen.git/server/etc/modules/Roxen.pmod:1422:   //! use in the response. It's @[Protocols.HTTP.HTTP_FOUND] (i.e. 302)   //! by default.   {    // If we don't get any URL we don't know what to do.    // But we do! /per    if(!url)    url = "";       url = make_absolute_url (url, id, prestates, variables);    +  url = http_encode_invalids(url); +     HTTP_WERR("Redirect -> "+url);       return http_status( http_code || Protocols.HTTP.HTTP_FOUND, -  "Redirect to " + url) +  "Redirect to " + html_encode_string(url))    + ([ "extra_heads":([ "Location":url ]) ]);   }      mapping http_stream(Stdio.File from)   //! Returns a result mapping where the data returned to the client   //! will be streamed raw from the given Stdio.File object, instead of   //! being packaged by roxen. In other words, it's entirely up to you   //! to make sure what you send is HTTP data.   {    return ([ "raw":1, "file":from, "len":-1, ]);
Roxen.git/server/etc/modules/Roxen.pmod:1748:    new["CONTENT_LENGTH"]=(string)strlen(id->data);    }       if(id->query && strlen(id->query))    new["INDEX"]=id->query;       new["REQUEST_METHOD"]=id->method||"GET";    new["SERVER_PORT"] = id->my_fd?    ((id->my_fd->query_address(1)||"foo unknown")/" ")[1]: "Internal";    +  // Protect against execution of arbitrary code in broken bash. +  foreach(new; string e; string v) { +  if (has_prefix(v, "() {")) { +  report_warning("ENV: Function definition in environment variable:\n" +  "ENV: %O=%O\n", +  e, v); +  new[e] = " " + v; +  } +  } +     return new;   }      mapping build_roxen_env_vars(RequestID id)   //! Generate a mapping with additional environment variables suitable   //! for use with CGI-scripts or SSI scripts etc. These variables are   //! roxen extensions and not defined in any standard document.   //! Specifically:   //! @pre{   //! For each cookie: COOKIE_cookiename=cookievalue
Roxen.git/server/etc/modules/Roxen.pmod:1840:       foreach(indices(id->supports), tmp)    {    tmp = mk_env_var_name(tmp-",");    new["SUPPORTS_"+tmp]="true";    if (new["SUPPORTS"])    new["SUPPORTS"] += " " + tmp;    else    new["SUPPORTS"] = tmp;    } +  +  // Protect against execution of arbitrary code in broken bash. +  foreach(new; string e; string v) { +  if (has_prefix(v, "() {")) { +  report_warning("ENV: Function definition in environment variable:\n" +  "ENV: %O=%O\n", +  e, v); +  new[e] = " " + v; +  } +  } +     return new;   }      string strip_config(string from)   //! Remove all 'config' data from the given (local) URL.   {    sscanf(from, "/<%*s>%s", from);    return from;   }   
Roxen.git/server/etc/modules/Roxen.pmod:2555:   //! Encodes the time `t' according to the format string `fmt'.   {    if(!sizeof(fmt)) return "";    mapping lt = localtime(t);    fmt=replace(fmt, "%%", "\0");    array(string) a = fmt/"%";    string res = a[0];    mapping(string:string) m = (["type":"string"]);       foreach(a[1..], string key) { -  if(key=="") continue; +  m_delete (m, "case");    int(0..1) prefix = 1; -  if(key[0] == '!' && sizeof(key) > 1) { +  int(0..1) alternative_numbers = 0; +  int(0..1) alternative_form = 0; +  while (sizeof(key)) { +  switch(key[0]) { +  // Flags. +  case '!': // Inhibit numerical padding (Roxen specific). +  case '-': // Inhibit numerical padding (glibc-style).    prefix = 0;    key = key[1..]; -  } -  switch(key[0]) { +  continue; +  case '^': // Upper-case (glibc-style). +  m->case = "upper"; +  key = key[1..]; +  continue; +  case '~': // Capitalize (Roxen specific). +  m->case = "capitalize"; +  key = key[1..]; +  continue; +  case 'E': // Locale-dependent alternative form. +  alternative_form = 1; +  key = key[1..]; +  continue; +  case 'O': // Locale-dependent alternative numeric representation. +  alternative_numbers = 1; +  key = key[1..]; +  continue; +  +  // Formats.    case 'a': // Abbreviated weekday name    if (language)    res += number2string(lt->wday+1,m,language(lang,"short_day",id));    else    res += ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" })[lt->wday];    break;    case 'A': // Weekday name    if (language)    res += number2string(lt->wday+1,m,language(lang,"day",id));    else
Roxen.git/server/etc/modules/Roxen.pmod:2584:    break;    case 'b': // Abbreviated month name    case 'h': // Abbreviated month name    if (language)    res += number2string(lt->mon+1,m,language(lang,"short_month",id));    else    res += ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun",    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" })[lt->mon];    break;    case 'B': // Month name -  if (language) +  if (language) { +  if (alternative_form) { +  res += number2string(lt->mon+1,m,language(lang,"numbered_month",id)); +  } else {    res += number2string(lt->mon+1,m,language(lang,"month",id)); -  else +  } +  } else    res += ({ "January", "February", "March", "April", "May", "June",    "July", "August", "September", "October", "November", "December" })[lt->mon];    break;    case 'c': // Date and time -  +  // FIXME: Should be preferred date and time for the locale.    res += strftime(sprintf("%%a %%b %02d %02d:%02d:%02d %04d",    lt->mday, lt->hour, lt->min, lt->sec, 1900 + lt->year), t);    break;    case 'C': // Century number; 0-prefix    res += my_sprintf(prefix, "%02d", 19 + lt->year/100);    break;    case 'd': // Day of month [1,31]; 0-prefix    res += my_sprintf(prefix, "%02d", lt->mday);    break;    case 'D': // Date as %m/%d/%y    res += strftime("%m/%d/%y", t);    break;    case 'e': // Day of month [1,31]; space-prefix    res += my_sprintf(prefix, "%2d", lt->mday);    break; -  case 'E': -  case 'O': -  key = key[1..]; // No support for E or O extension. +  case 'F': // ISO 8601 date %Y-%m-%d +  res += sprintf("%04d-%02d-%02d", +  1900 + lt->year, lt->mon + 1, lt->mday);    break; -  +  case 'G': // Year for the ISO 8601 week containing the day. +  { +  int wday = (lt->wday + 1)%7; // ISO 8601 weekday number. +  if ((wday - lt->yday) >= 4) { +  // The day belongs to the last week of the previous year. +  res += my_sprintf(prefix, "%04d", 1899 + lt->year); +  } else if ((lt->mon == 11) && ((lt->mday - wday) >= 29)) { +  // The day belongs to the first week of the next year. +  res += my_sprintf(prefix, "%04d", 1901 + lt->year); +  } else { +  res += my_sprintf(prefix, "%04d", 1900 + lt->year); +  } +  } +  break; +  case 'g': // Short year for the ISO 8601 week containing the day. +  { +  int wday = (lt->wday + 1)%7; // ISO 8601 weekday number. +  if ((wday - lt->yday) >= 4) { +  // The day belongs to the last week of the previous year. +  res += my_sprintf(prefix, "%02d", (99 + lt->year) % 100); +  } else if ((lt->mon == 11) && ((lt->mday - wday) >= 29)) { +  // The day belongs to the first week of the next year. +  res += my_sprintf(prefix, "%02d", (1 + lt->year) % 100); +  } else { +  res += my_sprintf(prefix, "%02d", (lt->year) % 100); +  } +  } +  break;    case 'H': // Hour (24-hour clock) [0,23]; 0-prefix    res += my_sprintf(prefix, "%02d", lt->hour);    break;    case 'I': // Hour (12-hour clock) [1,12]; 0-prefix    res += my_sprintf(prefix, "%02d", 1 + (lt->hour + 11)%12);    break;    case 'j': // Day number of year [1,366]; 0-prefix    res += my_sprintf(prefix, "%03d", lt->yday);    break;    case 'k': // Hour (24-hour clock) [0,23]; space-prefix
Roxen.git/server/etc/modules/Roxen.pmod:2640:    break;    case 'n': // Newline    res += "\n";    break;    case 'p': // a.m. or p.m.    res += lt->hour<12 ? "a.m." : "p.m.";    break;    case 'P': // am or pm    res += lt->hour<12 ? "am" : "pm";    break; +  case 'q': // Quarter number [1,4] (Roxen-specific) +  res += (string) ((lt->mon / 3) + 1); +  break;    case 'r': // Time in 12-hour clock format with %p -  res += strftime("%l:%M %p", t); +  res += strftime("%I:%M:%S %p", t);    break;    case 'R': // Time as %H:%M    res += sprintf("%02d:%02d", lt->hour, lt->min);    break; -  +  case 's': // Seconds since epoch. +  res += my_sprintf(prefix, "%d", t); +  break;    case 'S': // Seconds [00,61]; 0-prefix    res += my_sprintf(prefix, "%02d", lt->sec);    break;    case 't': // Tab    res += "\t";    break;    case 'T': // Time as %H:%M:%S -  case 'X': +  case 'X': // FIXME: Time in locale preferred format.    res += sprintf("%02d:%02d:%02d", lt->hour, lt->min, lt->sec);    break; -  case 'u': // Weekday as a decimal number [1,7], Sunday == 1 -  res += my_sprintf(prefix, "%d", lt->wday + 1); +  case 'u': // Weekday as a decimal number [1,7], Monday == 1 +  res += my_sprintf(prefix, "%d", 1 + ((lt->wday + 6) % 7));    break; -  +  case 'U': // Week number of current year [00,53]; 0-prefix +  // Sunday is first day of week. +  res += my_sprintf(prefix, "%02d", 1 + (lt->yday - lt->wday)/ 7); +  break; +  case 'V': // ISO week number of the year as a decimal number [01,53]; 0-prefix +  res += my_sprintf(prefix, "%02d", Calendar.ISO.Second(t)->week_no()); +  break;    case 'w': // Weekday as a decimal number [0,6], Sunday == 0    res += my_sprintf(prefix, "%d", lt->wday);    break; -  +  case 'W': // Week number of year as a decimal number [00,53], +  // with Monday as the first day of week 1; 0-prefix +  res += my_sprintf(prefix, "%02d", ((lt->yday+(5+lt->wday)%7)/7)); +  break;    case 'x': // Date -  +  // FIXME: Locale preferred date format.    res += strftime("%a %b %d %Y", t);    break;    case 'y': // Year [00,99]; 0-prefix    res += my_sprintf(prefix, "%02d", lt->year % 100);    break;    case 'Y': // Year [0000.9999]; 0-prefix    res += my_sprintf(prefix, "%04d", 1900 + lt->year);    break; -  -  case 'U': // Week number of year as a decimal number [00,53], -  // with Sunday as the first day of week 1; 0-prefix -  res += my_sprintf(prefix, "%02d", ((lt->yday-1+lt->wday)/7)); +  case 'z': // Time zone as hour offset from UTC. +  // Needed for RFC822 dates. +  { +  int minutes = lt->timezone/60; +  int hours = minutes/60; +  minutes -= hours * 60; +  res += my_sprintf(prefix, "%+05d%", hours*100 + minutes); +  }    break; -  case 'V': // ISO week number of the year as a decimal number [01,53]; 0-prefix -  res += my_sprintf(prefix, "%02d", Calendar.ISO.Second(t)->week_no()); -  break; -  case 'W': // Week number of year as a decimal number [00,53], -  // with Monday as the first day of week 1; 0-prefix -  res += my_sprintf(prefix, "%02d", ((lt->yday+(5+lt->wday)%7)/7)); -  break; +     case 'Z': // FIXME: Time zone name or abbreviation, or no bytes if    // no time zone information exists -  +  break;    }    res+=key[1..]; -  +  break;    } -  +  }    return replace(res, "\0", "%");   }      RoxenModule get_module (string modname)   //! Resolves a string as returned by get_modname to a module object if   //! one exists.   {    string cname, mname;    int mid = 0;   
Roxen.git/server/etc/modules/Roxen.pmod:2742:    sscanf (module->module_local_id(), "%*s#%d", int mod_copy);    if (mod_copy) name += " # " + mod_copy;    }    if (mappingp (name))    name = name->standard;    return (string) name;    }    else return 0;   }    - static constant xml_invalid_mappings = ([ + protected constant xml_invalid_mappings = ([    "\0":"\22000", "\1":"\22001",    "\2":"\22002", "\3":"\22003",    "\4":"\22004", "\5":"\22005",    "\6":"\22006", "\7":"\22007",    "\b":"\22010", "\13":"\22013",    "\14":"\22014", "\16":"\22016",    "\17":"\22017", "\20":"\22020",    "\21":"\22021", "\22":"\22022",    "\23":"\22023", "\24":"\22024",    "\25":"\22025", "\26":"\22026",
Roxen.git/server/etc/modules/Roxen.pmod:2782:   {    switch (encoding) {    case "":    case "none":    return val;       case "utf8":    case "utf-8":    return string_to_utf8(val);    +  case "-utf8": +  case "-utf-8": +  if( catch { +  return utf8_to_string(val); +  }) +  RXML.run_error("Cannot decode utf-8 string. Bad data.\n"); +     case "utf16":    case "utf16be": -  return Locale.Charset.encoder("utf16be")->feed(val)->drain(); +  return Charset.encoder("utf16be")->feed(val)->drain();       case "utf16le": -  return Locale.Charset.encoder("utf16le")->feed(val)->drain(); +  return Charset.encoder("utf16le")->feed(val)->drain();       case "hex":    if(String.width(val) > 8)    RXML.run_error( "Cannot hex encode wide characters.\n" );    return String.string2hex(val);    -  +  case "-hex": +  if( catch { +  return String.hex2string(val); +  }) +  RXML.run_error("Cannot decode hex string. Bad data.\n"); +     case "base64":    case "base-64":    case "b64": -  +  // RFC 4648: 3.1 Line Feeds in Encoded Data +  // +  // Implementations MUST NOT add line feeds to base-encoded data +  // unless the specification referring to this document explicitly +  // directs base encoders to add line feeds after a specific number +  // of characters. +  return MIME.encode_base64(val, 1); +  +  case "base64mime": +  case "base-64-mime": +  case "b64mime":    return MIME.encode_base64(val);    -  +  case "-base64": +  case "-base-64": +  case "-b64": +  case "-base64mime": +  case "-base-64-mime": +  case "-b64mime": +  if( catch { +  return MIME.decode_base64(val); +  }) +  RXML.run_error("Cannot decode base64 string. Bad data.\n"); +  +  case "base64url": +  case "base-64-url": +  case "b64url": + #if constant (MIME.encode_base64url) +  return MIME.encode_base64url(val); + #else +  return replace (MIME.encode_base64 (val, 1), +  ([ "=": "", +  "+": "-", +  "/": "_" ])); + #endif +  case "-base64url": +  case "-base-64-url": +  case "-b64url": +  if( catch { + #if constant (MIME.decode_base64url) +  return MIME.decode_base64url(val); + #else +  string data = replace (val, ([ "-": "+", +  "_": "/", +  "=": "" ])); +  data = (data + ("=" * (4-sizeof (data) % 4))); // Add padding. +  return MIME.decode_base64 (data); + #endif +  }) +  RXML.run_error("Cannot decode base64 string. Bad data.\n"); +     case "md5":    case "sha1":    case "sha256":    if (String.width(val) > 8)    RXML.run_error("Cannot hash wide characters.\n");    return Crypto[upper_case(encoding)]->hash(val);       case "quotedprintable":    case "quoted-printable":    case "qp":
Roxen.git/server/etc/modules/Roxen.pmod:2820:       case "http":    return http_encode_invalids (val);       case "cookie":    return http_encode_cookie (val);       case "url":    return http_encode_url (val);    +  case "-url": +  return http_decode_string (val); +     case "wml-url":    // Note: In 4.0 and earlier, this encoding was ambiguous since 8    // bit strings were %-encoded according to the ISO 8859-1 charset    // while wider strings first were UTF-8 encoded and then    // %-encoded. Although unlikely, it might be possible that the    // old ambiguous encoding is the one mandated by the WAP/WML    // standard - I haven't been able to verify it. /mast    return http_encode_url(val);       case "html":    return html_encode_string (val); -  +  case "-html": +  // Can't use html_decode_string() which doesn't understand numerical +  // entities. +  return RXML.TXml()->decode_charrefs(val);       case "invalids":    case "xmlinvalids":    case "xml-invalids":    return encode_xml_invalids(val);       case "wml":    return replace(html_encode_string(val), "$", "$$");       case "dtag":
Roxen.git/server/etc/modules/Roxen.pmod:2944:   //! the following:   //! @string   //! @value ""   //! @value "none"   //! No encoding.   //!   //! @value "utf8"   //! @value "utf-8"   //! UTF-8 encoding. C.f. @[string_to_utf8].   //! + //! @value "-utf8" + //! @value "-utf-8" + //! UTF-8 decoding. C.f. @[utf8_to_string]. + //!   //! @value "utf16"   //! @value "utf16be" - //! (Big endian) UTF-16 encoding. C.f. @[Locale.Charset], encoder + //! (Big endian) UTF-16 encoding. C.f. @[Charset], encoder   //! @expr{"utf16be"@}.   //!   //! @value "utf16le" - //! Little endian UTF-16 encoding. C.f. @[Locale.Charset], encoder + //! Little endian UTF-16 encoding. C.f. @[Charset], encoder   //! @expr{"utf16le"@}.   //!   //! @value "hex"   //! Hexadecimal encoding, e.g. @expr{"foo"@} is encoded to   //! @expr{"666f6f"@}. Requires octet (i.e. non-wide) strings.   //! C.f. @[String.string2hex].   //! -  + //! @value "-hex" + //! Hexadecimal decoding, e.g. @expr{"666f6f"@} is decoded to + //! @expr{"foo"@}. + //! C.f. @[String.hex2string]. + //!   //! @value "base64"   //! @value "base-64"   //! @value "b64" - //! Base-64 MIME encoding. Requires octet (i.e. non-wide) strings. + //! Base-64 encoding (@rfc{4648@}). Requires octet (i.e. non-wide) strings.   //! C.f. @[MIME.encode_base64].   //! -  + //! @value "base64mime" + //! @value "base-64-mime" + //! @value "b64mime" + //! Base-64 MIME content transfer encoding. Requires octet (i.e. + //! non-wide) strings. This encoding differs from @expr{"base64"@} + //! in that it enforces a maximum line length of 76 characters. + //! C.f. @[MIME.encode_base64]. + //! + //! @value "-base64" + //! @value "-base-64" + //! @value "-b64" + //! @value "-base64mime" + //! @value "-base-64mime" + //! @value "-b64mime" + //! Base-64 decoding (@rfc{4648@}). + //! C.f. @[MIME.decode_base64]. + //! + //! @value "base64url" + //! @value "base-64-url" + //! @value "b64url" + //! Base-64 URL encoding. Requires octet (i.e. non-wide) strings. + //! C.f. @[MIME.encode_base64url]. + //! + //! @value "-base64url" + //! @value "-base-64-url" + //! @value "-b64url" + //! Base-64 URL decoding (@rfc{4648@}). + //! C.f. @[MIME.decode_base64url]. + //!   //! @value "md5"   //! @value "sha1"   //! @value "sha256"   //! Message digest using supplied hash algorithm. Requires octet   //! (i.e. non-wide) strings. Note that the result is a binary string   //! so apply e.g. hex encoding afterward to get a printable value.   //! C.f. @[Crypto.MD5.hash], @[Crypto.SHA1.hash] and   //! @[Crypto.SHA256.hash].   //!   //! @value "quotedprintable"
Roxen.git/server/etc/modules/Roxen.pmod:2992:   //! URL-special chars, including @expr{%@}, are not encoded. 8-bit   //! and wider chars are encoded according to the IRI standard (RFC   //! 3987). C.f. @[Roxen.http_encode_invalids].   //!   //! @value "url"   //! Similar to the @expr{"http"@} encoding, but encodes all URI   //! reserved and excluded chars, that otherwise could have special   //! meaning; see RFC 3986. This includes @expr{:@}, @expr{/@},   //! @expr{%@}, and quote chars. C.f. @[Roxen.http_encode_url].   //! + //! @value "-url" + //! Reverse @expr{"url"@} encoding but without utf-8 decoding. If + //! utf-8 decoding is needed you can use the combination + //! @expr{"-url.-utf8"@}. + //!   //! @value "cookie"   //! Nonstandard HTTP-style encoding for cookie values. The Roxen   //! HTTP protocol module automatically decodes incoming cookies   //! using this encoding, so by using this for @expr{Set-Cookie@}   //! headers etc you will get back the original value in the   //! @expr{cookie@} scope. Note that @[Roxen.set_cookie] and the   //! RXML @expr{<set-cookie>@} tag already does this encoding for   //! you. C.f. @[Roxen.http_encode_cookie].   //!   //! @value "html"   //! HTML encoding, for generic text in html documents. This means   //! encoding chars like @expr{<@}, @expr{&@}, and quotes using   //! character reference entities.   //! -  + //! @value "-html" + //! HTML decoding of entities (literals and decimal/hexadecimal + //! representations). + //!   //! @value "wml"   //! HTML encoding, and doubling of any @tt{$@}'s.   //!   //! @value "csv"   //! CSV (Comma Separated Values) encoding. Properly quotes all   //! separator characters in CSV records (comma, semicolon, double-quotes   //! leading spaces and newlines).   //!   //! @value "pike"   //! Pike string quoting, for use in e.g. the @tt{<pike></pike>@}
Roxen.git/server/etc/modules/Roxen.pmod:3100:    return val;   }      string fix_relative( string file, RequestID|void id )   //! Using @expr{@[id]->not_query@}, turns a relative (or already   //! absolute) virtual path into an absolute virtual path, i.e. one   //! rooted at the virtual server's root directory. The returned path   //! is simplified to not contain any @expr{"."@} or @expr{".."@}   //! segments.   { -  Standards.URI uri = Standards.URI("://"); +  Standards.URI uri = Standards.URI(":///");    if (id) {    uri = Standards.URI(id->not_query, uri);    }    uri = Standards.URI(file, uri); -  +  if (uri->path != "") { +  // NB: Without the above test "" will expand to "/". +  // cf [bug 7566].    uri->path = (uri->combine_uri_path("", uri->path)/"/" - ({ ".." })) * "/"; -  +  }    string res = sprintf("%s", uri);    // +(id->misc->path_info?id->misc->path_info:"");    if (has_prefix(res, "://") && !has_prefix(file, "://") &&    (!id || !has_prefix(id->not_query, "://"))) {    // No scheme.    if (!has_prefix(file, "//") &&    (!id || !has_prefix(id->not_query, "//"))) {    // No host.    return res[sizeof("://")..];    }
Roxen.git/server/etc/modules/Roxen.pmod:3255:    if(m->time)    return sprintf("%02d:%02d:%02d", eris->hour, eris->min, eris->sec);       return sprintf("%d-%02d-%02dT%02d:%02d:%02d",    (eris->year+1900), eris->mon+1, eris->mday,    eris->hour, eris->min, eris->sec);       case "http":    return http_date (t);    + #pragma no_deprecation_warnings    case "discordian":   #if constant (spider.discdate)    array(string) not=spider.discdate(t);    res=not[0];    if(m->year)    res += " in the YOLD of "+not[1];    if(m->holiday && not[2])    res += ". Celebrate "+not[2];    return res;   #else
Roxen.git/server/etc/modules/Roxen.pmod:3306: Inside #if defined(old_rxml_compat)
   ", contains lower attribute in a tag. Use case=\"lower\" instead.");    }    if (m->cap||m->capitalize) {    res=capitalize(res);    report_warning("Old RXML in "+(id->query||id->not_query)+    ", contains capitalize or cap attribute in a tag. Use case=\"capitalize\" instead.");    }   #endif    return res;   } + #pragma deprecation_warnings      int time_dequantifier(mapping m, void|int t )    //! Calculates an integer with how many seconds a mapping    //! that maps from time units to an integer can be collapsed to.    //! E.g. (["minutes":"2"]) results in 120.    //! Valid units are seconds, minutes, beats, hours, days, weeks,    //! months and years.   {    int initial = t;    if (m->seconds) t+=(int)(m->seconds);
Roxen.git/server/etc/modules/Roxen.pmod:3414:   class _charset_decoder(object cs)   {    string decode(string what)    {    return cs->clear()->feed(what)->drain();    }   }      protected class CharsetDecoderWrapper   { -  protected object decoder; +  protected Charset.Decoder decoder;    string charset;       protected void create (string cs)    {    // Would be nice if it was possible to get the canonical charset -  // name back from Locale.Charset so we could use that instead in -  // the client_charset_decoders cache mapping. -  decoder = Locale.Charset.decoder (charset = cs); +  // name back from Charset so we could use that instead in the +  // client_charset_decoders cache mapping. +  decoder = Charset.decoder (charset = cs);    }       string decode (string what)    { -  object d = decoder; +  Charset.Decoder d = decoder;    // Relying on the interpreter lock here.    decoder = 0;    if (d) d->clear(); -  else d = Locale.Charset.decoder (charset); +  else d = Charset.decoder (charset);    string res = d->feed (what)->drain();    decoder = d;    return res;    }   }      protected multiset(string) charset_warned_for = (<>);      constant magic_charset_variable_placeholder = "UTF-8";   constant magic_charset_variable_value = "åäö&#x829f;@" + magic_charset_variable_placeholder;      protected mapping(string:function(string:string)) client_charset_decoders = ([    "http": http_decode_string,    "html": Parser.parse_html_entities,    "utf-8": utf8_to_string,    "utf-16": unicode_to_string, -  +  0: `+, // Identity function for strings.   ]);      protected function(string:string) make_composite_decoder (    function(string:string) outer, function(string:string) inner)   {    // This is put in a separate function to minimize the size of the    // dynamic frame for this lambda.    return lambda (string what) {    return outer (inner (what));    };
Roxen.git/server/etc/modules/Roxen.pmod:3635:      // Low-level C-roxen optimization functions.   inherit _Roxen;      // This symbol is added by roxenloader if an old _Roxen.make_http_headers()   // is detected.   #if constant(HAVE_OLD__Roxen_make_http_headers)   string make_http_headers(mapping(string:string|array(string)) heads,    int(0..1)|void no_terminator)   { -  string res = ::make_http_headers(heads); -  if (no_terminator) { -  // Remove the terminating CRLF. -  return res[..sizeof(res)-3]; +  foreach(heads; string key; string|array(string) val) { +  if (has_value(key, "\n") || has_value(key, "\r") || +  has_value(key, ":") || has_value(key, " ") || has_value(key, "\t")) { +  error("Invalid headername: %O (value: %O)\n", key, val);    } -  return res; +  if (stringp(val) && (has_value(val, "\n") || has_value(val, "\r"))) { +  error("Invalid value for header %O: %O\n", key, val);    } -  +  if (arrayp(val)) { +  foreach(val, string v) { +  if (has_value(v, "\n") || has_value(v, "\r")) { +  error("Invalid value for header %O: %O\n", key, val); +  } +  } +  } +  } +  return ::make_http_headers(heads, no_terminator); + }   #endif /* constant(HAVE_OLD__Roxen_make_http_headers) */      /*    * TODO:    *    * o Quota: Fix support for the index file.    *    */      #ifdef QUOTA_DEBUG   #define QD_WRITE(X) report_debug(X)   #else /* !QUOTA_DEBUG */   #define QD_WRITE(X)   #endif /* QUOTA_DEBUG */         class QuotaDB   { - #if constant(thread_create) +     object(Thread.Mutex) lock = Thread.Mutex();   #define LOCK() mixed key__; catch { key__ = lock->lock(); }   #define UNLOCK() do { if (key__) destruct(key__); } while(0) - #else /* !constant(thread_create) */ - #define LOCK() - #define UNLOCK() - #endif /* constant(thread_create) */ +        constant READ_BUF_SIZE = 256;    constant CACHE_SIZE_LIMIT = 512;       string base;       object catalog_file;    object data_file;       mapping(string:int) new_entries_cache = ([]);
Roxen.git/server/etc/modules/Roxen.pmod:4219:    }       string name()    {    RXML.Context ctx = RXML.get_context( );    return scope == "_" ? ctx->current_scope() : scope;    }       protected mixed `[]( string what )    { +  // NB: This function may be called by eg master()->describe_object() +  // with symbols such as "is_resolv_dirnode", in contexts where +  // the scope doesn't exist. cf [bug 6451].    RXML.Context ctx = RXML.get_context( ); -  return ctx->get_var( what, scope ); +  return ctx->scopes[scope || "_"] && ctx->get_var( what, scope );    }       protected mixed `->( string what )    {    return `[]( what );    }       protected mixed `[]=( string what, mixed nval )    {    RXML.Context ctx = RXML.get_context( );
Roxen.git/server/etc/modules/Roxen.pmod:4403:       mixed val = c->misc->scope_roxen[var];    if(!zero_type(val))    {    if (objectp(val) && val->rxml_var_eval) return val;    return ENCODE_RXML_TEXT(val, type);    }       switch(var)    { +  case "dist-patch-version": +  string patch_ver = roxen->plib->get_current_patch_version(); +  return ENCODE_RXML_TEXT( +  roxen_dist_version + (patch_ver ? "-" + patch_ver : ""), type); +  case "patch-version": +  return ENCODE_RXML_TEXT(roxen->plib->get_current_patch_version(), type);    case "nodename":    return uname()->nodename;    case "uptime":    c->id->lower_max_cache (1);    return ENCODE_RXML_INT(time(1)-roxenp()->start_time, type);    case "uptime-days":    c->id->lower_max_cache (3600 * 2);    return ENCODE_RXML_INT((time(1)-roxenp()->start_time)/3600/24, type);    case "uptime-hours":    c->id->lower_max_cache (1800);
Roxen.git/server/etc/modules/Roxen.pmod:4494:    object key = c->id->conf->getvar("license")->get_key();    return ENCODE_RXML_TEXT(key?sizeof(key->get_warnings()):0, type);    }       case "auto-charset-variable":    return ENCODE_RXML_TEXT("magic_roxen_automatic_charset_variable", type);    case "auto-charset-value":    return ENCODE_RXML_TEXT(magic_charset_variable_value, type);       case "null": +  // Note that we don't need to check compat_level < 5.2 and +  // return compat_5_1_null here, since this constant didn't exist +  // prior to 5.2.    return Val->null;    case "true":    return Val->true;    case "false":    return Val->false;    }       return RXML.nil;    }   
Roxen.git/server/etc/modules/Roxen.pmod:4538:   }      int get_ssl_strength(string ignored, RequestID id)   {    if (!id->my_fd || !id->my_fd->get_peer_certificate_info ||    !id->my_fd->query_connection())    return 0;    return id->my_fd->query_connection()->session->cipher_spec->key_bits;   }    + string hash_query_data(string ignored, RequestID id) + { +  // Some common cases. +  if (!id->data) return 0; +  if (zero_type(id->misc->len)) return 0; +  if (id->data == "") return ""; +  return Crypto.SHA1.hash(id->data); + } +    class ScopePage {    inherit RXML.Scope;    constant converter=(["fgcolor":"fgcolor", "bgcolor":"bgcolor",    "theme-bgcolor":"theme_bgcolor", "theme-fgcolor":"theme_fgcolor",    "theme-language":"theme_language"]);       mixed `[] (string var, void|RXML.Context c, void|string scope, void|RXML.Type type) {    if (!c) c = RXML_CONTEXT;       mixed val;
Roxen.git/server/etc/modules/Roxen.pmod:4559:    val = c->misc[converter[var]];    else    val = c->misc->scope_page[var];    if(!zero_type(val))    {    if (objectp (val) && val->rxml_var_eval)    return val;    return ENCODE_RXML_TEXT(val, type);    }    +  string get_mountpoint() +  { +  string s = c->id->virtfile || ""; +  return ENCODE_RXML_TEXT(s[sizeof(s)-1..sizeof(s)-1] == "/"? s[..sizeof(s)-2]: s, type); +  }; +     switch (var) {    case "pathinfo": return ENCODE_RXML_TEXT(c->id->misc->path_info, type);    case "realfile": return ENCODE_RXML_TEXT(c->id->realfile, type);    case "virtroot": return ENCODE_RXML_TEXT(c->id->virtfile, type); -  +  case "mountpoint-ver": +  string patch_ver = roxen->plib->get_current_patch_version(); +  return "/(" + +  ENCODE_RXML_TEXT( +  roxen_dist_version + (patch_ver ? "-" + patch_ver : ""), type) + +  ")" + +  get_mountpoint();    case "mountpoint": -  string s = c->id->virtfile || ""; -  return ENCODE_RXML_TEXT(s[sizeof(s)-1..sizeof(s)-1] == "/"? s[..sizeof(s)-2]: s, type); +  return get_mountpoint();    case "virtfile": // Fallthrough from deprecated name.    case "path": return ENCODE_RXML_TEXT(c->id->not_query, type);    case "query": return ENCODE_RXML_TEXT(c->id->query, type);    case "url": return ENCODE_RXML_TEXT(c->id->raw_url, type); -  +  case "post-data": +  c->id->register_vary_callback(0, hash_query_data); +  if (!c->id->data || zero_type(c->id->misc->len)) return RXML.nil; +  return ENCODE_RXML_TEXT(c->id->data, type);    case "last-true": return ENCODE_RXML_INT(c->misc[" _ok"], type);    case "language": return ENCODE_RXML_TEXT(c->misc->language, type);    case "scope": return ENCODE_RXML_TEXT(c->current_scope(), type);    case "filesize": return ENCODE_RXML_INT(c->misc[" _stat"]?    c->misc[" _stat"][1]:-4, type);    case "self": return ENCODE_RXML_TEXT( (c->id->not_query/"/")[-1], type);    case "ssl-strength":    c->id->register_vary_callback("host", get_ssl_strength);    return ENCODE_RXML_INT(get_ssl_strength("", c->id), type);    case "dir":
Roxen.git/server/etc/modules/Roxen.pmod:4603:    }    if(converter[var])    return c->misc[converter[var]]=val;    return c->misc->scope_page[var]=val;    }       array(string) _indices(void|RXML.Context c) {    if (!c) c = RXML_CONTEXT;    array ind=indices(c->misc->scope_page) +    ({ "pathinfo", "realfile", "virtroot", "mountpoint", "virtfile", "path", "query", -  "url", "last-true", "language", "scope", "filesize", "self", +  "url", "post-data", "last-true", "language", "scope", "filesize", "self",    "ssl-strength", "dir", "counter" });    foreach(indices(converter), string def)    if(c->misc[converter[def]]) ind+=({def});    return Array.uniq(ind);    }       void _m_delete (string var, void|RXML.Context c, void|string scope_name) {    if (!c) c = RXML_CONTEXT;    switch (var) {    case "pathinfo":
Roxen.git/server/etc/modules/Roxen.pmod:4952:   int httpdate_to_time(string date)   {    return parse_since(date)[0]||-1;   }      void set_cookie( RequestID id,    string name,    string value,    int|void expire_time_delta,    string|void domain, -  int(1..1)|string|void path, -  string|void secure, -  string|void httponly) +  int(0..1)|string|void path, +  int(0..1)|__deprecated__(string)|void secure, +  int(0..1)|__deprecated__(string)|void httponly)   //! Set the cookie specified by @[name] to @[value]. Adds a Set-Cookie   //! header in the response that will be made from @[id].   //! -  + //! @param id + //! @[RequestID] for which to set the cookie. + //! + //! @param name + //! Name of the cookie. + //! + //! @param value + //! Value of the cookie. + //!   //! @param expire_time_delta - //! If the expire_time_delta variable is -1, the cookie is set to - //! expire five years in the future. -2 will set the expiration time to - //! posix time 1 and add the argument Max-Age, set to 0. If expire_time_delta - //! is 0 or ommited, no expire information is sent to the client. This - //! usualy results in the cookie being kept until the browser is exited. + //! @int + //! @value 1.. + //! Number of seconds in the future that the cookie will expire. + //! @value 0 + //! @value UNDEFINED + //! If @[expire_time_delta] is @expr{0@} or ommited, no expire + //! information is sent to the client. This usually results in + //! the cookie being kept until the browser is exited. + //! @value -1 + //! Expire five years in the future. + //! @value -2 + //! Set the expiration time to POSIX time @expr{1@}, and set + //! the cookie Max-Age to @expr{0@}. Ie immediate expiry. + //! @endint   //! -  + //! @param domain + //! Extend the scope of the cookie to this domain. + //!   //! @param path   //! A path argument will always be added to the Set-Cookie header unless - //! @[path] is 1. It will otherwise be set to the provided string, or "" - //! if no string is provided. + //! @[path] is @expr{1@}. It will otherwise be set to the provided string, + //! or @expr{""@} if no string is provided. + //! + //! @param secure + //! Instruct the client to only provide the cookie over https connections. + //! + //! @param httponly + //! Instruct the client to not make the cookie available to client-side + //! scripting (ie Javascript). + //! + //! @note + //! The @[value] of the cookie will be passed through + //! @[http_encode_cookie()] before being sent to the client + //! this may affect client-side use of the cookie value.   {    if( expire_time_delta == -1 )    expire_time_delta = (3600*(24*365*5));    string cookie = (http_encode_cookie( name )+"="+    http_encode_cookie( value ));       if( expire_time_delta == -2 )    cookie += "; expires="+http_date(1)+"; Max-Age=0";    else if( expire_time_delta )    cookie += "; expires="+http_date( expire_time_delta+time(1) );       if( domain ) cookie += "; domain="+http_encode_cookie( domain );    if( path!=1 ) cookie += "; path="+http_encode_cookie( path||"" );    if( secure ) cookie += "; secure";    if( httponly ) cookie += "; HttpOnly";    id->add_response_header ("Set-Cookie", cookie);   }    -  + string get_wizard_id_cookie_name(RequestID id) + { +  bool secure = id->client_scheme && id->client_scheme() == "https"; +  return secure ? "RoxenHttpsWizardId" : "RoxenHttpWizardId"; + } +  + string set_wizard_id_cookie(RequestID id) + //! Sets the RoxenWizardId cookie and returns the value of the cookie. + //! + //! @param id + //! @[RequestID] for which to set the cookie. + //! + //! @returns + //! The value of the cookie. + { +  // Set the secure flag on the cookie if accessed over https [WS-135]. +  // NB: The cookie is used from Javascript, so it can't have +  // httponly set. +  string name = get_wizard_id_cookie_name(id); +  string value = sprintf("%08x", random(0x7fffffff)); +  int expire_time_delta = 0; +  string domain = 0; +  string path = "/"; +  int(0..1) secure = id->client_scheme && id->client_scheme() == "https"; +  int(0..1) httponly = 0; +  Roxen.set_cookie(id, +  name, +  value, +  expire_time_delta, +  domain, +  path, +  secure, +  httponly); +  id->cookies[name] = value; +  return value; + } +  + string get_wizard_id_cookie(RequestID id) + { +  return id->cookies[get_wizard_id_cookie_name(id)]; + } +    void remove_cookie( RequestID id,    string name,    string value,    string|void domain,    string|void path )   //! Remove the cookie specified by 'name'.   //! Sends a Set-Cookie header with an expire time of 00:00 1/1 1970.   //! The domain and path arguments are optional.   {    set_cookie( id, name, value, -2, domain, path );
Roxen.git/server/etc/modules/Roxen.pmod:5041:   //!   //! @note   //! If there is a @[RequestID] object available, you probably want to   //! call @[RequestID.url_base] in it instead, since that function also   //! takes into account information sent by the client and the port the   //! request came from. (It's often faster too.)   {    return c->get_url();   }    - static private array(string) local_addrs; + #ifndef NO_DNS + private array(string) local_addrs; + #endif      string get_world(array(string) urls) {    if(!sizeof(urls)) return 0;       string url = urls[0];    mapping(string:Standards.URI) uris = ([ ]);    foreach (urls, string u)    uris[u] = Standards.URI(u);       foreach( ({"http:","https:","ftp:"}), string p)
Roxen.git/server/etc/modules/Roxen.pmod:5129: Inside #if undefined(NO_DNS)
   if (addr && sizeof(addr)) {    addr = (addr/"/")[0]; // We don't care about the prefix bits.    addr = (addr/"%")[0]; // MacOS X.    local_addrs += ({ addr });    }    }    }    local_addrs = Array.uniq(local_addrs);    }    foreach(local_addrs, string addr) { +  // Shortcut some known aliases to avoid lengthy waits if DNS cannot +  // resolve them. +  if (addr == "127.0.0.1" || addr == "::1" || addr == "fe80::1") { +  if (addr != "fe80::1") +  hosts += ({ "localhost" }); +  break; +  } +     if ((dns = Protocols.DNS.gethostbyaddr(addr)) && sizeof(dns)) {    if (dns[0]) {    hosts += ({ dns[0] });    }    hosts += dns[1] + ({ addr });    if ((sizeof(dns[2]) != 1) || (dns[2][0] != addr)) {    hosts += dns[2];    }    }    }
Roxen.git/server/etc/modules/Roxen.pmod:5469:      Null null = Null();   //! Roxen replacement for @[Val.null] that adds rxml type conversions:   //! It's false in boolean tests, yields "" in a string context and 0   //! or 0.0, as appropriate, in a numeric context.      constant SqlNull = Null;   Val.Null sql_null;   // Compat aliases. sql_null is initialized in create() in roxen.pike.    + class Compat51Null + // Null object for compat_level < 5.2. Also inherits RXML.Nil, which + // among other things allows casting to empty values of various types. + { +  inherit Null; +  inherit RXML.Nil;    -  +  protected string _sprintf (int flag) +  { +  return flag == 'O' && "Roxen.compat_5_1_null"; +  } + } +  + Compat51Null compat_5_1_null = Compat51Null(); +  +    #ifdef REQUEST_TRACE   protected string trace_msg (mapping id_misc, string msg,    string|int name_or_time, int enter)   {    array(string) lines = msg / "\n";    if (lines[-1] == "") lines = lines[..<1];       if (sizeof (lines)) {   #if TOSTR (REQUEST_TRACE) == "TIMES"    string byline = sprintf ("%*s%c %s",
Roxen.git/server/etc/modules/Roxen.pmod:5714:    ctx_misc->wiretap_stack = c[..sizeof(c)-i-2];      #ifdef WIRETAP_TRACE    report_debug ("%*sPop wiretap stack for %O: tag=%O, fgcolor=%O, bgcolor=%O\n",    sizeof (c) * 2, "", id, tagname,    ctx_misc->fgcolor, ctx_misc->bgcolor);   #endif    }   }    - string generate_self_signed_certificate(string common_name) + #if constant(Standards.X509) +  + string generate_self_signed_certificate(string common_name, +  Crypto.Sign|void key)   {    int key_size = 4096; // Ought to be safe for a few years.    -  Crypto.RSA rsa = Crypto.RSA(); +  if (!key) { +  key = Crypto.RSA(); +  key->generate_key(key_size); +  } +  +  string key_type = key->name(); +  if (has_prefix(key_type, "ECDSA") || +  has_suffix(key_type, "ECDSA")) { +  key_type = "ECDSA"; +  } +  +  string key_pem = +  Standards.PEM.build(key_type + " PRIVATE KEY", +  Standards.PKCS[key_type].private_key(key)); +  +  // These are the fields used by testca.pem. +  array(mapping(string:object)) name = ({ +  ([ "organizationName": +  Standards.ASN1.Types.PrintableString("Roxen AB") +  ]), +  ([ "organizationUnitName": +  Standards.ASN1.Types.PrintableString("Automatic certificate") +  ]), +  ([ "commonName": +  (Standards.ASN1.Types.asn1_printable_valid(common_name)? +  Standards.ASN1.Types.PrintableString: +  Standards.ASN1.Types.BrokenTeletexString)(common_name) +  ]), +  }); +  +  int ttl = 3652; // 10 years. +  +  /* Create a plain X.509 v3 certificate, with just default extensions. */ +  string cert = +  Standards.X509.make_selfsigned_certificate(key, 24 * 3600 * ttl, name); +  +  return Standards.PEM.build("CERTIFICATE", cert) + key_pem; + } +  + #else + // NB: Several of the Tools.PEM and Tools.X509 APIs below + // have been deprecated in Pike 8.0. + #pragma no_deprecation_warnings +  + string generate_self_signed_certificate(string common_name, Crypto.RSA|void rsa) + { +  int key_size = 4096; // Ought to be safe for a few years. +  +  if (!rsa) { +  rsa = Crypto.RSA();    rsa->generate_key(key_size, Crypto.Random.random_string); -  +  }       string key = Tools.PEM.simple_build_pem ("RSA PRIVATE KEY",    Standards.PKCS.RSA.private_key(rsa));       // These are the fields used by testca.pem.    array(mapping(string:object)) name = ({    ([ "organizationName": -  Standards.ASN1.Types.asn1_printable_string("Roxen IS") +  Standards.ASN1.Types.asn1_printable_string("Roxen AB")    ]),    ([ "organizationUnitName":    Standards.ASN1.Types.asn1_printable_string("Automatic certificate")    ]),    ([ "commonName":    (Standards.ASN1.Types.asn1_printable_valid(common_name)?    Standards.ASN1.Types.asn1_printable_string:    Standards.ASN1.Types.asn1_broken_teletex_string)(common_name)    ]),    });       int ttl = 3652; // 10 years.       /* Create a plain X.509 v1 certificate, without any extensions */    string cert = Tools.X509.make_selfsigned_rsa_certificate    (rsa, 24 * 3600 * ttl, name);       return Tools.PEM.simple_build_pem("CERTIFICATE", cert) + key;   }    -  + #pragma deprecation_warnings + #endif /* Standards.X509 */ +    class LogPipe   //! The write end of a pipe that will log to the debug log. Use   //! @[get_log_pipe] to create an instance.   {    inherit Stdio.File;       protected string prefix = "";    protected string line_buf = "";       protected void read_cb (Stdio.File read_end, string data)
Roxen.git/server/etc/modules/Roxen.pmod:5787:    roxen->name_thread(this_thread(), "Log pipe");    while (1) {    string data = read_end->read (1024, 1);    if (!data || data == "") break;    read_cb (read_end, data);    }    close_cb (read_end);    roxen->name_thread(this_thread(), 0);    }    -  protected void create (Stdio.File read_end, Stdio.File write_end, -  int use_read_thread) +  protected void create (Stdio.File read_end, Stdio.File write_end)    { - #if constant(thread_create) -  if (use_read_thread) +     thread_create (log_pipe_read_thread, read_end); -  else - #endif -  { -  read_end->set_nonblocking (read_cb, 0, close_cb); -  read_end->set_id (read_end); -  } +     assign (write_end);    }       void set_prefix (string prefix)    //! Sets a string that will be prefixed to each line that is logged    //! via this pipe.    {    LogPipe::prefix = prefix;    }   }
Roxen.git/server/etc/modules/Roxen.pmod:5829:   //! @[Process.Process]. Otherwise the pipe will remain intact   //! after the process has exited and you'll get an fd leak.   //!   //! @note   //! The standard backend is used (when possible) to echo the data that   //! arrives on the pipe. If it's hung then data that arrives on the   //! pipe won't show in the debug log.   {    Stdio.File read_end = Stdio.File();    Stdio.File write_end; -  int use_read_thread; +     if (catch (write_end =    read_end->pipe (Stdio.PROP_IPC|Stdio.PROP_NONBLOCK))) {    // Some OS'es (notably Windows) can't create a nonblocking    // interprocess pipe.    read_end = Stdio.File();    write_end = read_end->pipe (Stdio.PROP_IPC); -  use_read_thread = 1; - #if 0 -  report_debug ("Using read thread with a blocking pipe for logging.\n"); - #endif +     }    if (!write_end) error ("Failed to create pipe: %s\n",    strerror (read_end->errno())); -  return LogPipe (read_end, write_end, use_read_thread); +  return LogPipe (read_end, write_end);   }    - constant DecodeError = Locale.Charset.DecodeError; - constant EncodeError = Locale.Charset.EncodeError; + constant DecodeError = Charset.DecodeError; + constant EncodeError = Charset.EncodeError;      mapping(string:int) get_memusage()   //! Returns a mapping of the memory used by the Roxen process.   //!   //! @returns   //! @mapping   //! @member string "resident"   //! Resident memory in KiB.   //! @member string "virtual"   //! Virtual memory in KiB.
Roxen.git/server/etc/modules/Roxen.pmod:5927:   //! If a segment ambiguously matches several entries in a directory   //! then it and all remaining segments are returned as-is. A warning   //! is also logged in this case, unless @[no_warn] is nonzero.   //!   //! The given path is assumed to be absolute, and it is normalized   //! with @[combine_path] before being checked. The returned paths   //! always have "/" as directory separators. If there is a trailing   //! slash then it is kept intact.   //!   //! If @[charset] is set then charset conversion is done: @[path] is - //! assumed to be a (possibly wide) unicode string, and @[charset] is + //! assumed to be a (possibly wide) unicode string in NFC, and @[charset] is   //! taken as the charset used in the file system. The returned path is - //! a unicode string as well. If @[charset] isn't specified then no - //! charset conversion is done anywhere, which means that @[path] must - //! have the same charset as the file system, and the case insensitive - //! comparisons only work in as far as @[lower_case] does the right - //! thing with that charset. + //! a unicode string as well. If @[charset] isn't specified then it + //! and the filesystem are assumed to be in utf-8, and the result will + //! be utf-8 encoded.   //!   //! If @[charset] is given then it's assumed to be a charset accepted - //! by @[Locale.Charset]. If there are charset conversion errors in - //! @[path] or in the file system then those paths are treated as - //! nonexisting. + //! by @[Charset]. If there are charset conversion errors in @[path] + //! or in the file system then those paths are treated as nonexisting.   //!   //! @note   //! Existing paths are cached without any time limit, but the cached   //! paths are always verified to still exist before being reused. Thus   //! the only overcaching effect that can occur is if the underlying   //! file system is case insensitive and some path segment only has   //! changed in case.   {    ASSERT_IF_DEBUG (is_absolute_path (path));       string cache_name = "case_insens_paths";       function(string:string) encode, decode; -  switch (charset) { +  switch (charset && lower_case(charset)) {    case 0: -  +  // NB: NT has a filesystem that uses UTF-16. + #ifndef __NT__ +  return string_to_utf8(this_function(utf8_to_string(path), no_warn, "utf8")); + #endif    break;    case "utf8":    case "utf-8":    encode = string_to_utf8;    decode = utf8_to_string;    cache_name += ":utf8";    break;    default: -  Locale.Charset.Encoder enc = Locale.Charset.encoder (charset); -  Locale.Charset.Decoder dec = Locale.Charset.decoder (charset); +  Charset.Encoder enc = Charset.encoder (charset); +  Charset.Decoder dec = Charset.decoder (charset);    encode = lambda (string in) {return enc->feed (in)->drain();};    decode = lambda (string in) {return dec->feed (in)->drain();};    cache_name += ":" + enc->charset;    break;    }       string dec_path, enc_path;    int nonexist;       void recur (string path)
Roxen.git/server/etc/modules/Roxen.pmod:6014:    }    recur (dec_path);       if (!nonexist) {    // FIXME: Note that get_dir on windows accepts and returns    // unicode paths, so the following isn't correct there. The    // charset handling in the file system interface on windows is    // inconsistent however, since most other functions do not    // accept neither wide strings nor strings encoded with any    // charset. This applies at least up to pike 7.8.589. +  string name = basename(path);    search_dir:    if (array(string) dir_list = get_dir (enc_path)) {    string lc_name = basename (lc_path);    string dec_name, enc_name; -  +  int fail;       foreach (dir_list, string enc_ent) {    string dec_ent;    if (!decode)    dec_ent = enc_ent;    else if (mixed err = catch (dec_ent = decode (enc_ent))) { -  +  if (decode != utf8_to_string) +  // utf8_to_string doesn't throw Charset.DecodeErrors.    if (!objectp (err) || !err->is_charset_decode_error)    throw (err);    // Ignore file system paths that we cannot decode. -  +  //werror ("path ignore in %O: %O\n", enc_path, enc_ent);    continue;    } -  +  if (String.width(dec_ent) > 8) { +  dec_ent = Unicode.normalize(dec_ent, "NFC"); +  }       if (lower_case (dec_ent) == lc_name) {    if (dec_name) {    if (!no_warn)    report_warning ("Ambiguous path %q matches both %q and %q "    "in %q.\n", path, dec_name, dec_ent, dec_path); -  break search_dir; +  fail = 1;    }    dec_name = dec_ent;    enc_name = enc_ent; -  +  if (enc_ent == name) { +  fail = 0; +  break;    }    } -  +  }    -  if (dec_name) { +  if (dec_name && !fail) {    dec_path = combine_path_unix (dec_path, dec_name);    enc_path = combine_path (enc_path, enc_name);    //werror ("path %O -> %O/%O\n", path, dec_path, enc_path);    cache_set (cache_name, lc_path, dec_path);    return;    }    }       nonexist = 1;    }