|
|
|
|
#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"); |
#else |
# define HTTP_WERR(X) |
#endif |
|
|
constant pike_cycle_depth = 0; |
|
object|array(object) parse_xml_tmpl( string ttag, string itag, |
string xml_file, |
string|void ident ) |
{ |
string tmpl; |
array(mapping) data = ({}); |
|
Parser.HTML p = Parser.HTML(); |
|
object apply_template( mapping data ) |
{ |
Parser.HTML p = Parser.HTML(); |
p->ignore_tags( 1 ); |
p->_set_entity_callback( lambda( Parser.HTML p, string ent ) |
{ |
string enc = "none"; |
sscanf( ent, "&%s;", ent ); |
sscanf( ent, "%s:%s", ent, enc ); |
sscanf( ent, "_.%s", ent ); |
switch( enc ) |
{ |
case "none": |
return data[ ent ]; |
case "int": |
return (string)(int)data[ ent ]; |
case "float": |
return (string)(float)data[ ent ]; |
case "string": |
default: |
return sprintf("%O", data[ent] ); |
} |
} ); |
string code = p->feed( tmpl )->finish()->read(); |
return compile_string( code, xml_file )(); |
}; |
|
|
p->xml_tag_syntax( 2 ); |
p->add_quote_tag ("!--", "", "--"); |
p->add_container( ttag, |
lambda( Parser.HTML p, mapping m, string c ) |
{ |
tmpl = c; |
} ); |
p->add_container( itag, |
lambda( Parser.HTML p, mapping m, string c ) |
{ |
string current_tag; |
mapping row = m; |
void got_tag( Parser.HTML p, string c ) |
{ |
sscanf( c, "<%s>", c ); |
if( c[0] == '/' ) |
current_tag = 0; |
else |
current_tag = c; |
}; |
|
void got_data( Parser.HTML p, string c ) |
{ |
if( current_tag ) |
if( row[current_tag] ) |
row[current_tag] += html_decode_string(c); |
else |
row[current_tag] = html_decode_string(c); |
}; |
|
p = Parser.HTML( ); |
p->xml_tag_syntax( 2 ); |
p->add_quote_tag ("!--", "", "--") |
->_set_tag_callback( got_tag ) |
->_set_data_callback( got_data ) |
->feed( c ) |
->finish(); |
data += ({ row }); |
} ) |
->feed( Stdio.read_file( xml_file ) ) |
->finish(); |
|
if( ident ) |
{ |
foreach( data, mapping m ) |
if( m->ident == ident ) |
return apply_template( m ); |
return 0; |
} |
return map( data, apply_template ); |
} |
|
object|array(object) parse_box_xml( string xml_file, string|void ident ) |
{ |
return parse_xml_tmpl( "template", "box", xml_file, ident ); |
} |
|
int ip_to_int(string ip) |
{ |
int res; |
foreach(((ip/".") + ({ "0", "0", "0" }))[..3], string num) |
res = (res<<8) | (int)num; |
return res; |
} |
|
string http_roxen_config_cookie(string from) |
{ |
return "RoxenConfig="+http_encode_cookie(from) |
+"; expires=" + http_date (3600*24*365*2 + time (1)) + "; path=/"; |
} |
|
string http_roxen_id_cookie() |
{ |
return "RoxenUserID=" + 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(); |
} |
|
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; |
} |
|
|
|
|
string get_remoteaddr(string ignored, RequestID id) |
{ |
return id->remoteaddr; |
} |
|
|
string msectos(int t) |
{ |
if(t<1000) |
{ |
return sprintf("0.%02d sec", t/10); |
} else if(t<6000) { |
return sprintf("%d.%02d sec", t/1000, (t%1000 + 5) / 10); |
} else if(t<3600000) { |
return sprintf("%d:%02d m:s", t/60000, (t%60000)/1000); |
} |
return sprintf("%d:%02d h:m", t/3600000, (t%3600000)/60000); |
} |
|
string decode_mode(int m) |
{ |
string s; |
s=""; |
|
if(S_ISLNK(m)) s += "Symbolic link"; |
else if(S_ISREG(m)) s += "File"; |
else if(S_ISDIR(m)) s += "Dir"; |
else if(S_ISSOCK(m)) s += "Socket"; |
else if(S_ISFIFO(m)) s += "FIFO"; |
else if((m&0xf000)==0xd000) s+="Door"; |
else if(S_ISBLK(m)) s += "Device"; |
else if(S_ISCHR(m)) s += "Special"; |
else s+= "Unknown"; |
|
s+=", "; |
|
if(S_ISREG(m) || S_ISDIR(m)) |
{ |
s+="<tt>"; |
if(m&S_IRUSR) s+="r"; else s+="-"; |
if(m&S_IWUSR) s+="w"; else s+="-"; |
if(m&S_IXUSR) s+="x"; else s+="-"; |
|
if(m&S_IRGRP) s+="r"; else s+="-"; |
if(m&S_IWGRP) s+="w"; else s+="-"; |
if(m&S_IXGRP) s+="x"; else s+="-"; |
|
if(m&S_IROTH) s+="r"; else s+="-"; |
if(m&S_IWOTH) s+="w"; else s+="-"; |
if(m&S_IXOTH) s+="x"; else s+="-"; |
s+="</tt>"; |
} else { |
s+="--"; |
} |
return s; |
} |
|
mapping(string:mixed) add_http_header(mapping(string:mixed) to, |
string name, string value) |
|
|
|
|
|
|
|
|
|
{ |
if(to[name]) { |
if(arrayp(to[name])) { |
if (search(to[name], value) == -1) |
to[name] += ({ value }); |
} else { |
if (to[name] != value) |
to[name] = ({ to[name], value }); |
} |
} |
else |
to[name] = value; |
return to; |
} |
|
mapping(string:mixed) merge_http_headers (mapping(string:mixed) a, |
mapping(string:mixed) b) |
|
|
|
{ |
mapping(string:mixed) res = a ^ b; |
foreach (a & b; string name;) { |
string|array(string) a_val = a[name], b_val = b[name]; |
if (a_val == b_val) |
|
|
|
res[name] = a_val; |
else { |
if (!arrayp (a_val)) a_val = ({a_val}); |
if (!arrayp (b_val)) b_val = ({b_val}); |
res[name] = a_val | b_val; |
} |
} |
return res; |
} |
|
int is_mysql_keyword( string name ) |
|
|
{ |
return (< |
"action", "add", "aggregate", "all", "alter", "after", "and", "as", |
"asc", "avg", "avg_row_length", "auto_increment", "between", "bigint", |
"bit", "binary", "blob", "bool", "both", "by", "cascade", "case", |
"char", "character", "change", "check", "checksum", "column", |
"columns", "comment", "constraint", "create", "cross", "current_date", |
"current_time", "current_timestamp", "data", "database", "databases", |
"date", "datetime", "day", "day_hour", "day_minute", "day_second", |
"dayofmonth", "dayofweek", "dayofyear", "dec", "decimal", "default", |
"delayed", "delay_key_write", "delete", "desc", "describe", "distinct", |
"distinctrow", "double", "drop", "end", "else", "escape", "escaped", |
"enclosed", "enum", "explain", "exists", "fields", "file", "first", |
"float", "float4", "float8", "flush", "foreign", "from", "for", "full", |
"function", "global", "grant", "grants", "group", "having", "heap", |
"high_priority", "hour", "hour_minute", "hour_second", "hosts", |
"identified", "ignore", "in", "index", "infile", "inner", "insert", |
"insert_id", "int", "integer", "interval", "int1", "int2", "int3", |
"int4", "int8", "into", "if", "is", "isam", "join", "key", "keys", |
"kill", "last_insert_id", "leading", "left", "length", "like", |
"lines", "limit", "load", "local", "lock", "logs", "long", "longblob", |
"longtext", "low_priority", "max", "max_rows", "match", "mediumblob", |
"mediumtext", "mediumint", "middleint", "min_rows", "minute", |
"minute_second", "modify", "month", "monthname", "myisam", "natural", |
"numeric", "no", "not", "null", "on", "optimize", "option", |
"optionally", "or", "order", "outer", "outfile", "pack_keys", |
"partial", "password", "precision", "primary", "procedure", "process", |
"processlist", "privileges", "read", "real", "references", "reload", |
"regexp", "rename", "replace", "restrict", "returns", "revoke", |
"rlike", "row", "rows", "second", "select", "set", "show", "shutdown", |
"smallint", "soname", "sql_big_tables", "sql_big_selects", |
"sql_low_priority_updates", "sql_log_off", "sql_log_update", |
"sql_select_limit", "sql_small_result", "sql_big_result", |
"sql_warnings", "straight_join", "starting", "status", "string", |
"table", "tables", "temporary", "terminated", "text", "then", "time", |
"timestamp", "tinyblob", "tinytext", "tinyint", "trailing", "to", |
"type", "use", "using", "unique", "unlock", "unsigned", "update", |
"usage", "values", "varchar", "variables", "varying", "varbinary", |
"with", "write", "when", "where", "year", "year_month", "zerofill", |
>)[ name ]; |
} |
|
string short_name(string|Configuration long_name) |
|
|
|
|
|
|
{ |
string id; |
if( objectp( long_name ) ) |
{ |
if( !long_name->name ) |
error("Illegal first argument to short_name.\n" |
"Expected Configuration object or string\n"); |
long_name = long_name->name; |
} |
|
id = Unicode.split_words_and_normalize( lower_case(long_name) )*"_"; |
|
if( strlen( id ) > 20 ) |
id = (id[..16]+"_"+hash(id)->digits(36))[..19]; |
|
if( !strlen( id ) ) |
id = hash(long_name)->digits(36); |
|
if( is_mysql_keyword( id ) ) |
return "x"+id[..19]; |
|
while( strlen(string_to_utf8( id )) > 20 ) |
id = id[..strlen(id)-2]; |
|
return string_to_utf8( id ); |
} |
|
int _match(string w, array (string) a) |
{ |
if(!stringp(w)) |
return -1; |
foreach(a, string q) |
if(stringp(q) && strlen(q) && glob(q, w)) |
return 1; |
} |
|
|
mapping(string:mixed) http_low_answer( int status_code, string data ) |
|
|
|
|
|
|
{ |
if(!data) data=""; |
HTTP_WERR("Return code "+status_code+" ("+data+")"); |
return |
([ |
"error" : status_code, |
"data" : data, |
"len" : strlen( data ), |
"type" : "text/html", |
]); |
} |
|
mapping(string:mixed) http_status (int status_code, |
void|string message, mixed... args) |
|
|
|
|
|
|
|
|
|
|
{ |
if (message) { |
if (sizeof (args)) message = sprintf (message, @args); |
HTTP_WERR ("Return status " + status_code + " " + message); |
return (["error": status_code, "rettext": message]); |
} |
else { |
HTTP_WERR ("Return status " + status_code); |
return (["error": status_code]); |
} |
} |
|
mapping(string:mixed) http_method_not_allowed ( |
string allowed_methods, void|string message, mixed... args) |
|
|
|
{ |
mapping(string:mixed) response = |
http_status (Protocols.HTTP.HTTP_METHOD_INVALID, message, @args); |
response->extra_heads = (["allow": allowed_methods]); |
return response; |
} |
|
|
|
|
|
|
|
|
|
|
|
mapping(string:mixed) http_pipe_in_progress() |
{ |
HTTP_WERR("Pipe in progress"); |
return ([ "file":-1, "pipe":1, ]); |
} |
|
mapping(string:mixed) http_rxml_answer( string rxml, RequestID id, |
void|Stdio.File file, |
void|string type ) |
|
|
|
|
{ |
rxml = |
([function(string,RequestID,Stdio.File:string)]id->conf->parse_rxml) |
(rxml, id, file); |
HTTP_WERR("RXML answer ("+(type||"text/html")+")"); |
return (["data":rxml, |
"type":(type||"text/html"), |
"stat":id->misc->defines[" _stat"], |
"error":id->misc->defines[" _error"], |
"rettext":id->misc->defines[" _rettext"], |
"extra_heads":id->misc->defines[" _extra_heads"], |
]); |
} |
|
|
mapping(string:mixed) http_try_again( float delay ) |
|
{ |
return ([ "try_again_later":delay ]); |
} |
|
protected class Delayer |
{ |
RequestID id; |
int resumed; |
|
void resume( ) |
{ |
if( resumed ) |
return; |
remove_call_out( resume ); |
resumed = 1; |
if( !id ) |
error("Cannot resume request -- connection close\n"); |
roxenp()->handle( id->handle_request ); |
id = 0; |
} |
|
void create( RequestID _id, float max_delay ) |
{ |
id = _id; |
if( max_delay && max_delay > 0.0 ) |
call_out( resume, max_delay ); |
} |
} |
|
array(object|mapping) http_try_resume( RequestID id, float|void max_delay ) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
Delayer delay = Delayer( id, max_delay ); |
return ({delay, ([ "try_again_later":delay ]) }); |
} |
|
mapping(string:mixed) http_string_answer(string text, string|void type) |
|
|
{ |
HTTP_WERR("String answer ("+(type||"text/html")+")"); |
return ([ "data":text, "type":(type||"text/html") ]); |
} |
|
mapping(string:mixed) http_file_answer(Stdio.File text, |
string|void type, void|int len) |
|
|
|
{ |
HTTP_WERR("file answer ("+(type||"text/html")+")"); |
return ([ "file":text, "type":(type||"text/html"), "len":len ]); |
} |
|
protected constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun", |
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }); |
protected constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }); |
|
string log_date(int t) { |
mapping(string:int) lt = localtime(t); |
return(sprintf("%04d-%02d-%02d", |
1900+lt->year,lt->mon+1, lt->mday)); |
} |
|
string log_time(int t) { |
mapping(string:int) lt = localtime(t); |
return(sprintf("%02d:%02d:%02d", |
lt->hour, lt->min, lt->sec)); |
} |
|
|
|
protected int chd_lt; |
protected string chd_lf; |
|
string cern_http_date(int t) |
|
{ |
if( t == chd_lt ) |
|
return chd_lf; |
|
string c; |
mapping(string:int) lt = localtime(t); |
int tzh = lt->timezone/3600; |
if(tzh > 0) |
c="-"; |
else { |
tzh = -tzh; |
c="+"; |
} |
|
c = sprintf("%02d/%s/%04d:%02d:%02d:%02d %s%02d00", |
lt->mday, months[lt->mon], 1900+lt->year, |
lt->hour, lt->min, lt->sec, c, tzh); |
|
chd_lt = t; |
|
chd_lf = c; |
|
return c; |
} |
|
constant http_status_messages = ([ |
100:"Continue", |
101:"Switching Protocols", |
102:"Processing", |
|
200:"OK", |
201:"Created", |
202:"Accepted", |
203:"Non-Authoritative Information", |
204:"No Content", |
205:"Reset Content", |
206:"Partial Content", |
207:"Multi-Status", |
226:"IM Used", |
|
300:"Multiple Choices", |
301:"Moved Permanently", |
302:"Found", |
303:"See Other", |
304:"Not Modified", |
305:"Use Proxy", |
|
307:"Temporary Redirect", |
|
400:"Bad Request", |
401:"Unauthorized", |
402:"Payment Required", |
403:"Forbidden", |
404:"Not Found", |
405:"Method Not Allowed", |
406:"Not Acceptable", |
407:"Proxy Authentication Required", |
408:"Request Timeout", |
409:"Conflict", |
410:"Gone", |
411:"Length Required", |
412:"Precondition Failed", |
413:"Request Entity Too Large", |
414:"Request-URI Too Long", |
415:"Unsupported Media Type", |
416:"Requested Range Not Satisfiable", |
417:"Expectation Failed", |
418:"I'm a teapot", |
|
420:"Server temporarily unavailable", |
421:"Server shutting down at operator request", |
422:"Unprocessable Entity", |
423:"Locked", |
424:"Failed Dependency", |
|
500:"Internal Server Error.", |
501:"Not Implemented", |
502:"Bad Gateway", |
503:"Service Unavailable", |
504:"Gateway Timeout", |
505:"HTTP Version Not Supported", |
506:"Variant Also Negotiates", |
507:"Insufficient Storage", |
]); |
|
string http_status_message (int status_code) |
|
|
{ |
return http_status_messages[status_code]; |
} |
|
string http_date( mixed t ) |
|
|
|
{ |
mapping(string:int) l = gmtime( (int)t ); |
return(sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT", |
days[l->wday], l->mday, months[l->mon], 1900+l->year, |
l->hour, l->min, l->sec)); |
} |
|
|
|
string iso8601_date_time(int ts, int|void ns) |
{ |
mapping(string:int) gmt = gmtime(ts); |
if (zero_type(ns)) { |
return sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ", |
1900 + gmt->year, gmt->mon+1, gmt->mday, |
gmt->hour, gmt->min, gmt->sec); |
} |
return sprintf("%04d-%02d-%02dT%02d:%02d:%02d.%09dZ", |
1900 + gmt->year, gmt->mon+1, gmt->mday, |
gmt->hour, gmt->min, gmt->sec, ns); |
} |
|
string http_encode_string(string f) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
#ifdef MODULE_DEBUG |
|
|
|
error ("Switch to http_encode_url or http_encode_invalids!\n"); |
#endif |
return replace(f, ({ "\000", " ", "\t", "\n", "\r", "%", "'", "\"" }), |
({"%00", "%20", "%09", "%0A", "%0D", "%25", "%27", "%22"})); |
} |
|
string http_encode_invalids (string f) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
return replace ( |
string_to_utf8 (f), ({ |
|
|
|
|
"\000", "\001", "\002", "\003", "\004", "\005", "\006", "\007", |
"\010", "\011", "\012", "\013", "\014", "\015", "\016", "\017", |
"\020", "\021", "\022", "\023", "\024", "\025", "\026", "\027", |
"\030", "\031", "\032", "\033", "\034", "\035", "\036", "\037", |
"\177", |
|
" ", "\"", |
|
|
|
|
|
|
"'", |
|
|
|
|
|
"\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207", |
"\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217", |
"\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227", |
"\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237", |
"\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247", |
"\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257", |
"\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267", |
"\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277", |
"\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307", |
"\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317", |
"\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327", |
"\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337", |
"\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347", |
"\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357", |
"\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367", |
"\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377", |
}), |
({ |
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", |
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", |
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", |
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", |
"%7F", |
"%20", "%22", |
"%27", |
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", |
"%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", |
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", |
"%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", |
"%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", |
"%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", |
"%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", |
"%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", |
"%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", |
"%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", |
"%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", |
"%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", |
"%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", |
"%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", |
"%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", |
"%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF", |
})); |
} |
|
string http_encode_cookie(string f) |
|
|
|
|
|
|
|
|
{ |
|
|
return replace( |
f, ({ |
"\000", "\001", "\002", "\003", "\004", "\005", "\006", "\007", |
"\010", "\011", "\012", "\013", "\014", "\015", "\016", "\017", |
"\020", "\021", "\022", "\023", "\024", "\025", "\026", "\027", |
"\030", "\031", "\032", "\033", "\034", "\035", "\036", "\037", |
"\177", |
"=", ",", ";", "%", |
}), ({ |
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", |
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", |
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", |
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", |
"%7F", |
"%3D", "%2C", "%3B", "%25", |
})); |
} |
|
string http_encode_url (string f) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
return replace ( |
string_to_utf8 (f), ({ |
|
"\000", "\001", "\002", "\003", "\004", "\005", "\006", "\007", |
"\010", "\011", "\012", "\013", "\014", "\015", "\016", "\017", |
"\020", "\021", "\022", "\023", "\024", "\025", "\026", "\027", |
"\030", "\031", "\032", "\033", "\034", "\035", "\036", "\037", |
"\177", |
|
":", "/", "?", "#", "[", "]", "@", |
|
"!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=", |
|
" ", "\"", "%", "<", ">", "\\", "^", "`", "{", "|", "}", |
|
|
|
|
|
"\200", "\201", "\202", "\203", "\204", "\205", "\206", "\207", |
"\210", "\211", "\212", "\213", "\214", "\215", "\216", "\217", |
"\220", "\221", "\222", "\223", "\224", "\225", "\226", "\227", |
"\230", "\231", "\232", "\233", "\234", "\235", "\236", "\237", |
"\240", "\241", "\242", "\243", "\244", "\245", "\246", "\247", |
"\250", "\251", "\252", "\253", "\254", "\255", "\256", "\257", |
"\260", "\261", "\262", "\263", "\264", "\265", "\266", "\267", |
"\270", "\271", "\272", "\273", "\274", "\275", "\276", "\277", |
"\300", "\301", "\302", "\303", "\304", "\305", "\306", "\307", |
"\310", "\311", "\312", "\313", "\314", "\315", "\316", "\317", |
"\320", "\321", "\322", "\323", "\324", "\325", "\326", "\327", |
"\330", "\331", "\332", "\333", "\334", "\335", "\336", "\337", |
"\340", "\341", "\342", "\343", "\344", "\345", "\346", "\347", |
"\350", "\351", "\352", "\353", "\354", "\355", "\356", "\357", |
"\360", "\361", "\362", "\363", "\364", "\365", "\366", "\367", |
"\370", "\371", "\372", "\373", "\374", "\375", "\376", "\377", |
}), |
({ |
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", |
"%08", "%09", "%0A", "%0B", "%0C", "%0D", "%0E", "%0F", |
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17", |
"%18", "%19", "%1A", "%1B", "%1C", "%1D", "%1E", "%1F", |
"%7F", |
"%3A", "%2F", "%3F", "%23", "%5B", "%5D", "%40", |
"%21","%24","%26","%27","%28","%29","%2A","%2B","%2C","%3B","%3D", |
"%20","%22","%25","%3C","%3E","%5C","%5E","%60","%7B","%7C","%7D", |
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87", |
"%88", "%89", "%8A", "%8B", "%8C", "%8D", "%8E", "%8F", |
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", |
"%98", "%99", "%9A", "%9B", "%9C", "%9D", "%9E", "%9F", |
"%A0", "%A1", "%A2", "%A3", "%A4", "%A5", "%A6", "%A7", |
"%A8", "%A9", "%AA", "%AB", "%AC", "%AD", "%AE", "%AF", |
"%B0", "%B1", "%B2", "%B3", "%B4", "%B5", "%B6", "%B7", |
"%B8", "%B9", "%BA", "%BB", "%BC", "%BD", "%BE", "%BF", |
"%C0", "%C1", "%C2", "%C3", "%C4", "%C5", "%C6", "%C7", |
"%C8", "%C9", "%CA", "%CB", "%CC", "%CD", "%CE", "%CF", |
"%D0", "%D1", "%D2", "%D3", "%D4", "%D5", "%D6", "%D7", |
"%D8", "%D9", "%DA", "%DB", "%DC", "%DD", "%DE", "%DF", |
"%E0", "%E1", "%E2", "%E3", "%E4", "%E5", "%E6", "%E7", |
"%E8", "%E9", "%EA", "%EB", "%EC", "%ED", "%EE", "%EF", |
"%F0", "%F1", "%F2", "%F3", "%F4", "%F5", "%F6", "%F7", |
"%F8", "%F9", "%FA", "%FB", "%FC", "%FD", "%FE", "%FF", |
})); |
} |
|
|
string correctly_http_encode_url(string f) { |
return http_encode_url (f); |
} |
|
string add_pre_state( string url, multiset state ) |
|
{ |
#ifdef MODULE_DEBUG |
if(!url) |
error("URL needed for add_pre_state()\n"); |
#endif |
if(!state || !sizeof(state)) |
return url; |
string base; |
if (sscanf (url, "%s://%[^/]%s", base, string host, url) == 3) |
base += "://" + host; |
else |
base = ""; |
if(strlen(url)>5 && (url[1] == '(' || url[1] == '<')) |
return base + url; |
return base + "/(" + sort(indices(state)) * "," + ")" + url ; |
} |
|
string make_absolute_url (string url, RequestID|void id, |
multiset|void prestates, mapping|void variables) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
url = fix_relative(url, id); |
|
|
if (has_prefix (url, "/")) { |
if(id) { |
url = id->url_base() + url[1..]; |
if (!prestates) prestates = id->prestate; |
} |
else { |
|
|
|
} |
} |
|
if(prestates && sizeof(prestates)) |
url = add_pre_state (url, prestates); |
|
if( String.width( url )>8 && !has_value( url, "?" ) ) |
url += "?magic_roxen_automatic_charset_variable="+ |
magic_charset_variable_value; |
|
url = http_encode_invalids (url); |
if (variables) { |
string concat_char = has_value (url, "?") ? "&" : "?"; |
foreach (indices (variables), string var) { |
var = http_encode_url (var); |
mixed val = variables[var]; |
if (stringp (val)) { |
url += concat_char + var + "=" + http_encode_url (val); |
concat_char = "&"; |
} |
else if (arrayp (val)) |
foreach (val, mixed part) |
if (stringp (part)) { |
url += concat_char + var + "=" + http_encode_url (part); |
concat_char = "&"; |
} |
} |
} |
|
return url; |
} |
|
mapping http_redirect( string url, RequestID|void id, multiset|void prestates, |
mapping|void variables) |
|
|
|
|
{ |
|
|
if(!url) |
url = ""; |
|
url = make_absolute_url (url, id, prestates, variables); |
|
HTTP_WERR("Redirect -> "+url); |
|
return http_status( 302, "Redirect to " + url) |
+ ([ "extra_heads":([ "Location":url ]) ]); |
} |
|
mapping http_stream(Stdio.File from) |
|
|
|
|
{ |
return ([ "raw":1, "file":from, "len":-1, ]); |
} |
|
mapping(string:mixed) http_digest_required(mapping(string:string) challenge, |
string|void message) |
|
|
|
|
|
|
|
{ |
if(!message) |
message = "<h1>Authentication failed.\n</h1>"; |
HTTP_WERR(sprintf("Auth required (%O)", challenge)); |
string digest_challenge = ""; |
foreach(challenge; string key; string val) { |
|
|
digest_challenge += sprintf(" %s=%O", key, val); |
} |
return http_low_answer(401, message) |
+ ([ "extra_heads":([ "WWW-Authenticate":"Digest"+digest_challenge,]),]); |
} |
|
mapping(string:mixed) http_auth_required(string realm, string|void message, |
void|RequestID id) |
|
|
|
|
|
|
|
|
|
{ |
HTTP_WERR("Auth required ("+realm+")"); |
if (id) { |
return id->conf->auth_failed_file( id, message ) |
+ ([ "extra_heads":([ "WWW-Authenticate": |
sprintf ("Basic realm=%O", realm)])]); |
} |
if(!message) |
message = "<h1>Authentication failed.</h1>"; |
return http_low_answer(401, message) |
+ ([ "extra_heads":([ "WWW-Authenticate": |
sprintf ("Basic realm=%O", realm)])]); |
} |
|
mapping(string:mixed) http_proxy_auth_required(string realm, |
void|string message) |
|
|
{ |
if(!message) |
message = "<h1>Proxy authentication failed.</h1>"; |
return http_low_answer(407, message) |
+ ([ "extra_heads":([ "Proxy-Authenticate": |
sprintf ("Basic realm=%O", realm)])]); |
} |
|
|
|
|
string extract_query(string from) |
{ |
if(!from) return ""; |
if(sscanf(from, "%*s?%s%*[ \t\n]", from)) |
return (from/"\r")[0]; |
return ""; |
} |
|
protected string mk_env_var_name(string name) |
{ |
name = replace(name, " ", "_"); |
string res = ""; |
do { |
string ok_part=""; |
sscanf(name, "%[A-Za-z0-9_]%s", ok_part, name); |
res += ok_part; |
if (sizeof(name)) { |
res += "_"; |
name = name[1..]; |
} |
} while (sizeof(name)); |
return res; |
} |
|
mapping build_env_vars(string f, RequestID id, string path_info) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
string addr=id->remoteaddr || "Internal"; |
mapping(string:string) new = ([]); |
|
if(id->query && strlen(id->query)) |
new->INDEX=id->query; |
|
if(path_info && strlen(path_info)) |
{ |
if(path_info[0] != '/') |
path_info = "/" + path_info; |
|
|
if ( ([mapping(string:mixed)]id->misc)->path_info == path_info ) { |
|
new["SCRIPT_NAME"]=id->not_query; |
} else { |
new["SCRIPT_NAME"]= |
id->not_query[0..strlen([string]id->not_query)-strlen(path_info)-1]; |
} |
new["PATH_INFO"]=path_info; |
|
|
|
string trailer = ""; |
while(1) |
{ |
|
string translated_base = id->conf->real_file(path_info, id); |
if (translated_base) |
{ |
new["PATH_TRANSLATED"] = combine_path_unix(translated_base, trailer); |
break; |
} |
array(string) tmp = path_info/"/" - ({""}); |
if(!sizeof(tmp)) |
break; |
path_info = "/" + (tmp[..sizeof(tmp)-2]) * "/"; |
trailer = tmp[-1] + "/" + trailer; |
} |
} else |
new["SCRIPT_NAME"]=id->not_query; |
|
|
RequestID tmpid = id; |
RequestID previd; |
while(tmpid->misc->orig) { |
|
tmpid = (previd = tmpid)->misc->orig; |
} |
|
|
new["REQUEST_URI"] = |
tmpid->misc->redirected_raw_url || tmpid->raw_url; |
|
if(tmpid->misc->is_redirected || previd) { |
|
if (tmpid->misc->redirected_to) { |
new["REDIRECT_URL"] = |
Roxen.http_encode_invalids(tmpid->misc->redirected_to); |
} else if (previd) { |
new["REDIRECT_URL"] = previd->raw_url; |
} |
new["REDIRECT_STATUS"] = "200"; |
} |
|
|
array(string) tmps; |
if(sizeof(tmps = tmpid->not_query/"/" - ({""}))) |
new["DOCUMENT_NAME"]=tmps[-1]; |
|
new["DOCUMENT_URI"]= tmpid->not_query; |
|
Stat tmpi; |
string real_file=tmpid->conf->real_file(tmpid->not_query||"", tmpid); |
if (real_file) { |
if(stringp(real_file)) { |
if ((tmpi = file_stat(real_file)) && |
sizeof(tmpi)) { |
new["LAST_MODIFIED"]=http_date(tmpi[3]); |
} |
} else { |
|
report_error(sprintf("real_file(%O, %O) returned %O\n", |
tmpid->not_query||"", tmpid, real_file)); |
} |
} |
|
|
|
|
if(string tmp = id->conf->real_file(new["SCRIPT_NAME"], id)) |
new["SCRIPT_FILENAME"] = tmp; |
|
if(string tmp = id->conf->real_file("/", id)) |
new["DOCUMENT_ROOT"] = tmp; |
|
if(!new["PATH_TRANSLATED"]) |
m_delete(new, "PATH_TRANSLATED"); |
else if(new["PATH_INFO"][-1] != '/' && new["PATH_TRANSLATED"][-1] == '/') |
new["PATH_TRANSLATED"] = |
new["PATH_TRANSLATED"][0..strlen(new["PATH_TRANSLATED"])-2]; |
|
|
|
mapping hdrs; |
|
if ((hdrs = id->request_headers)) { |
foreach(indices(hdrs) - ({ "authorization", "proxy-authorization", |
"security-scheme", }), string h) { |
string hh = "HTTP_" + replace(upper_case(h), |
({ " ", "-", "\0", "=" }), |
({ "_", "_", "", "_" })); |
|
new[mk_env_var_name(hh)] = replace(hdrs[h], ({ "\0" }), ({ "" })); |
} |
if (!new["HTTP_HOST"]) { |
if(objectp(id->my_fd) && id->my_fd->query_address(1)) |
new["HTTP_HOST"] = replace(id->my_fd->query_address(1)," ",":"); |
} |
} else { |
if(id->misc->host) |
new["HTTP_HOST"]=id->misc->host; |
else if(objectp(id->my_fd) && id->my_fd->query_address(1)) |
new["HTTP_HOST"]=replace(id->my_fd->query_address(1)," ",":"); |
if(id->misc["proxy-connection"]) |
new["HTTP_PROXY_CONNECTION"]=id->misc["proxy-connection"]; |
if(id->misc->accept) { |
if (arrayp(id->misc->accept)) { |
new["HTTP_ACCEPT"]=id->misc->accept*", "; |
} else { |
new["HTTP_ACCEPT"]=(string)id->misc->accept; |
} |
} |
|
if(id->misc->cookies) |
new["HTTP_COOKIE"] = id->misc->cookies; |
|
if(sizeof(id->pragma)) |
new["HTTP_PRAGMA"]=indices(id->pragma)*", "; |
|
if(stringp(id->misc->connection)) |
new["HTTP_CONNECTION"]=id->misc->connection; |
|
new["HTTP_USER_AGENT"] = id->client*" "; |
|
if(id->referer && sizeof(id->referer)) |
new["HTTP_REFERER"] = id->referer*""; |
} |
|
new["REMOTE_ADDR"]=addr; |
|
if(roxen->quick_ip_to_host(addr) != addr) |
new["REMOTE_HOST"]=roxen->quick_ip_to_host(addr); |
|
catch { |
if(id->my_fd) |
new["REMOTE_PORT"] = (id->my_fd->query_address()/" ")[1]; |
}; |
|
if (id->query && sizeof(id->query)) { |
new["QUERY_STRING"] = id->query; |
} |
|
if(id->realauth) |
new["REMOTE_USER"] = (id->realauth / ":")[0]; |
if( User u = id->conf->authenticate( id ) ) |
new["ROXEN_AUTHENTICATED"] = u->name(); |
|
|
if(id->data && strlen(id->data)) |
{ |
if(id->misc["content-type"]) |
new["CONTENT_TYPE"]=id->misc["content-type"]; |
else |
new["CONTENT_TYPE"]="application/x-www-form-urlencoded"; |
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"; |
|
return new; |
} |
|
mapping build_roxen_env_vars(RequestID id) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
mapping(string:string) new = ([]); |
string tmp; |
|
if(id->cookies->RoxenUserID) |
new["ROXEN_USER_ID"]=id->cookies->RoxenUserID; |
|
new["COOKIES"] = ""; |
foreach(indices(id->cookies), tmp) |
{ |
new["COOKIE_"+mk_env_var_name(tmp)] = id->cookies[tmp]; |
new["COOKIES"]+= mk_env_var_name(tmp)+" "; |
} |
|
foreach(indices(id->config), tmp) |
{ |
tmp = mk_env_var_name(tmp); |
new["CONFIG_"+tmp]="true"; |
if(new["CONFIGS"]) |
new["CONFIGS"] += " " + tmp; |
else |
new["CONFIGS"] = tmp; |
} |
|
foreach(indices(id->variables), tmp) |
{ |
string name = mk_env_var_name(tmp); |
if (mixed value = id->variables[tmp]) |
if (!catch (value = (string) value) && (sizeof(value) < 8192)) { |
|
new["QUERY_"+name] = replace(value,"\000"," "); |
new["VAR_"+name] = replace(value,"\000","#"); |
} |
|
if(new["VARIABLES"]) |
new["VARIABLES"]+= " " + name; |
else |
new["VARIABLES"]= name; |
} |
|
foreach(indices(id->prestate), tmp) |
{ |
tmp = mk_env_var_name(tmp); |
new["PRESTATE_"+tmp]="true"; |
if(new["PRESTATES"]) |
new["PRESTATES"] += " " + tmp; |
else |
new["PRESTATES"] = tmp; |
} |
|
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; |
} |
return new; |
} |
|
string strip_config(string from) |
|
{ |
sscanf(from, "/<%*s>%s", from); |
return from; |
} |
|
string strip_prestate(string from) |
|
{ |
sscanf(from, "/(%*s)%s", from); |
return from; |
} |
|
string parse_rxml(string what, RequestID id ) |
|
|
|
|
|
|
|
|
|
|
{ |
if(!objectp(id)) error("No id passed to parse_rxml\n"); |
return id->conf->parse_rxml( what, id ); |
} |
|
array(string|RXML.PCode) compile_rxml (string what, RequestID id) |
|
|
|
|
|
|
{ |
RXML.Parser parser = get_rxml_parser (id, 0, 1); |
parser->write_end (what); |
array(string|RXML.PCode) res = ({parser->eval(), parser->p_code}); |
res[1]->finish(); |
|
return res; |
} |
|
mixed eval_p_code (RXML.PCode p_code, RequestID id) |
|
|
|
|
|
|
|
|
|
|
|
|
{ |
return p_code->eval (p_code->new_context (id)); |
} |
|
RXML.Parser get_rxml_parser (RequestID id, void|RXML.Type type, void|int make_p_code) |
|
|
|
|
|
|
|
|
|
|
|
{ |
RXML.Parser parser = id->conf->rxml_tag_set->get_parser ( |
type || id->conf->default_content_type, id, make_p_code); |
parser->recover_errors = 1; |
if (make_p_code) parser->p_code->recover_errors = 1; |
return parser; |
} |
|
protected int(0..0) return_zero() {return 0;} |
|
protected Parser.HTML xml_parser = |
lambda() { |
Parser.HTML p = Parser.HTML(); |
p->lazy_entity_end (1); |
p->match_tag (0); |
p->xml_tag_syntax (3); |
p->add_quote_tag ("!--", return_zero, "--"); |
p->add_quote_tag ("![CDATA[", return_zero, "]]"); |
p->add_quote_tag ("?", return_zero, "?"); |
return p; |
}(); |
|
Parser.HTML get_xml_parser() |
|
|
|
|
{ |
return xml_parser->clone(); |
} |
|
constant iso88591 |
=([ " ": " ", |
"¡": "¡", |
"¢": "¢", |
"£": "£", |
"¤": "¤", |
"¥": "¥", |
"¦": "¦", |
"§": "§", |
"¨": "¨", |
"©": "©", |
"ª": "ª", |
"«": "«", |
"¬": "¬", |
"­": "", |
"®": "®", |
"¯": "¯", |
"°": "°", |
"±": "±", |
"²": "²", |
"³": "³", |
"´": "´", |
"µ": "µ", |
"¶": "¶", |
"·": "·", |
"¸": "¸", |
"¹": "¹", |
"º": "º", |
"»": "»", |
"¼": "¼", |
"½": "½", |
"¾": "¾", |
"¿": "¿", |
"À": "À", |
"Á": "Á", |
"Â": "Â", |
"Ã": "Ã", |
"Ä": "Ä", |
"Å": "Å", |
"Æ": "Æ", |
"Ç": "Ç", |
"È": "È", |
"É": "É", |
"Ê": "Ê", |
"Ë": "Ë", |
"Ì": "Ì", |
"Í": "Í", |
"Î": "Î", |
"Ï": "Ï", |
"Ð": "Ð", |
"Ñ": "Ñ", |
"Ò": "Ò", |
"Ó": "Ó", |
"Ô": "Ô", |
"Õ": "Õ", |
"Ö": "Ö", |
"×": "×", |
"Ø": "Ø", |
"Ù": "Ù", |
"Ú": "Ú", |
"Û": "Û", |
"Ü": "Ü", |
"Ý": "Ý", |
"Þ": "Þ", |
"ß": "ß", |
"à": "à", |
"á": "á", |
"â": "â", |
"ã": "ã", |
"ä": "ä", |
"å": "å", |
"æ": "æ", |
"ç": "ç", |
"è": "è", |
"é": "é", |
"ê": "ê", |
"ë": "ë", |
"ì": "ì", |
"í": "í", |
"î": "î", |
"ï": "ï", |
"ð": "ð", |
"ñ": "ñ", |
"ò": "ò", |
"ó": "ó", |
"ô": "ô", |
"õ": "õ", |
"ö": "ö", |
"÷": "÷", |
"ø": "ø", |
"ù": "ù", |
"ú": "ú", |
"û": "û", |
"ü": "ü", |
"ý": "ý", |
"þ": "þ", |
"ÿ": "ÿ", |
]); |
|
constant international |
=([ "Œ": "\x0152", |
"œ": "\x0153", |
"Š": "\x0160", |
"š": "\x0161", |
"Ÿ": "\x0178", |
"ˆ": "\x02C6", |
"˜": "\x02DC", |
" ": "\x2002", |
" ": "\x2003", |
" ": "\x2009", |
"‌": "\x200C", |
"‍": "\x200D", |
"‎": "\x200E", |
"‏": "\x200F", |
"–": "\x2013", |
"—": "\x2014", |
"‘": "\x2018", |
"’": "\x2019", |
"‚": "\x201A", |
"“": "\x201C", |
"”": "\x201D", |
"„": "\x201E", |
"†": "\x2020", |
"‡": "\x2021", |
"‰": "\x2030", |
"‹": "\x2039", |
"›": "\x203A", |
"€": "\x20AC", |
]); |
|
constant symbols |
=([ "ƒ": "\x0192", |
"ϑ": "\x03D1", |
"ϒ": "\x03D2", |
"ϖ": "\x03D6", |
"•": "\x2022", |
"…": "\x2026", |
"′": "\x2032", |
"″": "\x2033", |
"‾": "\x203E", |
"⁄": "\x2044", |
"℘": "\x2118", |
"ℑ": "\x2111", |
"ℜ": "\x211C", |
"™": "\x2122", |
"ℵ": "\x2135", |
"←": "\x2190", |
"↑": "\x2191", |
"→": "\x2192", |
"↓": "\x2193", |
"↔": "\x2194", |
"↵": "\x21B5", |
"⇐": "\x21D0", |
"⇑": "\x21D1", |
"⇒": "\x21D2", |
"⇓": "\x21D3", |
"⇔": "\x21D4", |
"∀": "\x2200", |
"∂": "\x2202", |
"∃": "\x2203", |
"∅": "\x2205", |
"∇": "\x2207", |
"∈": "\x2208", |
"∉": "\x2209", |
"∋": "\x220B", |
"∏": "\x220F", |
"∑": "\x2211", |
"−": "\x2212", |
"∗": "\x2217", |
"√": "\x221A", |
"∝": "\x221D", |
"∞": "\x221E", |
"∠": "\x2220", |
"∧": "\x2227", |
"∨": "\x2228", |
"∩": "\x2229", |
"∪": "\x222A", |
"∫": "\x222B", |
"∴": "\x2234", |
"∼": "\x223C", |
"≅": "\x2245", |
"≈": "\x2248", |
"≠": "\x2260", |
"≡": "\x2261", |
"≤": "\x2264", |
"≥": "\x2265", |
"⊂": "\x2282", |
"⊃": "\x2283", |
"⊄": "\x2284", |
"⊆": "\x2286", |
"⊇": "\x2287", |
"⊕": "\x2295", |
"⊗": "\x2297", |
"⊥": "\x22A5", |
"⋅": "\x22C5", |
"⌈": "\x2308", |
"⌉": "\x2309", |
"⌊": "\x230A", |
"⌋": "\x230B", |
"⟨": "\x2329", |
"⟩": "\x232A", |
"◊": "\x25CA", |
"♠": "\x2660", |
"♣": "\x2663", |
"♥": "\x2665", |
"♦": "\x2666", |
]); |
|
constant greek |
= ([ "Α": "\x391", |
"Β": "\x392", |
"Γ": "\x393", |
"Δ": "\x394", |
"Ε": "\x395", |
"Ζ": "\x396", |
"Η": "\x397", |
"Θ": "\x398", |
"Ι": "\x399", |
"Κ": "\x39A", |
"Λ": "\x39B", |
"Μ": "\x39C", |
"Ν": "\x39D", |
"Ξ": "\x39E", |
"Ο": "\x39F", |
"Π": "\x3A0", |
"Ρ": "\x3A1", |
"Σ": "\x3A3", |
"Τ": "\x3A4", |
"Υ": "\x3A5", |
"Φ": "\x3A6", |
"Χ": "\x3A7", |
"Ψ": "\x3A8", |
"Ω": "\x3A9", |
"α": "\x3B1", |
"β": "\x3B2", |
"γ": "\x3B3", |
"δ": "\x3B4", |
"ε": "\x3B5", |
"ζ": "\x3B6", |
"η": "\x3B7", |
"θ": "\x3B8", |
"ι": "\x3B9", |
"κ": "\x3BA", |
"λ": "\x3BB", |
"μ": "\x3BC", |
"ν": "\x3BD", |
"ξ": "\x3BE", |
"ο": "\x3BF", |
"π": "\x3C0", |
"ρ": "\x3C1", |
"ς": "\x3C2", |
"σ": "\x3C3", |
"τ": "\x3C4", |
"υ": "\x3C5", |
"φ": "\x3C6", |
"χ": "\x3C7", |
"ψ": "\x3C8", |
"ω": "\x3C9", |
]); |
|
constant replace_entities = indices( iso88591 ) + |
indices( international ) + |
indices( symbols ) + |
indices( greek ) + |
({"<",">","&",""","'",""",""","'","�"}); |
|
constant replace_values = values( iso88591 ) + |
values( international ) + |
values( symbols ) + |
values( greek ) + |
({"<",">","&","\"","\'","\"","\"","\'","\000"}); |
|
constant safe_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"/""; |
constant empty_strings = ({""})*sizeof(safe_characters); |
|
|
|
int(0..1) is_safe_string(string in) |
{ |
return strlen(in) && !strlen(replace(in, safe_characters, empty_strings)); |
} |
|
string make_entity( string q ) |
{ |
return "&"+q+";"; |
} |
|
string make_tag_attributes(mapping(string:string) in, |
void|int preserve_roxen_entities) |
{ |
if (!in || !sizeof(in)) |
return ""; |
|
|
|
string quote_fn(string text) |
{ |
string out = ""; |
int pos = 0; |
while ((pos = search(text, "&")) >= 0) { |
if ((sscanf(text[pos..], "&%[^ <>;&];", string entity) == 1) && |
search(entity, ".") >= 0) { |
out += html_encode_string(text[..pos - 1]) + "&" + entity + ";"; |
text = text[pos + strlen(entity) + 2..]; |
} else { |
out += html_encode_string(text[..pos]); |
text = text[pos + 1..]; |
} |
} |
return out + html_encode_string(text); |
}; |
|
string res = ""; |
if (preserve_roxen_entities) { |
foreach(indices(in), string a) |
res += " " + a + "=\"" + quote_fn((string) in[a]) + "\""; |
} else { |
foreach(indices(in), string a) |
res += " " + a + "=\"" + html_encode_string((string) in[a]) + "\""; |
} |
return res; |
} |
|
string make_tag(string name, mapping(string:string) args, void|int xml, |
void|int preserve_roxen_entities) |
|
|
|
|
{ |
string attrs = make_tag_attributes(args, preserve_roxen_entities); |
return "<" + name + attrs + (xml ? " /" : "" ) + ">"; |
} |
|
string make_container(string name, mapping(string:string) args, string content, |
void|int preserve_roxen_entities) |
|
|
|
{ |
if(args["/"]=="/") m_delete(args, "/"); |
return make_tag(name, args, 0, |
preserve_roxen_entities) + content + "</" + name + ">"; |
} |
|
string add_config( string url, array config, multiset prestate ) |
{ |
if(!sizeof(config)) |
return url; |
if(strlen(url)>5 && (url[1] == '(' || url[1] == '<')) |
return url; |
return "/<" + config * "," + ">" + add_pre_state(url, prestate); |
} |
|
string extension( string f, RequestID|void id) |
{ |
string ext, key; |
if(!f || !strlen(f)) return ""; |
if(!id || !(ext = [string]id->misc[key="_ext_"+f])) { |
sscanf(reverse(f), "%s.%*s", ext); |
if(!ext) ext = ""; |
else { |
ext = lower_case(reverse(ext)); |
if(sizeof (ext) && (ext[-1] == '~' || ext[-1] == '#')) |
ext = ext[..strlen(ext)-2]; |
} |
if(id) id->misc[key]=ext; |
} |
return ext; |
} |
|
int(0..1) backup_extension( string f ) |
|
|
{ |
if(!strlen(f)) |
return 1; |
return (f[-1] == '#' || f[-1] == '~' || f[0..1]==".#" |
|| (f[-1] == 'd' && sscanf(f, "%*s.old")) |
|| (f[-1] == 'k' && sscanf(f, "%*s.bak"))); |
} |
|
array(string) win_drive_prefix(string path) |
|
|
{ |
#ifdef __NT__ |
string prefix; |
if (sscanf(path, "\\\\%s%*[\\/]%s", prefix, string path_end) == 3) { |
return ({ "\\\\" + prefix, "/" + path_end }); |
} else if (sscanf(path, "%1s:%s", prefix, path) == 2) { |
return ({ prefix + ":", path }); |
} |
#endif |
return ({ "", path }); |
} |
|
string simplify_path(string file) |
|
|
{ |
|
if(!strlen(file) || (!has_value(file, "./") && (file[-1] != '.') && |
!has_value (file, "//"))) |
return file; |
|
int relative, got_slashdot_suffix; |
|
[string prefix, file] = win_drive_prefix(file); |
|
if (!has_prefix (file, "/")) |
relative = 1; |
|
|
|
|
|
if (has_suffix (file, "/.")) |
got_slashdot_suffix = 1; |
|
file=combine_path("/", file); |
|
if(got_slashdot_suffix) file += "/."; |
if(relative) return prefix + file[1..]; |
|
return prefix + file; |
} |
|
string short_date(int timestamp) |
|
{ |
int date = time(1); |
|
if(ctime(date)[20..23] != ctime(timestamp)[20..23]) |
return ctime(timestamp)[4..9] +" "+ ctime(timestamp)[20..23]; |
|
return ctime(timestamp)[4..9] +" "+ ctime(timestamp)[11..15]; |
} |
|
string int2roman(int m) |
|
{ |
string res=""; |
if (m>10000000||m<0) return "que"; |
while (m>999) { res+="M"; m-=1000; } |
if (m>899) { res+="CM"; m-=900; } |
else if (m>499) { res+="D"; m-=500; } |
else if (m>399) { res+="CD"; m-=400; } |
while (m>99) { res+="C"; m-=100; } |
if (m>89) { res+="XC"; m-=90; } |
else if (m>49) { res+="L"; m-=50; } |
else if (m>39) { res+="XL"; m-=40; } |
while (m>9) { res+="X"; m-=10; } |
if (m>8) return res+"IX"; |
else if (m>4) { res+="V"; m-=5; } |
else if (m>3) return res+"IV"; |
while (m) { res+="I"; m--; } |
return res; |
} |
|
string number2string(int n, mapping m, array|function names) |
{ |
string s; |
switch (m->type) |
{ |
case "string": |
if (functionp(names)) { |
s=([function(int:string)]names)(n); |
break; |
} |
if (n<0 || n>=sizeof(names)) |
s=""; |
else |
s=([array(string)]names)[n]; |
break; |
case "roman": |
s=int2roman(n); |
break; |
default: |
return (string)n; |
} |
|
switch(m["case"]) { |
case "lower": return lower_case(s); |
case "upper": return upper_case(s); |
case "capitalize": return capitalize(s); |
} |
|
#ifdef old_rxml_compat |
if (m->lower) return lower_case(s); |
if (m->upper) return upper_case(s); |
if (m->cap||m->capitalize) return capitalize(s); |
#endif |
|
return s; |
} |
|
string image_from_type( string t ) |
|
|
{ |
if(t) |
{ |
sscanf(t, "%s/", t); |
switch(t) |
{ |
case "audio": |
case "sound": |
return "internal-gopher-sound"; |
case "image": |
return "internal-gopher-image"; |
case "application": |
return "internal-gopher-binary"; |
case "text": |
return "internal-gopher-text"; |
} |
} |
return "internal-gopher-unknown"; |
} |
|
#define PREFIX ({ "bytes", "kb", "Mb", "Gb", "Tb", "Pb", "Eb", "Zb", "Yb" }) |
string sizetostring( int size ) |
|
|
|
|
|
|
{ |
if(size<0) return "--------"; |
float s = (float)size; |
size=0; |
|
if(s<1024.0) return (int)s+" bytes"; |
while( s > 1024.0 ) |
{ |
s /= 1024.0; |
size ++; |
} |
return sprintf("%.1f %s", s, PREFIX[ size ]); |
} |
|
string html_decode_string(LocaleString str) |
|
{ |
return replace((string)str, replace_entities, replace_values); |
} |
|
string html_encode_tag_value(LocaleString str) |
|
{ |
|
return "\"" + replace((string)str, ({"&", "\"", "<"}), ({"&", """, "<"})) + "\""; |
} |
|
protected string my_sprintf(int prefix, string f, int arg) |
|
{ |
if(!prefix && sscanf(f, "%%%*d%s", string format) == 2) |
f = "%" + format; |
return sprintf(f, arg); |
} |
|
string strftime(string fmt, int t, |
void|string lang, void|function language, void|RequestID id) |
|
{ |
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; |
int(0..1) prefix = 1; |
if(key[0] == '!' && sizeof(key) > 1) { |
prefix = 0; |
key = key[1..]; |
} |
switch(key[0]) { |
case 'a': |
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': |
if (language) |
res += number2string(lt->wday+1,m,language(lang,"day",id)); |
else |
res += ({ "Sunday", "Monday", "Tuesday", "Wednesday", |
"Thursday", "Friday", "Saturday" })[lt->wday]; |
break; |
case 'b': |
case 'h': |
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': |
if (language) |
res += number2string(lt->mon+1,m,language(lang,"month",id)); |
else |
res += ({ "January", "February", "March", "April", "May", "June", |
"July", "August", "September", "October", "November", "December" })[lt->mon]; |
break; |
case 'c': |
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': |
res += my_sprintf(prefix, "%02d", 19 + lt->year/100); |
break; |
case 'd': |
res += my_sprintf(prefix, "%02d", lt->mday); |
break; |
case 'D': |
res += strftime("%m/%d/%y", t); |
break; |
case 'e': |
res += my_sprintf(prefix, "%2d", lt->mday); |
break; |
case 'E': |
case 'O': |
key = key[1..]; |
break; |
case 'H': |
res += my_sprintf(prefix, "%02d", lt->hour); |
break; |
case 'I': |
res += my_sprintf(prefix, "%02d", 1 + (lt->hour + 11)%12); |
break; |
case 'j': |
res += my_sprintf(prefix, "%03d", lt->yday); |
break; |
case 'k': |
res += my_sprintf(prefix, "%2d", lt->hour); |
break; |
case 'l': |
res += my_sprintf(prefix, "%2d", 1 + (lt->hour + 11)%12); |
break; |
case 'm': |
res += my_sprintf(prefix, "%02d", lt->mon + 1); |
break; |
case 'M': |
res += my_sprintf(prefix, "%02d", lt->min); |
break; |
case 'n': |
res += "\n"; |
break; |
case 'p': |
res += lt->hour<12 ? "a.m." : "p.m."; |
break; |
case 'r': |
res += strftime("%l:%M %p", t); |
break; |
case 'R': |
res += sprintf("%02d:%02d", lt->hour, lt->min); |
break; |
case 'S': |
res += my_sprintf(prefix, "%02d", lt->sec); |
break; |
case 't': |
res += "\t"; |
break; |
case 'T': |
case 'X': |
res += sprintf("%02d:%02d:%02d", lt->hour, lt->min, lt->sec); |
break; |
case 'u': |
res += my_sprintf(prefix, "%d", lt->wday + 1); |
break; |
case 'w': |
res += my_sprintf(prefix, "%d", lt->wday); |
break; |
case 'x': |
res += strftime("%a %b %d %Y", t); |
break; |
case 'y': |
res += my_sprintf(prefix, "%02d", lt->year % 100); |
break; |
case 'Y': |
res += my_sprintf(prefix, "%04d", 1900 + lt->year); |
break; |
|
case 'U': |
|
res += my_sprintf(prefix, "%02d", ((lt->yday-1+lt->wday)/7)); |
break; |
case 'V': |
res += my_sprintf(prefix, "%02d", Calendar.ISO.Second(t)->week_no()); |
break; |
case 'W': |
|
res += my_sprintf(prefix, "%02d", ((lt->yday+(5+lt->wday)%7)/7)); |
break; |
case 'Z': |
|
} |
res+=key[1..]; |
} |
return replace(res, "\0", "%"); |
} |
|
RoxenModule get_module (string modname) |
|
|
{ |
string cname, mname; |
int mid = 0; |
|
if (sscanf (modname, "%s/%s", cname, mname) != 2 || |
!sizeof (cname) || !sizeof(mname)) return 0; |
sscanf (mname, "%s#%d", mname, mid); |
|
if (Configuration conf = roxen->get_configuration (cname)) |
if (mapping moddata = conf->modules[mname]) |
return moddata->copies[mid]; |
|
return 0; |
} |
|
string get_modname (RoxenModule module) |
|
|
{ |
return module && module->module_identifier(); |
} |
|
string get_modfullname (RoxenModule module) |
|
|
|
{ |
if (module) { |
string|mapping(string:string)|Locale.DeferredLocale name = 0; |
if (module->query) |
catch { |
mixed res = module->query ("_name"); |
if (res) name = (string) res; |
}; |
if (!(name && sizeof (name)) && module->query_name) |
name = module->query_name(); |
if (!(name && sizeof (name))) |
name = [string]module->register_module()[1]; |
if (mappingp (name)) |
name = name->standard; |
return (string) name; |
} |
else return 0; |
} |
|
protected constant xml_invalids = ((string)({ |
0, 1, 2, 3, 4, 5, 6, 7, |
8, 11, 12, 14, 15, |
16, 17, 18, 19, 20, 21, 22, 23, |
24, 25, 26, 27, 28, 29, 30, 31, |
127 |
}))/""; |
|
protected constant xml_printables = ((string)({ |
0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, |
0x2408, 0x240b, 0x240c, 0x240e, 0x240f, |
0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, |
0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, |
0x2421, |
}))/""; |
|
string encode_xml_invalids(string s) |
|
|
{ |
return replace(s, xml_invalids, xml_printables); |
} |
|
|
|
|
protected string low_roxen_encode(string val, string encoding) |
{ |
switch (encoding) { |
case "": |
case "none": |
return val; |
|
case "utf8": |
case "utf-8": |
return string_to_utf8(val); |
|
case "utf16": |
case "utf16be": |
return Locale.Charset.encoder("utf16be")->feed(val)->drain(); |
|
case "utf16le": |
return Locale.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 "base64": |
case "base-64": |
case "b64": |
return MIME.encode_base64(val); |
|
case "quotedprintable": |
case "quoted-printable": |
case "qp": |
return MIME.encode_qp(val); |
|
case "http": |
return http_encode_invalids (val); |
|
case "cookie": |
return http_encode_cookie (val); |
|
case "url": |
return http_encode_url (val); |
|
case "wml-url": |
|
|
|
|
|
|
return http_encode_url(val); |
|
case "html": |
return html_encode_string (val); |
|
case "invalids": |
case "xmlinvalids": |
case "xml-invalids": |
return encode_xml_invalids(val); |
|
case "wml": |
return replace(html_encode_string(val), "$", "$$"); |
|
case "dtag": |
|
return replace (val, "\"", "\"'\"'\""); |
|
case "stag": |
|
return replace(val, "'", "'\"'\"'"); |
|
case "pike": |
return replace (val, |
({ "\"", "\\", "\n" }), |
({ "\\\"", "\\\\", "\\n" })); |
|
case "js": |
case "javascript": |
return replace (val, |
({ "\b", "\014", "\n", "\r", "\t", "\\", "'", "\"" }), |
({ "\\b", "\\f", "\\n", "\\r", "\\t", "\\\\", |
"\\'", "\\\"" })); |
|
case "mysql": |
return replace (val, |
({ "\"", "'", "\\" }), |
({ "\\\"" , "\\'", "\\\\" }) ); |
|
case "sql": |
case "oracle": |
return replace (val, "'", "''"); |
|
case "mysql-dtag": |
|
return replace (val, |
({ "\"", "'", "\\" }), |
({ "\\\"'\"'\"", "\\'", "\\\\" })); |
|
case "mysql-pike": |
return replace (val, |
({ "\"", "'", "\\", "\n" }), |
({ "\\\\\\\"", "\\\\'", |
"\\\\\\\\", "\\n" }) ); |
|
case "sql-dtag": |
case "oracle-dtag": |
|
return replace (val, |
({ "'", "\"" }), |
({ "''", "\"'\"'\"" }) ); |
|
default: |
|
return 0; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
string roxen_encode(string val, string encoding) |
{ |
foreach(encoding/".", string enc) { |
if (!(val = low_roxen_encode(val, enc))) |
return 0; |
} |
return val; |
} |
|
string fix_relative( string file, RequestID id ) |
|
|
|
|
|
{ |
if (sscanf (file, "%[-+.a-zA-Z0-9]://%s", string prot, file) == 2) |
return prot + ":/" + combine_path ("/", file); |
|
#if 0 |
|
|
[string prefix, file] = win_drive_prefix(file); |
#endif |
|
|
if (has_prefix (file, "/")) |
return combine_path ("/", file); |
else if (has_prefix (file, "#")) |
return combine_path ("/", id->not_query + file); |
else |
return combine_path ("/", dirname (id->not_query), file); |
} |
|
Stdio.File open_log_file( string logfile ) |
|
|
|
{ |
mapping m = localtime(time(1)); |
m->year += 1900; |
m->mon++; |
if(m->mon < 10) m->mon = "0"+m->mon; |
if(m->mday < 10) m->mday = "0"+m->mday; |
if(m->hour < 10) m->hour = "0"+m->hour; |
logfile = replace(logfile,({"%d","%m","%y","%h" }), |
({ (string)m->mday, (string)(m->mon), |
(string)(m->year),(string)m->hour,})); |
if(strlen(logfile)) |
{ |
Stdio.File lf=Stdio.File( logfile, "wac"); |
if(!lf) |
{ |
mkdirhier(logfile); |
if(!(lf=Stdio.File( logfile, "wac"))) |
{ |
report_error("Failed to open logfile. ("+logfile+"): " |
+ strerror( errno() )+"\n"); |
return 0; |
} |
} |
return lf; |
} |
return Stdio.stderr; |
} |
|
string tagtime(int t, mapping(string:string) m, RequestID id, |
function(string, string, |
object:function(int, mapping(string:string):string)) language) |
|
|
|
|
{ |
string res; |
|
if (m->adjust) t+=(int)m->adjust; |
|
string lang; |
if(id && id->misc->defines && id->misc->defines->theme_language) |
lang=id->misc->defines->theme_language; |
if(m->lang) lang=m->lang; |
|
if(m->strftime) |
return strftime(m->strftime, t, lang, language, id); |
|
if (m->part) |
{ |
string sp; |
if(m->type == "ordered") |
{ |
m->type="string"; |
sp = "ordered"; |
} |
|
switch (m->part) |
{ |
case "year": |
return number2string(localtime(t)->year+1900,m, |
language(lang, sp||"number",id)); |
case "month": |
return number2string(localtime(t)->mon+1,m, |
language(lang, sp||"month",id)); |
case "week": |
return number2string(Calendar.ISO.Second(t)->week_no(), |
m, language(lang, sp||"number",id)); |
case "beat": |
|
mapping lt=gmtime(t); |
int secs=3600; |
secs+=lt->hour*3600; |
secs+=lt->min*60; |
secs+=lt->sec; |
secs%=24*3600; |
float beats=secs/86.4; |
if(!sp) return sprintf("@%03d",(int)beats); |
return number2string((int)beats,m, |
language(lang, sp||"number",id)); |
|
case "day": |
case "wday": |
return number2string(localtime(t)->wday+1,m, |
language(lang, sp||"day",id)); |
case "date": |
case "mday": |
return number2string(localtime(t)->mday,m, |
language(lang, sp||"number",id)); |
case "hour": |
return number2string(localtime(t)->hour,m, |
language(lang, sp||"number",id)); |
|
case "min": |
case "minute": |
return number2string(localtime(t)->min,m, |
language(lang, sp||"number",id)); |
case "sec": |
case "second": |
return number2string(localtime(t)->sec,m, |
language(lang, sp||"number",id)); |
case "seconds": |
return number2string(t,m, |
language(lang, sp||"number",id)); |
case "yday": |
return number2string(localtime(t)->yday,m, |
language(lang, sp||"number",id)); |
default: return ""; |
} |
} |
else if(m->type) { |
switch(m->type) |
{ |
case "unix": |
return (string)t; |
case "iso": |
mapping eris=localtime(t); |
if(m->date) |
return sprintf("%d-%02d-%02d", |
(eris->year+1900), eris->mon+1, eris->mday); |
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); |
|
case "discordian": |
#if efun(discdate) |
array(string) not=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 |
return "Discordian date support disabled"; |
#endif |
case "stardate": |
#if efun(stardate) |
return (string)stardate(t, (int)m->prec||1); |
#else |
return "Stardate support disabled"; |
#endif |
} |
} |
|
res=language(lang, "date", id)(t,m); |
|
if(m["case"]) |
switch(lower_case(m["case"])) |
{ |
case "upper": return upper_case(res); |
case "lower": return lower_case(res); |
case "capitalize": return capitalize(res); |
} |
|
#ifdef old_rxml_compat |
|
if (m->upper) { |
res=upper_case(res); |
report_warning("Old RXML in "+(id->query||id->not_query)+ |
", contains upper attribute in a tag. Use case=\"upper\" instead."); |
} |
if (m->lower) { |
res=lower_case(res); |
report_warning("Old RXML in "+(id->query||id->not_query)+ |
", 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; |
} |
|
int time_dequantifier(mapping m, void|int t ) |
|
|
|
|
|
{ |
int initial = t; |
if (m->seconds) t+=(int)(m->seconds); |
if (m->minutes) t+=(int)(m->minutes)*60; |
if (m->beats) t+=(int)((float)(m->beats)*86.4); |
if (m->hours) t+=(int)(m->hours)*3600; |
if (m->days) { |
int days = (int)m->days; |
if(initial) { |
if(days<0) |
t = (Calendar.ISO.Second("unix", t) - |
Calendar.ISO.Day()*abs(days))->unix_time(); |
else |
t = (Calendar.ISO.Second("unix", t) + |
Calendar.ISO.Day()*days)->unix_time(); |
} |
else |
t+=days*24*3600; |
} |
if (m->weeks) { |
int weeks = (int)m->weeks; |
if(initial) { |
if(weeks<0) |
t = (Calendar.ISO.Second("unix", t) - |
Calendar.ISO.Week()*abs(weeks))->unix_time(); |
else |
t = (Calendar.ISO.Second("unix", t) + |
Calendar.ISO.Week()*weeks)->unix_time(); |
} |
else |
t+=weeks*604800; |
} |
if (m->months) { |
int mon = (int)m->months; |
if(initial) { |
if(mon<0) |
t = (Calendar.ISO.Second("unix", t) - |
Calendar.ISO.Month()*abs(mon))->unix_time(); |
else |
t = (Calendar.ISO.Second("unix", t) + |
Calendar.ISO.Month()*mon)->unix_time(); |
} |
else |
t+=(int)(mon*24*3600*30.436849); |
} |
if (m->years) { |
int year = (int)m->years; |
if(initial) { |
if(year<0) |
t = (Calendar.ISO.Second("unix", t) - |
Calendar.ISO.Year()*abs(year))->unix_time(); |
else |
t = (Calendar.ISO.Second("unix", t) + |
Calendar.ISO.Year()*(int)m->years)->unix_time(); |
} |
else |
t+=(int)((float)(m->years)*3600*24*365.242190); |
} |
return (int)t; |
} |
|
class _charset_decoder(object cs) |
{ |
string decode(string what) |
{ |
return cs->clear()->feed(what)->drain(); |
} |
} |
|
protected class CharsetDecoderWrapper |
{ |
protected object decoder; |
string charset; |
|
protected void create (string cs) |
{ |
|
|
|
decoder = Locale.Charset.decoder (charset = cs); |
werror ("created %O from %O\n", decoder, cs); |
} |
|
string decode (string what) |
{ |
object d = decoder; |
|
decoder = 0; |
if (d) d->clear(); |
else d = Locale.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 = "åäö芟@" + 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, |
]); |
|
protected function(string:string) make_composite_decoder ( |
function(string:string) outer, function(string:string) inner) |
{ |
|
|
return lambda (string what) { |
return outer (inner (what)); |
}; |
} |
|
function(string:string) get_decoder_for_client_charset (string charset) |
|
|
{ |
if (function(string:string) dec = client_charset_decoders[charset]) |
|
return dec; |
|
if (sscanf (charset, "%s|%s", string outer_cs, string inner_cs)) { |
function(string:string) outer = client_charset_decoders[outer_cs]; |
if (!outer) |
outer = client_charset_decoders[outer_cs] = |
CharsetDecoderWrapper (outer_cs)->decode; |
return client_charset_decoders[charset] = |
make_composite_decoder (outer, get_decoder_for_client_charset (inner_cs)); |
} |
|
return client_charset_decoders[charset] = |
CharsetDecoderWrapper (charset)->decode; |
} |
|
string get_client_charset (string åäö) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
if (has_prefix(åäö, "%") && !has_prefix(åäö, "%%")) { |
report_notice("Warning: Double HTTP encoding detected: %s\n", åäö); |
string cs = get_client_charset (http_decode_string(åäö)); |
if (cs) { |
return cs + "|http"; |
} else { |
return "http"; |
} |
} |
|
|
|
|
string test = åäö; |
sscanf (test, "%s\0", test); |
string test2 = test; |
sscanf (test2, "%s@%s", test2, string charset); |
test2 = replace(test2, ({ "\201", "?x829f;", }), ({ "?", "?", })); |
|
test = replace(test2, |
({ "å", "å", "å", |
"ä", "ä", "ä", |
"ö", "ö", "ö", |
"芟","芟", }), |
({ "?", "?", "?", |
"?", "?", "?", |
"?", "?", "?", |
"?", "?", })); |
|
switch( test ) { |
case "edv": |
case "edv?": |
report_notice( "Warning: Non 8-bit safe client detected.\n"); |
return 0; |
|
case "åäö?": |
if (test2 != test) |
return "html"; |
|
case "åäö": |
return "iso-8859-1"; |
|
case "\33-Aåäö": |
case "\33-A\345\344\366\33$Bgl": |
return "iso-2022-jp"; |
|
case "+AOUA5AD2-": |
case "+AOUA5AD2gp8-": |
return "utf-7"; |
|
case "åäö?": |
if (test != test2) { |
return "html|utf-8"; |
} |
|
case "åäö": |
case "åä": |
case "åäö\350\212\237": |
case "\357\277\275\357\277\275\357\277\275\350\212\237": |
return "utf-8"; |
|
case "\214\212\232?": |
if (test != test2) { |
return "html|mac"; |
} |
|
case "\214\212\232": |
return "mac"; |
|
case "\0å\0ä\0ö": |
case "\0å\0ä\0ö\202\237": |
return "utf-16"; |
|
case "\344\214": |
case "???\344\214": |
case "\217\206H\217\206B\217\206r\344\214": |
return "shift_jis"; |
} |
|
|
if (charset) |
catch { |
get_decoder_for_client_charset (charset); |
return charset; |
}; |
|
if (!charset_warned_for[test] && (sizeof(charset_warned_for) < 256)) { |
charset_warned_for[test] = 1; |
report_warning( "Unable to find charset decoder for %O " |
"(vector %O, charset %O).\n", |
åäö, test, charset); |
} |
} |
|
function(string:string) get_client_charset_decoder( string åäö, |
RequestID|void id ) |
|
|
|
|
|
|
{ |
string charset = get_client_charset (åäö); |
|
if (function(string|function:void) f = id && id->set_output_charset) |
switch (charset) { |
case "iso-2022-jp": f ("iso-2022"); break; |
case "utf-7": f ("utf-7"); break; |
case "html|utf-8": case "utf-8": f ("utf-8"); break; |
case "html|mac": case "mac": f ("mac"); break; |
case "utf-16": f (string_to_unicode); break; |
case "shift_jis": f ("shift_jis"); break; |
} |
|
return get_decoder_for_client_charset (charset); |
} |
|
|
|
inherit _Roxen; |
|
|
|
#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) { |
|
return res[..sizeof(res)-3]; |
} |
return res; |
} |
#endif /* constant(HAVE_OLD__Roxen_make_http_headers) */ |
|
|
|
|
|
|
|
|
#ifdef QUOTA_DEBUG |
#define QD_WRITE(X) report_debug(X) |
#else /* !QUOTA_DEBUG */ |
#define QD_WRITE(X) |
#endif /* QUOTA_DEBUG */ |
|
#undef CACHE |
#undef NOCACHE |
#ifdef DEBUG_CACHEABLE |
#define CACHE(id,seconds) do { \ |
int old_cacheable = ([mapping(string:mixed)]id->misc)->cacheable; \ |
([mapping(string:mixed)]id->misc)->cacheable = \ |
min(([mapping(string:mixed)]id->misc)->cacheable,seconds); \ |
report_debug("%s:%d lowered cacheable to %d (was: %d, now: %d)\n", \ |
__FILE__, __LINE__, seconds, old_cacheable, \ |
([mapping(string:mixed)]id->misc)->cacheable); \ |
} while(0) |
#define NOCACHE(id) do { \ |
int old_cacheable = ([mapping(string:mixed)]id->misc)->cacheable; \ |
([mapping(string:mixed)]id->misc)->cacheable = 0; \ |
report_debug("%s:%d set cacheable to 0 (was: %d)\n", \ |
__FILE__, __LINE__, old_cacheable, \ |
([mapping(string:mixed)]id->misc)->cacheable); \ |
} while(0) |
#else /* !DEBUG_CACHEABLE */ |
#define CACHE(id,X) ([mapping(string:mixed)]id->misc)->cacheable=min(([mapping(string:mixed)]id->misc)->cacheable,X) |
#define NOCACHE(id) ([mapping(string:mixed)]id->misc)->cacheable=0 |
#endif /* DEBUG_CACHEABLE */ |
|
|
class QuotaDB |
{ |
#if constant(create_thread) |
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(create_thread) */ |
#define LOCK() |
#define UNLOCK() |
#endif /* constant(create_thread) */ |
|
constant READ_BUF_SIZE = 256; |
constant CACHE_SIZE_LIMIT = 512; |
|
string base; |
|
object catalog_file; |
object data_file; |
|
mapping(string:int) new_entries_cache = ([]); |
mapping(string:object) active_objects = ([]); |
|
array(int) index; |
array(string) index_acc; |
int acc_scale; |
|
int next_offset; |
|
protected class QuotaEntry |
{ |
string name; |
int data_offset; |
|
protected int usage; |
protected int quota; |
|
protected void store() |
{ |
LOCK(); |
|
QD_WRITE(sprintf("QuotaEntry::store(): Usage for %O is now %O(%O)\n", |
name, usage, quota)); |
|
data_file->seek(data_offset); |
data_file->write(sprintf("%4c", usage)); |
|
UNLOCK(); |
} |
|
protected void read() |
{ |
LOCK(); |
|
data_file->seek(data_offset); |
string s = data_file->read(4); |
|
usage = 0; |
sscanf(s, "%4c", usage); |
|
if (usage < 0) { |
|
usage = 0; |
} |
|
QD_WRITE(sprintf("QuotaEntry::read(): Usage for %O is %O(%O)\n", |
name, usage, quota)); |
|
UNLOCK(); |
} |
|
void create(string n, int d_o, int q) |
{ |
QD_WRITE(sprintf("QuotaEntry(%O, %O, %O)\n", n, d_o, q)); |
|
name = n; |
data_offset = d_o; |
quota = q; |
|
read(); |
} |
|
int check_quota(string uri, int amount) |
{ |
QD_WRITE(sprintf("QuotaEntry::check_quota(%O, %O): usage:%d(%d)\n", |
uri, amount, usage, quota)); |
|
if (!quota) { |
|
return 0; |
} |
|
if (amount == 0x7fffffff) { |
|
return 1; |
} |
|
return(usage + amount <= quota); |
} |
|
int allocate(string uri, int amount) |
{ |
QD_WRITE(sprintf("QuotaEntry::allocate(%O, %O): usage:%d => %d(%d)\n", |
uri, amount, usage, usage + amount, quota)); |
|
usage += amount; |
|
if (usage < 0) { |
|
usage = 0; |
} |
|
store(); |
|
return(usage <= quota); |
} |
|
int deallocate(string uri, int amount) |
{ |
return(allocate(uri, -amount)); |
} |
|
int get_usage(string uri) |
{ |
return usage; |
} |
|
void set_usage(string uri, int amount) |
{ |
usage = amount; |
|
store(); |
} |
} |
|
protected object read_entry(int offset, int|void quota) |
{ |
QD_WRITE(sprintf("QuotaDB::read_entry(%O, %O)\n", offset, quota)); |
|
catalog_file->seek(offset); |
|
string data = catalog_file->read(READ_BUF_SIZE); |
|
if (data == "") { |
QD_WRITE(sprintf("QuotaDB::read_entry(%O, %O): At EOF\n", |
offset, quota)); |
|
return 0; |
} |
|
int len; |
int data_offset; |
string key; |
|
sscanf(data[..7], "%4c%4c", len, data_offset); |
if (len > sizeof(data)) { |
key = data[8..] + catalog_file->read(len - sizeof(data)); |
|
len -= 8; |
|
if (sizeof(key) != len) { |
error(sprintf("Failed to read catalog entry at offset %d.\n" |
"len: %d, sizeof(key):%d\n", |
offset, len, sizeof(key))); |
} |
} else { |
key = data[8..len-1]; |
catalog_file->seek(offset + 8 + sizeof(key)); |
} |
|
return QuotaEntry(key, data_offset, quota); |
} |
|
protected Stdio.File open(string fname, int|void create_new) |
{ |
Stdio.File f = Stdio.File(); |
string mode = create_new?"rwc":"rw"; |
|
if (!f->open(fname, mode)) { |
error(sprintf("Failed to open quota file %O.\n", fname)); |
} |
if (f->try_lock && !f->try_lock()) { |
error(sprintf("Failed to lock quota file %O.\n", fname)); |
} |
return(f); |
} |
|
protected void init_index_acc() |
{ |
|
|
|
acc_scale = 1; |
if (sizeof(index)) { |
int i = sizeof(index)/2; |
|
while (i) { |
i /= 4; |
acc_scale *= 2; |
} |
} |
index_acc = allocate((sizeof(index) + acc_scale -1)/acc_scale); |
|
QD_WRITE(sprintf("QuotaDB()::init_index_acc(): " |
"sizeof(index):%d, sizeof(index_acc):%d acc_scale:%d\n", |
sizeof(index), sizeof(index_acc), acc_scale)); |
} |
|
void rebuild_index() |
{ |
array(string) new_keys = sort(indices(new_entries_cache)); |
|
int prev; |
array(int) new_index = ({}); |
|
foreach(new_keys, string key) { |
QD_WRITE(sprintf("QuotaDB::rebuild_index(): key:%O lo:0 hi:%d\n", |
key, sizeof(index_acc))); |
|
int lo; |
int hi = sizeof(index_acc); |
if (hi) { |
do { |
|
|
|
|
int probe = (lo + hi)/2; |
|
QD_WRITE(sprintf("QuotaDB::rebuild_index(): acc: " |
"key:%O lo:%d probe:%d hi:%d\n", |
key, lo, probe, hi)); |
|
if (!index_acc[probe]) { |
object e = read_entry(index[probe * acc_scale]); |
|
index_acc[probe] = e->name; |
} |
if (index_acc[probe] < key) { |
lo = probe + 1; |
} else if (index_acc[probe] > key) { |
hi = probe; |
} else { |
|
|
break; |
} |
} while(lo < hi); |
|
if (lo < hi) { |
|
|
|
continue; |
} |
if (hi) { |
hi *= acc_scale; |
lo = hi - acc_scale; |
|
if (hi > sizeof(index)) { |
hi = sizeof(index); |
} |
|
do { |
|
|
int probe = (lo + hi)/2; |
|
QD_WRITE(sprintf("QuotaDB::rebuild_index(): " |
"key:%O lo:%d probe:%d hi:%d\n", |
key, lo, probe, hi)); |
|
object e = read_entry(index[probe]); |
if (e->name < key) { |
lo = probe + 1; |
} else if (e->name > key) { |
hi = probe; |
} else { |
|
|
break; |
} |
} while (lo < hi); |
if (lo < hi) { |
|
|
|
continue; |
} |
} |
new_index += index[prev..hi-1] + ({ new_entries_cache[key] }); |
prev = hi; |
} else { |
new_index += ({ new_entries_cache[key] }); |
} |
} |
|
|
new_index += index[prev..]; |
|
QD_WRITE("Index rebuilt.\n"); |
|
LOCK(); |
|
object index_file = open(base + ".index.new", 1); |
string to_write = sprintf("%@4c", new_index); |
if (index_file->write(to_write) != sizeof(to_write)) { |
index_file->close(); |
rm(base + ".index.new"); |
} else { |
mv(base + ".index.new", base + ".index"); |
} |
|
index = new_index; |
init_index_acc(); |
|
UNLOCK(); |
|
foreach(new_keys, string key) { |
m_delete(new_entries_cache, key); |
} |
} |
|
protected object low_lookup(string key, int quota) |
{ |
QD_WRITE(sprintf("QuotaDB::low_lookup(%O, %O)\n", key, quota)); |
|
int cat_offset; |
|
if (!zero_type(cat_offset = new_entries_cache[key])) { |
QD_WRITE(sprintf("QuotaDB::low_lookup(%O, %O): " |
"Found in new entries cache.\n", key, quota)); |
return read_entry(cat_offset, quota); |
} |
|
|
|
|
int lo; |
int hi = sizeof(index_acc); |
if (hi) { |
do { |
|
|
|
int probe = (lo + hi)/2; |
|
QD_WRITE(sprintf("QuotaDB:low_lookup(%O): " |
"In acc: lo:%d, probe:%d, hi:%d\n", |
key, lo, probe, hi)); |
|
if (!index_acc[probe]) { |
object e = read_entry(index[probe * acc_scale], quota); |
|
index_acc[probe] = e->name; |
|
if (key == e->name) { |
|
QD_WRITE(sprintf("QuotaDB:low_lookup(%O): In acc: Found at %d\n", |
key, probe * acc_scale)); |
return e; |
} |
} |
if (index_acc[probe] < key) { |
lo = probe + 1; |
} else if (index_acc[probe] > key) { |
hi = probe; |
} else { |
|
QD_WRITE(sprintf("QuotaDB:low_lookup(%O): In acc: Found at %d\n", |
key, probe * acc_scale)); |
return read_entry(index[probe * acc_scale], quota); |
} |
} while(lo < hi); |
|
|
|
if (hi) { |
|
|
hi *= acc_scale; |
lo = hi - acc_scale; |
|
if (hi > sizeof(index)) { |
hi = sizeof(index); |
} |
|
do { |
|
|
int probe = (lo + hi)/2; |
|
QD_WRITE(sprintf("QuotaDB:low_lookup(%O): lo:%d, probe:%d, hi:%d\n", |
key, lo, probe, hi)); |
|
object e = read_entry(index[probe], quota); |
|
if (e->name < key) { |
lo = probe + 1; |
} else if (e->name > key) { |
hi = probe; |
} else { |
|
QD_WRITE(sprintf("QuotaDB:low_lookup(%O): Found at %d\n", |
key, probe)); |
return e; |
} |
} while (lo < hi); |
} |
} |
|
QD_WRITE(sprintf("QuotaDB::low_lookup(%O): Not found\n", key)); |
|
return 0; |
} |
|
object lookup(string key, int quota) |
{ |
QD_WRITE(sprintf("QuotaDB::lookup(%O, %O)\n", key, quota)); |
|
LOCK(); |
|
object res; |
|
if (res = active_objects[key]) { |
QD_WRITE(sprintf("QuotaDB::lookup(%O, %O): User in active objects.\n", |
key, quota)); |
|
return res; |
} |
if (res = low_lookup(key, quota)) { |
active_objects[key] = res; |
|
return res; |
} |
|
QD_WRITE(sprintf("QuotaDB::lookup(%O, %O): New user.\n", key, quota)); |
|
|
data_file->seek(-1); |
data_file->read(1); |
|
catalog_file->seek(next_offset); |
|
|
|
int data_offset = data_file->tell(); |
|
|
if (data_file->write(sprintf("%4c", 0)) != 4) { |
error(sprintf("write() failed for quota data file!\n")); |
} |
string entry = sprintf("%4c%4c%s", sizeof(key)+8, data_offset, key); |
|
if (catalog_file->write(entry) != sizeof(entry)) { |
error(sprintf("write() failed for quota catalog file!\n")); |
} |
|
new_entries_cache[key] = next_offset; |
next_offset = catalog_file->tell(); |
|
if (sizeof(new_entries_cache) > CACHE_SIZE_LIMIT) { |
rebuild_index(); |
} |
|
|
return low_lookup(key, quota); |
} |
|
void create(string base_name, int|void create_new) |
{ |
base = base_name; |
|
catalog_file = open(base_name + ".cat", create_new); |
data_file = open(base_name + ".data", create_new); |
object index_file = open(base_name + ".index", 1); |
|
set_weak_flag(active_objects, 1); |
|
|
array index_st = index_file->stat(); |
if (!index_st || !sizeof(index_st)) { |
error(sprintf("stat() failed for quota index file!\n")); |
} |
array data_st = data_file->stat(); |
if (!data_st || !sizeof(data_st)) { |
error(sprintf("stat() failed for quota data file!\n")); |
} |
if (index_st[1] < 0) { |
error("quota index file isn't a regular file!\n"); |
} |
if (data_st[1] < 0) { |
error("quota data file isn't a regular file!\n"); |
} |
if (data_st[1] < index_st[1]) { |
error("quota data file is shorter than the index file!\n"); |
} |
if (index_st[1] & 3) { |
error("quota index file has odd length!\n"); |
} |
if (data_st[1] & 3) { |
error("quota data file has odd length!\n"); |
} |
|
|
|
int i; |
array(string) index_str = index_file->read()/4; |
index = allocate(sizeof(index_str)); |
|
if (sizeof(index_str) && (sizeof(index_str[-1]) != 4)) { |
error("Truncated read of the index file!\n"); |
} |
|
foreach(index_str, string offset_str) { |
int offset; |
sscanf(offset_str, "%4c", offset); |
index[i++] = offset; |
if (offset > next_offset) { |
next_offset = offset; |
} |
} |
|
init_index_acc(); |
|
if (sizeof(index)) { |
|
mixed entry = read_entry(next_offset); |
next_offset = catalog_file->tell(); |
} |
|
if (index_st[1] < data_st[1]) { |
|
while (mixed entry = read_entry(next_offset)) { |
new_entries_cache[entry->name] = next_offset; |
next_offset = catalog_file->tell(); |
} |
|
|
rebuild_index(); |
} |
} |
} |
|
|
#define CTX() |
class EScope(string scope) |
{ |
void delete( string var ) |
{ |
RXML.Context ctx = RXML.get_context( ); |
ctx->delete_var( var, scope ); |
} |
|
string name() |
{ |
RXML.Context ctx = RXML.get_context( ); |
return scope == "_" ? ctx->current_scope() : scope; |
} |
|
protected mixed `[]( string what ) |
{ |
RXML.Context ctx = RXML.get_context( ); |
return ctx->get_var( what, scope ); |
} |
|
protected mixed `->( string what ) |
{ |
return `[]( what ); |
} |
|
protected mixed `[]=( string what, mixed nval ) |
{ |
RXML.Context ctx = RXML.get_context( ); |
ctx->set_var( what, nval, scope ); |
return nval; |
} |
|
protected mixed `->=( string what, mixed nval ) |
{ |
return `[]=( what, nval ); |
} |
|
protected array(string) _indices( ) |
{ |
RXML.Context ctx = RXML.get_context( ); |
return ctx->list_var( scope ); |
} |
|
protected array(string) _values( ) |
{ |
RXML.Context ctx = RXML.get_context( ); |
return map( ctx->list_var( scope ), `[] ); |
} |
} |
|
class SRestore |
{ |
mapping osc = ([]); |
void destroy() |
{ |
foreach( indices( osc ), string o ) |
add_constant( o, osc[o] ); |
add_constant( "roxen", roxenp() ); |
} |
} |
|
SRestore add_scope_constants( string|void name, function|void add_constant ) |
{ |
SRestore res = SRestore(); |
mapping ac = all_constants(); |
if(!add_constant) |
add_constant = predef::add_constant; |
if(!name) name = ""; |
if( RXML.get_context() ) |
{ |
foreach( RXML.get_context()->list_scopes()|({"_"}), string scope ) |
{ |
if( add_constant == predef::add_constant ) |
res->osc[ name+scope ] = ac[ name+scope ]; |
add_constant( name+scope, EScope( scope ) ); |
} |
} |
return res; |
} |
|
|
|
mapping(string:string) parser_charref_table = |
lambda () { |
mapping(string:string) table = ([]); |
for (int i = 0; i < sizeof (replace_entities); i++) { |
string chref = replace_entities[i]; |
table[chref[1..sizeof (chref) - 2]] = replace_values[i]; |
} |
return table; |
}(); |
|
|
mapping(string:string) inverse_charref_table = |
lambda () { |
mapping(string:string) table = ([]); |
for (int i = 0; i < sizeof (replace_entities); i++) { |
string chref = replace_entities[i]; |
table[replace_values[i]] = chref[1..sizeof (chref) - 2]; |
} |
return table; |
}(); |
|
string decode_charref (string chref) |
|
|
{ |
if (sizeof (chref) <= 2 || chref[0] != '&' || chref[-1] != ';') return 0; |
if (chref[1] != '#') return parser_charref_table[chref[1..sizeof (chref) - 2]]; |
if (sscanf (chref, |
(<'x', 'X'>)[chref[2]] ? "&%*2s%x;%*c" : "&%*c%d;%*c", |
int c) == 2) |
catch {return (string) ({c});}; |
return 0; |
} |
|
string|program safe_compile( string code ) |
{ |
program ret; |
roxenloader.LowErrorContainer ec = roxenloader.LowErrorContainer(); |
roxenloader.push_compile_error_handler( ec ); |
catch(ret = compile_string( code )); |
roxenloader.pop_compile_error_handler( ); |
if( !ret ) return ec->get(); |
return ret; |
} |
|
string encode_charref (string char) |
|
|
|
|
{ |
if (string chref = inverse_charref_table[char]) return "&" + chref + ";"; |
return sprintf ("&#%d;", char[0]); |
} |
|
|
|
|
class ScopeRequestHeader { |
inherit RXML.Scope; |
|
mixed `[] (string var, void|RXML.Context c, void|string scope, void|RXML.Type type) { |
string|array(string) val = (c || RXML_CONTEXT)->id->request_headers[var]; |
if(!val) |
return RXML.nil; |
if(type) |
{ |
if(arrayp(val) && type->subtype_of (RXML.t_any_text)) |
val *= "\0"; |
return type->encode(val); |
} |
return val; |
} |
|
array(string) _indices(void|RXML.Context c) { |
return indices((c || RXML_CONTEXT)->id->request_headers); |
} |
|
array(string) _values(void|RXML.Context c) { |
return values((c || RXML_CONTEXT)->id->request_headers); |
} |
|
string _sprintf (int flag) |
{ |
return flag == 'O' && "RXML.Scope(request-header)"; |
} |
} |
|
class ScopeRoxen { |
inherit RXML.Scope; |
|
string pike_version=predef::version(); |
int ssl_strength=0; |
|
#if constant(SSL) |
void create() { |
ssl_strength=40; |
#if constant(SSL.constants.CIPHER_des) |
if(SSL.constants.CIPHER_algorithms[SSL.constants.CIPHER_des]) |
ssl_strength=128; |
if(SSL.constants.CIPHER_algorithms[SSL.constants.CIPHER_3des]) |
ssl_strength=168; |
#endif /* !constant(SSL.constants.CIPHER_des) */ |
} |
#endif |
|
mixed `[] (string var, void|RXML.Context c, void|string scope, void|RXML.Type type) { |
if (!c) c = RXML_CONTEXT; |
|
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 "nodename": |
return uname()->nodename; |
case "uptime": |
CACHE(c->id,1); |
return ENCODE_RXML_INT(time(1)-roxenp()->start_time, type); |
case "uptime-days": |
CACHE(c->id,3600*2); |
return ENCODE_RXML_INT((time(1)-roxenp()->start_time)/3600/24, type); |
case "uptime-hours": |
CACHE(c->id,1800); |
return ENCODE_RXML_INT((time(1)-roxenp()->start_time)/3600, type); |
case "uptime-minutes": |
CACHE(c->id,60); |
return ENCODE_RXML_INT((time(1)-roxenp()->start_time)/60, type); |
case "hits-per-minute": |
CACHE(c->id,2); |
|
return ENCODE_RXML_INT(c->id->conf->requests / ((time(1)-roxenp()->start_time)/60 + 1), |
type); |
case "hits": |
NOCACHE(c->id); |
return ENCODE_RXML_INT(c->id->conf->requests, type); |
case "sent-mb": |
CACHE(c->id,10); |
|
return ENCODE_RXML_TEXT(sprintf("%1.2f",c->id->conf->sent / (1024.0*1024.0)), type); |
case "sent": |
NOCACHE(c->id); |
return ENCODE_RXML_INT(c->id->conf->sent, type); |
case "sent-per-minute": |
CACHE(c->id,2); |
return ENCODE_RXML_INT(c->id->conf->sent / ((time(1)-roxenp()->start_time)/60 || 1), |
type); |
case "sent-kbit-per-second": |
CACHE(c->id,2); |
|
return ENCODE_RXML_TEXT(sprintf("%1.2f",((c->id->conf->sent*8)/1024.0/ |
(time(1)-roxenp()->start_time || 1))), |
type); |
case "ssl-strength": |
return ENCODE_RXML_INT(ssl_strength, type); |
case "pike-version": |
return ENCODE_RXML_TEXT(pike_version, type); |
case "version": |
return ENCODE_RXML_TEXT(roxenp()->version(), type); |
case "base-version": |
return ENCODE_RXML_TEXT(roxen_ver, type); |
case "build": |
return ENCODE_RXML_TEXT(roxen_build, type); |
case "dist-version": |
return ENCODE_RXML_TEXT(roxen_dist_version, type); |
case "product-name": |
return ENCODE_RXML_TEXT(roxen_product_name, type); |
case "time": |
CACHE(c->id,1); |
return ENCODE_RXML_INT(time(), type); |
case "server": |
return ENCODE_RXML_TEXT (c->id->url_base(), type); |
case "domain": |
sscanf(c->id->url_base(), "%*s://%[^:/]", string tmp); |
return ENCODE_RXML_TEXT(tmp, type); |
case "locale": |
NOCACHE(c->id); |
return ENCODE_RXML_TEXT(roxenp()->locale->get(), type); |
case "path": |
return ENCODE_RXML_TEXT(c->id->misc->site_prefix_path, type); |
case "unique-id": |
return ENCODE_RXML_TEXT(roxenp()->create_unique_id(), type); |
|
case "license-type": { |
object key = c->id->conf->getvar("license")->get_key(); |
return ENCODE_RXML_TEXT(key?key->type():"none", type); |
} |
case "license-warnings": { |
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); |
} |
|
return RXML.nil; |
} |
|
mixed `[]= (string var, mixed val, void|RXML.Context c, |
void|string scope_name) { |
if (!c) c = RXML_CONTEXT; |
return c->misc->scope_roxen[var]=val; |
} |
|
array(string) _indices(void|RXML.Context c) { |
if (!c) c = RXML_CONTEXT; |
return |
Array.uniq(indices(c->misc->scope_roxen) + |
({ "uptime", "uptime-days", "uptime-hours", "uptime-minutes", |
"hits-per-minute", "hits", "sent-mb", "sent", "unique-id", |
"sent-per-minute", "sent-kbit-per-second", "ssl-strength", |
"pike-version", "version", "time", "server", "domain", |
"locale", "path", "auto-charset-variable", |
"auto-charset-value" }) ); |
} |
|
void _m_delete (string var, void|RXML.Context c, void|string scope_name) { |
if (!c) c = RXML_CONTEXT; |
predef::m_delete(c->misc->scope_roxen, var); |
} |
|
string _sprintf (int flag) { return flag == 'O' && "RXML.Scope(roxen)"; } |
} |
|
int get_ssl_strength(string ignored, RequestID id) |
{ |
if (!id->my_fd || !id->my_fd->SSLConnection || |
!id->my_fd->query_connection()) |
return 0; |
return id->my_fd->query_connection()->session->cipher_spec->key_bits; |
} |
|
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; |
if(converter[var]) |
val = c->misc[converter[var]]; |
else |
val = c->misc->scope_page[var]; |