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

version» Context lines:

Roxen.git/server/etc/modules/Roxen.pmod:1:   /* -  * $Id: Roxen.pmod,v 1.56 2000/12/11 03:07:22 per Exp $ +  * $Id: Roxen.pmod,v 1.57 2000/12/11 03:36:16 per Exp $    *    * Various helper functions.    *    * Henrik Grubbström 1999-05-03    */   #include <roxen.h>   #include <config.h>   #include <version.h>   #include <module.h>   #include <variables.h>   #include <stat.h> -  + #define roxen roxenp()      #ifdef HTTP_DEBUG   # define HTTP_WERR(X) werror("HTTP: "+X+"\n");   #else   # define HTTP_WERR(X)   #endif       - #define roxen roxenp() - // From the old 'http' file - string http_res_to_string( mapping file, RequestID id ) + string http_roxen_config_cookie(string from)   { -  mapping(string:string|array(string)) heads= -  ([ -  "Content-type":[string]file["type"], -  "Server":replace(roxen->version(), " ", "·"), -  "Date":http_date([int]id->time) -  ]); +  return "RoxenConfig="+http_encode_cookie(from) +  +"; expires=" + http_date (3600*24*365*2 + time (1)) + "; path=/"; + }    -  if(file->encoding) -  heads["Content-Encoding"] = [string]file->encoding; + string http_roxen_id_cookie() + { +  return sprintf("RoxenUserID=0x%x; expires=" + +  http_date (3600*24*365*2 + time (1)) + "; path=/", +  roxen->increase_id()); + }    -  if(!file->error) -  file->error=200; +     -  if(file->expires) -  heads->Expires = http_date([int]file->expires); -  -  if(!file->len) + // These two functions are questionable, but rather widely used. + string short_name(string long_name)   { -  if(objectp(file->file)) -  if(!file->stat && !(file->stat=([mapping(string:mixed)]id->misc)->stat)) -  file->stat = (array(int))file->file->stat(); -  array fstat; -  if(arrayp(fstat = file->stat)) -  { -  if(file->file && !file->len) -  file->len = fstat[1]; -  -  heads["Last-Modified"] = http_date([int]fstat[3]); +  long_name = replace(long_name, " ", "_"); +  return lower_case(long_name);   } -  if(stringp(file->data)) -  file->len += strlen([string]file->data); -  } +     -  if(mappingp(file->extra_heads)) -  heads |= file->extra_heads; -  -  if(mappingp(([mapping(string:mixed)]id->misc)->moreheads)) -  heads |= ([mapping(string:mixed)]id->misc)->moreheads; -  -  array myheads=({id->prot+" "+(file->rettext||errors[file->error])}); -  foreach(indices(heads), string h) -  if(arrayp(heads[h])) -  foreach([array(string)]heads[h], string tmp) -  myheads += ({ `+(h,": ", tmp)}); -  else -  myheads += ({ `+(h, ": ", heads[h])}); -  -  -  if(file->len > -1) -  myheads += ({"Content-length: " + file->len }); -  string head_string = (myheads+({"",""}))*"\r\n"; -  -  if(id->conf) { -  id->conf->hsent+=strlen(head_string||""); -  if(id->method != "HEAD") -  id->conf->sent+=(file->len>0 ? file->len : 1000); + int _match(string w, array (string) a) + { +  if(!stringp(w)) // Internal request.. +  return -1; +  foreach(a, string q) +  if(stringp(q) && strlen(q) && glob(q, w)) +  return 1;   } -  if(id->method != "HEAD") -  head_string+=(file->data||"")+(file->file?file->file->read():""); -  return head_string; - } +     -  +  + // From the old 'http' file   mapping http_low_answer( int errno, string data )   //! Return a result mapping with the error and data specified. The   //! error is infact the status response, so '200' is HTTP Document   //! follows, and 500 Internal Server error, etc.   {    if(!data) data="";    HTTP_WERR("Return code "+errno+" ("+data+")");    return    ([    "error" : errno,    "data" : data,    "len" : strlen( data ),    "type" : "text/html",    ]);   }      mapping http_pipe_in_progress() -  + //! Return a result mapping that indicates that the request file + //! should be kept open, but no result should be sent. Another result + //! mapping can be sent later on with the send_result member + //! function of the protocol object, if it is available.   {    HTTP_WERR("Pipe in progress");    return ([ "file":-1, "pipe":1, ]);   }      mapping 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
Roxen.git/server/etc/modules/Roxen.pmod:128:    ]);   }         mapping http_try_again( float delay )   //! Causes the request to be retried in delay seconds.   {    return ([ "try_again_later":delay ]);   }    - class Delayer + static class Delayer   {    RequestID id;    int resumed;       void resume( )    {    if( resumed )    return;    remove_call_out( resume );    resumed = 1;
Roxen.git/server/etc/modules/Roxen.pmod:182:   //! };   //! thread_create( do_the_work, key );   //! return result;   //! }   //! }   {    Delayer delay = Delayer( id, max_delay );    return ({delay, ([ "try_again":delay ]) });   }    +  + mapping add_http_header(mapping to, string name, string value) + { +  if(to[name]) { +  if(arrayp(to[name])) +  to[name] += ({ value }); +  else +  to[name] = ({ to[name], value }); +  } +  else +  to[name] = value; +  return to; + } +    mapping http_string_answer(string text, string|void type)   //! Generates a result mapping with the given text as the request body   //! with a content type of `type' (or "text/html" if none was given).   {    HTTP_WERR("String answer ("+(type||"text/html")+")");    return ([ "data":text, "type":(type||"text/html") ]);   }      mapping http_file_answer(Stdio.File text, string|void type, void|int len) -  + //! Generate a result mapping with the given (open) file object as the + //! request body, the content type defaults to text/html if none is + //! given, and the length to the length of the file object.   {    HTTP_WERR("file answer ("+(type||"text/html")+")");    return ([ "file":text, "type":(type||"text/html"), "len":len ]);   }    - constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + static constant months = ({ "Jan", "Feb", "Mar", "Apr", "May", "Jun",    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }); - constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }); + static constant days = ({ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" });            static int chd_lt;   static string chd_lf;   string cern_http_date(int t) - //! Return a date, used in the common log format + //! Return a date, formated to be used in the common log format   {    if( t == chd_lt ) return chd_lf;       string c;    mapping(string:int) lt = localtime(t);    int tzh = lt->timezone/3600 - lt->isdst;    if(tzh > 0)    c="-";    else {    tzh = -tzh;
Roxen.git/server/etc/modules/Roxen.pmod:227:    }    chd_lt = t;    return(chd_lf=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));   }      string http_date(int t)   //! Returns a http_date, as specified by the HTTP-protocol standard.   //! This is used for logging as well as the Last-Modified and Time - //! heads in the reply. + //! headers in the reply.   {    mapping(string:int) l = gmtime( 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 http_encode_string(string f) -  + //! Encode dangerous characters in a string so that it can be used as + //! a URL. Specifically, nul, space, tab, newline, linefeed, %, ' and + //! " are quoted.   {    return replace(f, ({ "\000", " ", "\t", "\n", "\r", "%", "'", "\"" }),    ({"%00", "%20", "%09", "%0a", "%0d", "%25", "%27", "%22"}));   }      string http_encode_cookie(string f)   {    return replace(f, ({ "=", ",", ";", "%" }), ({ "%3d", "%2c", "%3b", "%25"}));   }      string http_encode_url (string f) -  + //! Like http_encode_string, but also quotes #, &, ?, =, / and :.   {    return replace (f, ({"\000", " ", "\t", "\n", "\r", "%", "'", "\"", "#",    "&", "?", "=", "/", ":"}),    ({"%00", "%20", "%09", "%0a", "%0d", "%25", "%27", "%22", "%23",    "%26", "%3f", "%3d", "%2f", "%3a"}));   }    - 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 sprintf("RoxenUserID=0x%x; expires=" + -  http_date (3600*24*365*2 + time (1)) + "; path=/", -  roxen->increase_id()); - } -  +    string add_pre_state( string url, multiset state )   {    if(!url)    error("URL needed for add_pre_state()\n");    if(!state || !sizeof(state))    return url;    if(strlen(url)>5 && (url[1] == '(' || url[1] == '<'))    return url;    return "/(" + sort(indices(state)) * "," + ")" + url ;   }
Roxen.git/server/etc/modules/Roxen.pmod:362:   //! In HTTP terms, this sends a <tt>407 Proxy authentication   //! failed</tt> response with the header <tt>Proxy-Authenticate: basic   //! realm="`realm'"</tt>. For more info, see RFC 2617.   {    if(!message)    message = "<h1>Proxy authentication failed.\n</h1>";    return http_low_answer(407, message)    + ([ "extra_heads":([ "Proxy-Authenticate":"basic realm=\""+realm+"\"",]),]);   }    - mapping add_http_header(mapping to, string name, string value) - { -  if(to[name]) { -  if(arrayp(to[name])) -  to[name] += ({ value }); -  else -  to[name] = ({ to[name], value }); -  } -  else -  to[name] = value; -  return to; - } -  +    // From the old 'roxenlib' file   string extract_query(string from)   {    if(!from) return "";    if(sscanf(from, "%*s?%s%*[ \t\n]", from))    return (from/"\r")[0];    return "";   }      mapping build_env_vars(string f, RequestID id, string path_info) -  + //! Generate a mapping with environment variables suitable for use + //! with CGI-scripts or SSI scripts etc.   {    string addr=id->remoteaddr || "Internal";    mapping(string:string) new = ([]);    RequestID tmpid;       if(id->query && strlen(id->query))    new->INDEX=id->query;       if(path_info && strlen(path_info))    {
Roxen.git/server/etc/modules/Roxen.pmod:556:    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) + //! 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: + //! For each cookie: COOKIE_cookiename=cookievalue + //! For each variable: VAR_variablename=variablevalue + //! For each 'prestate': PRESTATE_x=true + //! For each 'config': CONFIG_x=true + //! For each 'supports' flag: SUPPORTS_x=true   {    mapping(string:string) new = ([]);    string tmp;       if(id->cookies->RoxenUserID)    new["ROXEN_USER_ID"]=id->cookies->RoxenUserID;       new["COOKIES"] = "";    foreach(indices(id->cookies), tmp)    {
Roxen.git/server/etc/modules/Roxen.pmod:613:    {    new["SUPPORTS_"+replace(tmp-",", " ", "_")]="true";    if (new["SUPPORTS"])    new["SUPPORTS"] += " " + replace(tmp, " ", "_");    else    new["SUPPORTS"] = replace(tmp, " ", "_");    }    return new;   }    -  -  - 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_ISCHR(m)) s += "Special"; -  else if(S_ISBLK(m)) s += "Device"; -  else if(S_ISFIFO(m)) s += "FIFO"; -  else if(S_ISSOCK(m)) s += "Socket"; -  else if((m&0xf000)==0xd000) s+="Door"; -  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; - } -  - int _match(string w, array (string) a) - { -  if(!stringp(w)) // Internal request.. -  return -1; -  foreach(a, string q) -  if(stringp(q) && strlen(q) && glob(q, w)) -  return 1; - } -  - string short_name(string long_name) - { -  long_name = replace(long_name, " ", "_"); -  return lower_case(long_name); - } -  +    string strip_config(string from) -  + //! Remove all 'config' data from the given (local) URL.   {    sscanf(from, "/<%*s>%s", from);    return from;   }      string strip_prestate(string from) -  + //! Remove all 'prestate' data from the given (local) URL.   {    sscanf(from, "/(%*s)%s", from);    return from;   }      #define _error defines[" _error"]   #define _extra_heads defines[" _extra_heads"]   #define _rettext defines[" _rettext"]      string parse_rxml(string what, RequestID id,
Roxen.git/server/etc/modules/Roxen.pmod:994:      string make_container(string name, mapping(string:string) args, string content)   //! Returns a container tag `name' encasing the string `content', with   //! the tag arguments dictated by the mapping `args'. Use   //! RXML.t_xml->format_tag(name, args, content) instead.   {    if(args["/"]=="/") m_delete(args, "/");    return make_tag(name,args)+content+"</"+name+">";   }    - string dirname( string file ) - { -  if(!file) -  return "/"; -  if(file[-1] == '/') -  if(strlen(file) > 1) -  return file[0..strlen(file)-2]; -  else -  return file; -  array tmp=file/"/"; -  if(sizeof(tmp)==2 && tmp[0]=="") -  return "/"; -  return tmp[0..sizeof(tmp)-2]*"/"; - } -  - string conv_hex( int color ) - { -  return sprintf("#%06X", color); - } -  +    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 msectos(int t) - { -  if(t<1000) /* One sec. */ -  { -  return sprintf("0.%02d sec", t/10); -  } else if(t<6000) { /* One minute */ -  return sprintf("%d.%02d sec", t/1000, (t%1000 + 5) / 10); -  } else if(t<3600000) { /* One hour */ -  return sprintf("%d:%02d m:s", t/60000, (t%60000)/1000); -  } -  return sprintf("%d:%02d h:m", t/3600000, (t%3600000)/60000); - } -  +    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] == '#'))
Roxen.git/server/etc/modules/Roxen.pmod:1219:       if(s<1024.0) return (int)s+" bytes";    while( s > 1024.0 )    {    s /= 1024.0;    size ++;    }    return sprintf("%.1f %s", s, PREFIX[ size ]);   }    - mapping proxy_auth_needed(RequestID id) - { -  int|mapping res = id->conf->check_security(proxy_auth_needed, id); -  if(res) -  { -  if(res==1) // Nope... -  return http_low_answer(403, "You are not allowed to access this proxy"); -  if(!mappingp(res)) -  return 0; // Error, really. -  res->error = 407; -  return [mapping]res; -  } -  return 0; - } -  - // Please use __FILE__ if possible. - string program_filename() - { -  return master()->program_name(this_object())||""; - } -  - string program_directory() - { -  array(string) p = program_filename()/"/"; -  return (sizeof(p)>1? p[..sizeof(p)-2]*"/" : getcwd()); - } -  +    string html_encode_string(LocaleString str)   //! Encodes `str' for use as a literal in html text.   {    return replace((string)str, ({"&", "<", ">", "\"", "\'", "\000" }),    ({"&amp;", "&lt;", "&gt;", "&#34;", "&#39;", "&#0;"}));   }      string html_decode_string(LocaleString str)   //! Decodes `str', opposite to <ref>html_encode_string()</ref>   {