Home | History | Annotate | Download | only in cgi
      1 /*
      2  * Copyright 2001-2004 Brandon Long
      3  * All Rights Reserved.
      4  *
      5  * ClearSilver Templating System
      6  *
      7  * This code is made available under the terms of the ClearSilver License.
      8  * http://www.clearsilver.net/license.hdf
      9  *
     10  */
     11 
     12 #include "cs_config.h"
     13 
     14 #include <unistd.h>
     15 #include <string.h>
     16 #include <stdlib.h>
     17 #include <ctype.h>
     18 #include <stdio.h>
     19 #include <stdarg.h>
     20 #include <time.h>
     21 #if defined(HTML_COMPRESSION)
     22 #include <zlib.h>
     23 #endif
     24 
     25 #include "util/neo_misc.h"
     26 #include "util/neo_err.h"
     27 #include "util/neo_hdf.h"
     28 #include "util/neo_str.h"
     29 #include "cgi.h"
     30 #include "cgiwrap.h"
     31 #include "html.h"
     32 #include "cs/cs.h"
     33 
     34 
     35 struct _cgi_vars
     36 {
     37   char *env_name;
     38   char *hdf_name;
     39 } CGIVars[] = {
     40   {"AUTH_TYPE", "AuthType"},
     41   {"CONTENT_TYPE", "ContentType"},
     42   {"CONTENT_LENGTH", "ContentLength"},
     43   {"DOCUMENT_ROOT", "DocumentRoot"},
     44   {"GATEWAY_INTERFACE", "GatewayInterface"},
     45   {"PATH_INFO", "PathInfo"},
     46   {"PATH_TRANSLATED", "PathTranslated"},
     47   {"QUERY_STRING", "QueryString"},
     48   {"REDIRECT_REQUEST", "RedirectRequest"},
     49   {"REDIRECT_QUERY_STRING", "RedirectQueryString"},
     50   {"REDIRECT_STATUS", "RedirectStatus"},
     51   {"REDIRECT_URL", "RedirectURL",},
     52   {"REMOTE_ADDR", "RemoteAddress"},
     53   {"REMOTE_HOST", "RemoteHost"},
     54   {"REMOTE_IDENT", "RemoteIdent"},
     55   {"REMOTE_PORT", "RemotePort"},
     56   {"REMOTE_USER", "RemoteUser"},
     57   {"REMOTE_GROUP", "RemoteGroup"},
     58   {"REQUEST_METHOD", "RequestMethod"},
     59   {"REQUEST_URI", "RequestURI"},
     60   {"SCRIPT_FILENAME", "ScriptFilename"},
     61   {"SCRIPT_NAME", "ScriptName"},
     62   {"SERVER_ADDR", "ServerAddress"},
     63   {"SERVER_ADMIN", "ServerAdmin"},
     64   {"SERVER_NAME", "ServerName"},
     65   {"SERVER_PORT", "ServerPort"},
     66   {"SERVER_ROOT", "ServerRoot"},
     67   {"SERVER_PROTOCOL", "ServerProtocol"},
     68   {"SERVER_SOFTWARE", "ServerSoftware"},
     69   /* SSL Vars from mod_ssl */
     70   {"HTTPS", "HTTPS"},
     71   {"SSL_PROTOCOL", "SSL.Protocol"},
     72   {"SSL_SESSION_ID", "SSL.SessionID"},
     73   {"SSL_CIPHER", "SSL.Cipher"},
     74   {"SSL_CIPHER_EXPORT", "SSL.Cipher.Export"},
     75   {"SSL_CIPHER_USEKEYSIZE", "SSL.Cipher.UseKeySize"},
     76   {"SSL_CIPHER_ALGKEYSIZE", "SSL.Cipher.AlgKeySize"},
     77   {"SSL_VERSION_INTERFACE", "SSL.Version.Interface"},
     78   {"SSL_VERSION_LIBRARY", "SSL.Version.Library"},
     79   {"SSL_CLIENT_M_VERSION", "SSL.Client.M.Version"},
     80   {"SSL_CLIENT_M_SERIAL", "SSL.Client.M.Serial"},
     81   {"SSL_CLIENT_S_DN", "SSL.Client.S.DN"},
     82   {"SSL_CLIENT_S_DN_x509", "SSL.Client.S.DN.x509"},
     83   {"SSL_CLIENT_I_DN", "SSL.Client.I.DN"},
     84   {"SSL_CLIENT_I_DN_x509", "SSL.Client.I.DN.x509"},
     85   {"SSL_CLIENT_V_START", "SSL.Client.V.Start"},
     86   {"SSL_CLIENT_V_END", "SSL.Client.V.End"},
     87   {"SSL_CLIENT_A_SIG", "SSL.Client.A.SIG"},
     88   {"SSL_CLIENT_A_KEY", "SSL.Client.A.KEY"},
     89   {"SSL_CLIENT_CERT", "SSL.Client.CERT"},
     90   {"SSL_CLIENT_CERT_CHAINn", "SSL.Client.CERT.CHAINn"},
     91   {"SSL_CLIENT_VERIFY", "SSL.Client.Verify"},
     92   {"SSL_SERVER_M_VERSION", "SSL.Server.M.Version"},
     93   {"SSL_SERVER_M_SERIAL", "SSL.Server.M.Serial"},
     94   {"SSL_SERVER_S_DN", "SSL.Server.S.DN"},
     95   {"SSL_SERVER_S_DN_x509", "SSL.Server.S.DN.x509"},
     96   {"SSL_SERVER_S_DN_CN", "SSL.Server.S.DN.CN"},
     97   {"SSL_SERVER_S_DN_EMAIL", "SSL.Server.S.DN.Email"},
     98   {"SSL_SERVER_S_DN_O", "SSL.Server.S.DN.O"},
     99   {"SSL_SERVER_S_DN_OU", "SSL.Server.S.DN.OU"},
    100   {"SSL_SERVER_S_DN_C", "SSL.Server.S.DN.C"},
    101   {"SSL_SERVER_S_DN_SP", "SSL.Server.S.DN.SP"},
    102   {"SSL_SERVER_S_DN_L", "SSL.Server.S.DN.L"},
    103   {"SSL_SERVER_I_DN", "SSL.Server.I.DN"},
    104   {"SSL_SERVER_I_DN_x509", "SSL.Server.I.DN.x509"},
    105   {"SSL_SERVER_I_DN_CN", "SSL.Server.I.DN.CN"},
    106   {"SSL_SERVER_I_DN_EMAIL", "SSL.Server.I.DN.Email"},
    107   {"SSL_SERVER_I_DN_O", "SSL.Server.I.DN.O"},
    108   {"SSL_SERVER_I_DN_OU", "SSL.Server.I.DN.OU"},
    109   {"SSL_SERVER_I_DN_C", "SSL.Server.I.DN.C"},
    110   {"SSL_SERVER_I_DN_SP", "SSL.Server.I.DN.SP"},
    111   {"SSL_SERVER_I_DN_L", "SSL.Server.I.DN.L"},
    112   {"SSL_SERVER_V_START", "SSL.Server.V.Start"},
    113   {"SSL_SERVER_V_END", "SSL.Server.V.End"},
    114   {"SSL_SERVER_A_SIG", "SSL.Server.A.SIG"},
    115   {"SSL_SERVER_A_KEY", "SSL.Server.A.KEY"},
    116   {"SSL_SERVER_CERT", "SSL.Server.CERT"},
    117   /* SSL Vars mapped from others */
    118   /* Hmm, if we're running under mod_ssl w/ +CompatEnvVars, we set these
    119    * twice... */
    120   {"SSL_PROTOCOL_VERSION", "SSL.Protocol"},
    121   {"SSLEAY_VERSION", "SSL.Version.Library"},
    122   {"HTTPS_CIPHER", "SSL.Cipher"},
    123   {"HTTPS_EXPORT", "SSL.Cipher.Export"},
    124   {"HTTPS_SECRETKEYSIZE", "SSL.Cipher.UseKeySize"},
    125   {"HTTPS_KEYSIZE", "SSL.Cipher.AlgKeySize"},
    126   {"SSL_SERVER_KEY_SIZE", "SSL.Cipher.AlgKeySize"},
    127   {"SSL_SERVER_CERTIFICATE", "SSL.Server.CERT"},
    128   {"SSL_SERVER_CERT_START", "SSL.Server.V.Start"},
    129   {"SSL_SERVER_CERT_END", "SSL.Server.V.End"},
    130   {"SSL_SERVER_CERT_SERIAL", "SSL.Server.M.Serial"},
    131   {"SSL_SERVER_SIGNATURE_ALGORITHM", "SSL.Server.A.SIG"},
    132   {"SSL_SERVER_DN", "SSL.Server.S.DN"},
    133   {"SSL_SERVER_CN", "SSL.Server.S.DN.CN"},
    134   {"SSL_SERVER_EMAIL", "SSL.Server.S.DN.Email"},
    135   {"SSL_SERVER_O", "SSL.Server.S.DN.O"},
    136   {"SSL_SERVER_OU", "SSL.Server.S.DN.OU"},
    137   {"SSL_SERVER_C", "SSL.Server.S.DN.C"},
    138   {"SSL_SERVER_SP", "SSL.Server.S.DN.SP"},
    139   {"SSL_SERVER_L", "SSL.Server.S.DN.L"},
    140   {"SSL_SERVER_IDN", "SSL.Server.I.DN"},
    141   {"SSL_SERVER_ICN", "SSL.Server.I.DN.CN"},
    142   {"SSL_SERVER_IEMAIL", "SSL.Server.I.DN.Email"},
    143   {"SSL_SERVER_IO", "SSL.Server.I.DN.O"},
    144   {"SSL_SERVER_IOU", "SSL.Server.I.DN.OU"},
    145   {"SSL_SERVER_IC", "SSL.Server.I.DN.C"},
    146   {"SSL_SERVER_ISP", "SSL.Server.I.DN.SP"},
    147   {"SSL_SERVER_IL", "SSL.Server.I.DN.L"},
    148   {"SSL_CLIENT_CERTIFICATE", "SSL.Client.CERT"},
    149   {"SSL_CLIENT_CERT_START", "SSL.Client.V.Start"},
    150   {"SSL_CLIENT_CERT_END", "SSL.Client.V.End"},
    151   {"SSL_CLIENT_CERT_SERIAL", "SSL.Client.M.Serial"},
    152   {"SSL_CLIENT_SIGNATURE_ALGORITHM", "SSL.Client.A.SIG"},
    153   {"SSL_CLIENT_DN", "SSL.Client.S.DN"},
    154   {"SSL_CLIENT_CN", "SSL.Client.S.DN.CN"},
    155   {"SSL_CLIENT_EMAIL", "SSL.Client.S.DN.Email"},
    156   {"SSL_CLIENT_O", "SSL.Client.S.DN.O"},
    157   {"SSL_CLIENT_OU", "SSL.Client.S.DN.OU"},
    158   {"SSL_CLIENT_C", "SSL.Client.S.DN.C"},
    159   {"SSL_CLIENT_SP", "SSL.Client.S.DN.SP"},
    160   {"SSL_CLIENT_L", "SSL.Client.S.DN.L"},
    161   {"SSL_CLIENT_IDN", "SSL.Client.I.DN"},
    162   {"SSL_CLIENT_ICN", "SSL.Client.I.DN.CN"},
    163   {"SSL_CLIENT_IEMAIL", "SSL.Client.I.DN.Email"},
    164   {"SSL_CLIENT_IO", "SSL.Client.I.DN.O"},
    165   {"SSL_CLIENT_IOU", "SSL.Client.I.DN.OU"},
    166   {"SSL_CLIENT_IC", "SSL.Client.I.DN.C"},
    167   {"SSL_CLIENT_ISP", "SSL.Client.I.DN.SP"},
    168   {"SSL_CLIENT_IL", "SSL.Client.I.DN.L"},
    169   {"SSL_EXPORT", "SSL.Cipher.Export"},
    170   {"SSL_KEYSIZE", "SSL.Cipher.AlgKeySize"},
    171   {"SSL_SECKEYSIZE", "SSL.Cipher.UseKeySize"},
    172   {"SSL_SSLEAY_VERSION", "SSL.Version.Library"},
    173 /* Old vars not in mod_ssl */
    174   {"SSL_STRONG_CRYPTO", "SSL.Strong.Crypto"},
    175   {"SSL_SERVER_KEY_EXP", "SSL.Server.Key.Exp"},
    176   {"SSL_SERVER_KEY_ALGORITHM", "SSL.Server.Key.Algorithm"},
    177   {"SSL_SERVER_KEY_SIZE", "SSL.Server.Key.Size"},
    178   {"SSL_SERVER_SESSIONDIR", "SSL.Server.SessionDir"},
    179   {"SSL_SERVER_CERTIFICATELOGDIR", "SSL.Server.CertificateLogDir"},
    180   {"SSL_SERVER_CERTFILE", "SSL.Server.CertFile"},
    181   {"SSL_SERVER_KEYFILE", "SSL.Server.KeyFile"},
    182   {"SSL_SERVER_KEYFILETYPE", "SSL.Server.KeyFileType"},
    183   {"SSL_CLIENT_KEY_EXP", "SSL.Client.Key.Exp"},
    184   {"SSL_CLIENT_KEY_ALGORITHM", "SSL.Client.Key.Algorithm"},
    185   {"SSL_CLIENT_KEY_SIZE", "SSL.Client.Key.Size"},
    186   {NULL, NULL}
    187 };
    188 
    189 struct _http_vars
    190 {
    191   char *env_name;
    192   char *hdf_name;
    193 } HTTPVars[] = {
    194   {"HTTP_ACCEPT", "Accept"},
    195   {"HTTP_ACCEPT_CHARSET", "AcceptCharset"},
    196   {"HTTP_ACCEPT_ENCODING", "AcceptEncoding"},
    197   {"HTTP_ACCEPT_LANGUAGE", "AcceptLanguage"},
    198   {"HTTP_COOKIE", "Cookie"},
    199   {"HTTP_HOST", "Host"},
    200   {"HTTP_USER_AGENT", "UserAgent"},
    201   {"HTTP_IF_MODIFIED_SINCE", "IfModifiedSince"},
    202   {"HTTP_REFERER", "Referer"},
    203   {"HTTP_VIA", "Via"},
    204   /* SOAP */
    205   {"HTTP_SOAPACTION", "Soap.Action"},
    206   {NULL, NULL}
    207 };
    208 
    209 static char *Argv0 = "";
    210 
    211 int IgnoreEmptyFormVars = 0;
    212 
    213 static int ExceptionsInit = 0;
    214 NERR_TYPE CGIFinished = -1;
    215 NERR_TYPE CGIUploadCancelled = -1;
    216 NERR_TYPE CGIParseNotHandled = -1;
    217 
    218 static NEOERR *_add_cgi_env_var (CGI *cgi, char *env, char *name)
    219 {
    220   NEOERR *err;
    221   char *s;
    222 
    223   err = cgiwrap_getenv (env, &s);
    224   if (err != STATUS_OK) return nerr_pass (err);
    225   if (s != NULL)
    226   {
    227     err = hdf_set_buf (cgi->hdf, name, s);
    228     if (err != STATUS_OK)
    229     {
    230       free(s);
    231       return nerr_pass (err);
    232     }
    233   }
    234   return STATUS_OK;
    235 }
    236 
    237 char *cgi_url_unescape (char *value)
    238 {
    239   int i = 0, o = 0;
    240   unsigned char *s = (unsigned char *)value;
    241 
    242   if (s == NULL) return value;
    243   while (s[i])
    244   {
    245     if (s[i] == '+')
    246     {
    247       s[o++] = ' ';
    248       i++;
    249     }
    250     else if (s[i] == '%' && isxdigit(s[i+1]) && isxdigit(s[i+2]))
    251     {
    252       char num;
    253       num = (s[i+1] >= 'A') ? ((s[i+1] & 0xdf) - 'A') + 10 : (s[i+1] - '0');
    254       num *= 16;
    255       num += (s[i+2] >= 'A') ? ((s[i+2] & 0xdf) - 'A') + 10 : (s[i+2] - '0');
    256       s[o++] = num;
    257       i+=3;
    258     }
    259     else {
    260       s[o++] = s[i++];
    261     }
    262   }
    263   if (i && o) s[o] = '\0';
    264   return (char *)s;
    265 }
    266 
    267 NEOERR *cgi_js_escape (const char *in, char **esc)
    268 {
    269   return nerr_pass(neos_js_escape(in, esc));
    270 }
    271 
    272 NEOERR *cgi_url_escape (const char *buf, char **esc)
    273 {
    274   return nerr_pass(neos_url_escape(buf, esc, NULL));
    275 }
    276 
    277 NEOERR *cgi_url_escape_more (const char *in, char **esc,
    278                                  const char *other)
    279 {
    280   return nerr_pass(neos_url_escape(in, esc, other));
    281 }
    282 
    283 NEOERR *cgi_url_validate (const char *buf, char **esc)
    284 {
    285   return nerr_pass(neos_url_validate(buf, esc));
    286 }
    287 
    288 static NEOERR *_parse_query (CGI *cgi, char *query)
    289 {
    290   NEOERR *err = STATUS_OK;
    291   char *t, *k, *v, *l;
    292   char buf[256];
    293   char unnamed[10];
    294   int unnamed_count = 0;
    295   HDF *obj, *child;
    296 
    297   if (query && *query)
    298   {
    299     k = strtok_r(query, "&", &l);
    300     while (k && *k)
    301     {
    302       v = strchr(k, '=');
    303       if (v == NULL)
    304       {
    305 	v = "";
    306       }
    307       else
    308       {
    309 	*v = '\0';
    310 	v++;
    311       }
    312 
    313 
    314       /* Check for some invalid query strings */
    315       if (*k == 0) {
    316         /*  '?=foo' gets mapped in as Query._1=foo */
    317         snprintf(unnamed,sizeof(unnamed), "_%d", unnamed_count++);
    318         k = unnamed;
    319       } else if (*k == '.') {
    320         /* an hdf element can't start with a period */
    321         *k = '_';
    322       }
    323       snprintf(buf, sizeof(buf), "Query.%s", cgi_url_unescape(k));
    324 
    325       if (!(cgi->ignore_empty_form_vars && (*v == '\0')))
    326       {
    327 
    328 
    329 	cgi_url_unescape(v);
    330 	obj = hdf_get_obj (cgi->hdf, buf);
    331 	if (obj != NULL)
    332 	{
    333 	  int i = 0;
    334 	  char buf2[10];
    335 	  child = hdf_obj_child (obj);
    336 	  if (child == NULL)
    337 	  {
    338 	    t = hdf_obj_value (obj);
    339 	    err = hdf_set_value (obj, "0", t);
    340 	    if (err != STATUS_OK) break;
    341 	    i = 1;
    342 	  }
    343 	  else
    344 	  {
    345 	    while (child != NULL)
    346 	    {
    347 	      i++;
    348 	      child = hdf_obj_next (child);
    349 	      if (err != STATUS_OK) break;
    350 	    }
    351 	    if (err != STATUS_OK) break;
    352 	  }
    353 	  snprintf (buf2, sizeof(buf2), "%d", i);
    354 	  err = hdf_set_value (obj, buf2, v);
    355 	  if (err != STATUS_OK) break;
    356 	}
    357 	err = hdf_set_value (cgi->hdf, buf, v);
    358 	if (nerr_match(err, NERR_ASSERT)) {
    359 	  STRING str;
    360 
    361 	  string_init(&str);
    362 	  nerr_error_string(err, &str);
    363 	  ne_warn("Unable to set Query value: %s = %s: %s", buf, v, str.buf);
    364 	  string_clear(&str);
    365 	  nerr_ignore(&err);
    366 	}
    367 	if (err != STATUS_OK) break;
    368       }
    369       k = strtok_r(NULL, "&", &l);
    370     }
    371   }
    372   return nerr_pass(err);
    373 }
    374 
    375 /* Is it an error if its a short read? */
    376 static NEOERR *_parse_post_form (CGI *cgi)
    377 {
    378   NEOERR *err = STATUS_OK;
    379   char *l, *query;
    380   int len, r, o;
    381 
    382   l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
    383   if (l == NULL) return STATUS_OK;
    384   len = atoi (l);
    385   if (len <= 0) return STATUS_OK;
    386 
    387   cgi->data_expected = len;
    388 
    389   query = (char *) malloc (sizeof(char) * (len + 1));
    390   if (query == NULL)
    391     return nerr_raise (NERR_NOMEM,
    392 	"Unable to allocate memory to read POST input of length %d", len);
    393 
    394 
    395   o = 0;
    396   while (o < len)
    397   {
    398     cgiwrap_read (query + o, len - o, &r);
    399     if (r <= 0) break;
    400     o = o + r;
    401   }
    402   if (r < 0)
    403   {
    404     free(query);
    405     return nerr_raise_errno (NERR_IO, "Short read on CGI POST input (%d < %d)",
    406 	o, len);
    407   }
    408   if (o != len)
    409   {
    410     free(query);
    411     return nerr_raise (NERR_IO, "Short read on CGI POST input (%d < %d)",
    412 	o, len);
    413   }
    414   query[len] = '\0';
    415   err = _parse_query (cgi, query);
    416   free(query);
    417   return nerr_pass(err);
    418 }
    419 
    420 static NEOERR *_parse_cookie (CGI *cgi)
    421 {
    422   NEOERR *err;
    423   char *cookie;
    424   char *k, *v, *l;
    425   HDF *obj;
    426 
    427   err = hdf_get_copy (cgi->hdf, "HTTP.Cookie", &cookie, NULL);
    428   if (err != STATUS_OK) return nerr_pass(err);
    429   if (cookie == NULL) return STATUS_OK;
    430 
    431   err = hdf_set_value (cgi->hdf, "Cookie", cookie);
    432   if (err != STATUS_OK)
    433   {
    434     free(cookie);
    435     return nerr_pass(err);
    436   }
    437   obj = hdf_get_obj (cgi->hdf, "Cookie");
    438 
    439   k = l = cookie;
    440   while (*l && *l != '=' && *l != ';') l++;
    441   while (*k)
    442   {
    443     if (*l == '=')
    444     {
    445       if (*l) *l++ = '\0';
    446       v = l;
    447       while (*l && *l != ';') l++;
    448       if (*l) *l++ = '\0';
    449     }
    450     else
    451     {
    452       v = "";
    453       if (*l) *l++ = '\0';
    454     }
    455     k = neos_strip (k);
    456     v = neos_strip (v);
    457     if (k[0] && v[0])
    458     {
    459       err = hdf_set_value (obj, k, v);
    460       if (nerr_match(err, NERR_ASSERT)) {
    461 	STRING str;
    462 
    463 	string_init(&str);
    464 	nerr_error_string(err, &str);
    465 	ne_warn("Unable to set Cookie value: %s = %s: %s", k, v, str.buf);
    466 	string_clear(&str);
    467 	nerr_ignore(&err);
    468       }
    469       if (err) break;
    470     }
    471     k = l;
    472     while (*l && *l != '=' && *l != ';') l++;
    473   }
    474 
    475   free (cookie);
    476 
    477   return nerr_pass(err);
    478 }
    479 
    480 #ifdef ENABLE_REMOTE_DEBUG
    481 
    482 static void _launch_debugger (CGI *cgi, char *display)
    483 {
    484   pid_t myPid, pid;
    485   char buffer[127];
    486   char *debugger;
    487   HDF *obj;
    488   char *allowed;
    489 
    490   /* Only allow remote debugging from allowed hosts */
    491   for (obj = hdf_get_child (cgi->hdf, "Config.Displays");
    492       obj; obj = hdf_obj_next (obj))
    493   {
    494     allowed = hdf_obj_value (obj);
    495     if (allowed && !strcmp (display, allowed)) break;
    496   }
    497   if (obj == NULL) return;
    498 
    499   myPid = getpid();
    500 
    501   if ((pid = fork()) < 0)
    502     return;
    503 
    504   if ((debugger = hdf_get_value (cgi->hdf, "Config.Debugger", NULL)) == NULL)
    505   {
    506     debugger = "/usr/local/bin/sudo /usr/local/bin/ddd -display %s %s %d";
    507   }
    508 
    509   if (!pid)
    510   {
    511     sprintf(buffer, debugger, display, Argv0, myPid);
    512     execl("/bin/sh", "sh", "-c", buffer, NULL);
    513   }
    514   else
    515   {
    516     sleep(60);
    517   }
    518 }
    519 
    520 #endif
    521 
    522 static NEOERR *cgi_pre_parse (CGI *cgi)
    523 {
    524   NEOERR *err;
    525   int x = 0;
    526   char buf[256];
    527   char *query;
    528 
    529   while (CGIVars[x].env_name)
    530   {
    531     snprintf (buf, sizeof(buf), "CGI.%s", CGIVars[x].hdf_name);
    532     err = _add_cgi_env_var(cgi, CGIVars[x].env_name, buf);
    533     if (err != STATUS_OK) return nerr_pass (err);
    534     x++;
    535   }
    536   x = 0;
    537   while (HTTPVars[x].env_name)
    538   {
    539     snprintf (buf, sizeof(buf), "HTTP.%s", HTTPVars[x].hdf_name);
    540     err = _add_cgi_env_var(cgi, HTTPVars[x].env_name, buf);
    541     if (err != STATUS_OK) return nerr_pass (err);
    542     x++;
    543   }
    544   err = _parse_cookie(cgi);
    545   if (err != STATUS_OK) return nerr_pass (err);
    546 
    547   err = hdf_get_copy (cgi->hdf, "CGI.QueryString", &query, NULL);
    548   if (err != STATUS_OK) return nerr_pass (err);
    549   if (query != NULL)
    550   {
    551     err = _parse_query(cgi, query);
    552     free(query);
    553     if (err != STATUS_OK) return nerr_pass (err);
    554   }
    555 
    556   {
    557     char *d = hdf_get_value(cgi->hdf, "Query.debug_pause", NULL);
    558     char *d_p = hdf_get_value(cgi->hdf, "Config.DebugPassword", NULL);
    559 
    560     if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
    561         d && d_p && !strcmp(d, d_p)) {
    562       sleep(20);
    563     }
    564   }
    565 
    566 #ifdef ENABLE_REMOTE_DEBUG
    567   {
    568     char *display;
    569 
    570     display = hdf_get_value (cgi->hdf, "Query.xdisplay", NULL);
    571     if (display)
    572     {
    573       fprintf(stderr, "** Got display %s\n", display);
    574       _launch_debugger(cgi, display);
    575     }
    576   }
    577 #endif
    578 
    579   return STATUS_OK;
    580 }
    581 
    582 NEOERR *cgi_register_parse_cb(CGI *cgi, const char *method, const char *ctype,
    583                               void *rock, CGI_PARSE_CB parse_cb)
    584 {
    585   struct _cgi_parse_cb *my_pcb;
    586 
    587   if (method == NULL || ctype == NULL)
    588     return nerr_raise(NERR_ASSERT, "method and type must not be NULL to register cb");
    589 
    590   my_pcb = (struct _cgi_parse_cb *) calloc(1, sizeof(struct _cgi_parse_cb));
    591   if (my_pcb == NULL)
    592     return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb");
    593 
    594   my_pcb->method = strdup(method);
    595   my_pcb->ctype = strdup(ctype);
    596   if (my_pcb->method == NULL || my_pcb->ctype == NULL)
    597   {
    598     if (my_pcb->method != NULL)
    599       free(my_pcb->method);
    600     if (my_pcb->ctype != NULL)
    601       free(my_pcb->ctype);
    602     free(my_pcb);
    603     return nerr_raise(NERR_NOMEM, "Unable to allocate memory to register parse cb");
    604   }
    605   if (!strcmp(my_pcb->method, "*"))
    606     my_pcb->any_method = 1;
    607   if (!strcmp(my_pcb->ctype, "*"))
    608     my_pcb->any_ctype = 1;
    609   my_pcb->rock = rock;
    610   my_pcb->parse_cb = parse_cb;
    611   my_pcb->next = cgi->parse_callbacks;
    612   cgi->parse_callbacks = my_pcb;
    613   return STATUS_OK;
    614 }
    615 
    616 NEOERR *cgi_parse (CGI *cgi)
    617 {
    618   NEOERR *err;
    619   char *method, *type;
    620   struct _cgi_parse_cb *pcb;
    621 
    622 
    623   method = hdf_get_value (cgi->hdf, "CGI.RequestMethod", "GET");
    624   type = hdf_get_value (cgi->hdf, "CGI.ContentType", NULL);
    625   /* Walk the registered parse callbacks for a matching one */
    626   pcb = cgi->parse_callbacks;
    627   while (pcb != NULL)
    628   {
    629     if ( (pcb->any_method || !strcasecmp(pcb->method, method)) &&
    630 	 (pcb->any_ctype || (type && !strcasecmp(pcb->ctype, type))) )
    631     {
    632       err = pcb->parse_cb(cgi, method, type, pcb->rock);
    633       if (err && !nerr_handle(&err, CGIParseNotHandled))
    634 	return nerr_pass(err);
    635     }
    636     pcb = pcb->next;
    637   }
    638 
    639   /* Fallback to internal methods */
    640 
    641   if (!strcmp(method, "POST"))
    642   {
    643     if (type && !strcmp(type, "application/x-www-form-urlencoded"))
    644     {
    645       err = _parse_post_form(cgi);
    646       if (err != STATUS_OK) return nerr_pass (err);
    647     }
    648     else if (type && !strncmp (type, "multipart/form-data", 19))
    649     {
    650       err = parse_rfc2388 (cgi);
    651       if (err != STATUS_OK) return nerr_pass (err);
    652     }
    653 #if 0
    654     else
    655     {
    656       int len, x, r;
    657       char *l;
    658       char buf[4096];
    659       FILE *fp;
    660 
    661       fp = fopen("/tmp/upload", "w");
    662 
    663       l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
    664       if (l == NULL) return STATUS_OK;
    665       len = atoi (l);
    666 
    667       x = 0;
    668       while (x < len)
    669       {
    670 	if (len-x > sizeof(buf))
    671 	  cgiwrap_read (buf, sizeof(buf), &r);
    672 	else
    673 	  cgiwrap_read (buf, len - x, &r);
    674 	fwrite (buf, 1, r, fp);
    675 	x += r;
    676       }
    677       fclose (fp);
    678       if (err) return nerr_pass(err);
    679     }
    680 #endif
    681   }
    682   else if (!strcmp(method, "PUT"))
    683   {
    684     FILE *fp;
    685     int len, x, r, w;
    686     char *l;
    687     char buf[4096];
    688     int unlink_files = hdf_get_int_value(cgi->hdf, "Config.Upload.Unlink", 1);
    689 
    690     err = open_upload(cgi, unlink_files, &fp);
    691     if (err) return nerr_pass(err);
    692 
    693     l = hdf_get_value (cgi->hdf, "CGI.ContentLength", NULL);
    694     if (l == NULL) return STATUS_OK;
    695     len = atoi (l);
    696     if (len <= 0) return STATUS_OK;
    697 
    698     x = 0;
    699     while (x < len)
    700     {
    701       if (len-x > sizeof(buf))
    702 	cgiwrap_read (buf, sizeof(buf), &r);
    703       else
    704 	cgiwrap_read (buf, len - x, &r);
    705       w = fwrite (buf, sizeof(char), r, fp);
    706       if (w != r)
    707       {
    708 	err = nerr_raise_errno(NERR_IO, "Short write on PUT: %d < %d", w, r);
    709 	break;
    710       }
    711       x += r;
    712     }
    713     if (err) return nerr_pass(err);
    714     fseek(fp, 0, SEEK_SET);
    715     l = hdf_get_value(cgi->hdf, "CGI.PathInfo", NULL);
    716     if (l) err = hdf_set_value (cgi->hdf, "PUT", l);
    717     if (err) return nerr_pass(err);
    718     if (type) err = hdf_set_value (cgi->hdf, "PUT.Type", type);
    719     if (err) return nerr_pass(err);
    720     err = hdf_set_int_value (cgi->hdf, "PUT.FileHandle", uListLength(cgi->files));
    721     if (err) return nerr_pass(err);
    722     if (!unlink_files)
    723     {
    724       char *name;
    725       err = uListGet(cgi->filenames, uListLength(cgi->filenames)-1,
    726 	  (void *)&name);
    727       if (err) return nerr_pass(err);
    728       err = hdf_set_value (cgi->hdf, "PUT.FileName", name);
    729       if (err) return nerr_pass(err);
    730     }
    731   }
    732   return STATUS_OK;
    733 }
    734 
    735 NEOERR *cgi_init (CGI **cgi, HDF *hdf)
    736 {
    737   NEOERR *err = STATUS_OK;
    738   CGI *mycgi;
    739 
    740   if (ExceptionsInit == 0)
    741   {
    742     err = nerr_init();
    743     if (err) return nerr_pass(err);
    744     err = nerr_register(&CGIFinished, "CGIFinished");
    745     if (err) return nerr_pass(err);
    746     err = nerr_register(&CGIUploadCancelled, "CGIUploadCancelled");
    747     if (err) return nerr_pass(err);
    748     err = nerr_register(&CGIUploadCancelled, "CGIParseNotHandled");
    749     if (err) return nerr_pass(err);
    750     ExceptionsInit = 1;
    751   }
    752 
    753   *cgi = NULL;
    754   mycgi = (CGI *) calloc (1, sizeof(CGI));
    755   if (mycgi == NULL)
    756     return nerr_raise(NERR_NOMEM, "Unable to allocate space for CGI");
    757 
    758   mycgi->time_start = ne_timef();
    759 
    760   mycgi->ignore_empty_form_vars = IgnoreEmptyFormVars;
    761 
    762   do
    763   {
    764     if (hdf == NULL)
    765     {
    766       err = hdf_init (&(mycgi->hdf));
    767       if (err != STATUS_OK) break;
    768     }
    769     else
    770     {
    771       mycgi->hdf = hdf;
    772     }
    773     err = cgi_pre_parse (mycgi);
    774     if (err != STATUS_OK) break;
    775 
    776   } while (0);
    777 
    778   if (err == STATUS_OK)
    779     *cgi = mycgi;
    780   else
    781   {
    782     cgi_destroy(&mycgi);
    783   }
    784   return nerr_pass(err);
    785 }
    786 
    787 static void _destroy_tmp_file(char *filename)
    788 {
    789   unlink(filename);
    790   free(filename);
    791 }
    792 
    793 void cgi_destroy (CGI **cgi)
    794 {
    795   CGI *my_cgi;
    796 
    797   if (!cgi || !*cgi)
    798     return;
    799   my_cgi = *cgi;
    800   if (my_cgi->hdf)
    801     hdf_destroy (&(my_cgi->hdf));
    802   if (my_cgi->buf)
    803     free(my_cgi->buf);
    804   if (my_cgi->files)
    805     uListDestroyFunc(&(my_cgi->files), (void (*)(void *))fclose);
    806   if (my_cgi->filenames)
    807     uListDestroyFunc(&(my_cgi->filenames), (void (*)(void *))_destroy_tmp_file);
    808   free (*cgi);
    809   *cgi = NULL;
    810 }
    811 
    812 static NEOERR *render_cb (void *ctx, char *buf)
    813 {
    814   STRING *str = (STRING *)ctx;
    815   NEOERR *err;
    816 
    817   err = nerr_pass(string_append(str, buf));
    818   return err;
    819 }
    820 
    821 static NEOERR *cgi_headers (CGI *cgi)
    822 {
    823   NEOERR *err = STATUS_OK;
    824   HDF *obj, *child;
    825   char *s, *charset = NULL;
    826 
    827   if (hdf_get_int_value (cgi->hdf, "Config.NoCache", 0))
    828   {
    829     /* Ok, we try really hard to defeat caches here */
    830     /* this isn't in any HTTP rfc's, it just seems to be a convention */
    831     err = cgiwrap_writef ("Pragma: no-cache\r\n");
    832     if (err != STATUS_OK) return nerr_pass (err);
    833     err = cgiwrap_writef ("Expires: Fri, 01 Jan 1990 00:00:00 GMT\r\n");
    834     if (err != STATUS_OK) return nerr_pass (err);
    835     err = cgiwrap_writef ("Cache-control: no-cache, must-revalidate, no-cache=\"Set-Cookie\", private\r\n");
    836     if (err != STATUS_OK) return nerr_pass (err);
    837   }
    838   obj = hdf_get_obj (cgi->hdf, "cgiout");
    839   if (obj)
    840   {
    841     s = hdf_get_value (obj, "Status", NULL);
    842     if (s)
    843       err = cgiwrap_writef ("Status: %s\r\n", s);
    844     if (err != STATUS_OK) return nerr_pass (err);
    845     s = hdf_get_value (obj, "Location", NULL);
    846     if (s)
    847       err = cgiwrap_writef ("Location: %s\r\n", s);
    848     if (err != STATUS_OK) return nerr_pass (err);
    849     child = hdf_get_obj (cgi->hdf, "cgiout.other");
    850     if (child)
    851     {
    852       child = hdf_obj_child (child);
    853       while (child != NULL)
    854       {
    855 	s = hdf_obj_value (child);
    856 	err = cgiwrap_writef ("%s\r\n", s);
    857 	if (err != STATUS_OK) return nerr_pass (err);
    858 	child = hdf_obj_next(child);
    859       }
    860     }
    861     charset = hdf_get_value (obj, "charset", NULL);
    862     s = hdf_get_value (obj, "ContentType", "text/html");
    863     if (charset)
    864       err = cgiwrap_writef ("Content-Type: %s; charset=%s\r\n\r\n", s, charset);
    865     else
    866       err = cgiwrap_writef ("Content-Type: %s\r\n\r\n", s);
    867     if (err != STATUS_OK) return nerr_pass (err);
    868   }
    869   else
    870   {
    871     /* Default */
    872     err = cgiwrap_writef ("Content-Type: text/html\r\n\r\n");
    873     if (err != STATUS_OK) return nerr_pass (err);
    874   }
    875   return STATUS_OK;
    876 }
    877 
    878 #if defined(HTML_COMPRESSION)
    879 /* Copy these here from zutil.h, which we aren't supposed to include */
    880 #define DEF_MEM_LEVEL 8
    881 #define OS_CODE 0x03
    882 
    883 static NEOERR *cgi_compress (STRING *str, char *obuf, int *olen)
    884 {
    885   z_stream stream;
    886   int err;
    887 
    888   stream.next_in = (Bytef*)str->buf;
    889   stream.avail_in = (uInt)str->len;
    890   stream.next_out = (Bytef*)obuf;
    891   stream.avail_out = (uInt)*olen;
    892   if ((uLong)stream.avail_out != *olen)
    893     return nerr_raise(NERR_NOMEM, "Destination too big: %d", *olen);
    894 
    895   stream.zalloc = (alloc_func)0;
    896   stream.zfree = (free_func)0;
    897   stream.opaque = (voidpf)0;
    898 
    899   /* err = deflateInit(&stream, Z_DEFAULT_COMPRESSION); */
    900   err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
    901   if (err != Z_OK)
    902     return nerr_raise(NERR_SYSTEM, "deflateInit2 returned %d", err);
    903 
    904   err = deflate(&stream, Z_FINISH);
    905   if (err != Z_STREAM_END) {
    906     deflateEnd(&stream);
    907     return nerr_raise(NERR_SYSTEM, "deflate returned %d", err);
    908   }
    909   *olen = stream.total_out;
    910 
    911   err = deflateEnd(&stream);
    912   return STATUS_OK;
    913 }
    914 #endif
    915 
    916 /* This ws strip function is Dave's version, designed to make debug
    917  * easier, and the output a bit smaller... but not as small as it could
    918  * be: essentially, it strips all empty lines, all extra space at the
    919  * end of the line, except in pre/textarea tags.
    920  *
    921  * Ok, expanding to 3 levels:
    922  * 0 - No stripping
    923  * 1 - Dave's debug stripper (as above)
    924  * 2 - strip all extra white space
    925  *
    926  * We don't currently strip white space in a tag
    927  *
    928  * */
    929 
    930 #if 0
    931 static void debug_output(char *header, char *s, int n)
    932 {
    933   int x;
    934   fprintf (stderr, "%s ", header);
    935   for (x = 0; x < n; x++)
    936   {
    937     fprintf (stderr, "%c", s[x]);
    938   }
    939   fprintf(stderr, "\n");
    940 }
    941 #endif
    942 
    943 void cgi_html_ws_strip(STRING *str, int level)
    944 {
    945   int ws = 0;
    946   int seen_nonws = level > 1;
    947   int i, o, l;
    948   char *ch;
    949 
    950   i = o = 0;
    951   if (str->len) {
    952     ws = isspace(str->buf[0]);
    953   }
    954   while (i < str->len)
    955   {
    956     if (str->buf[i] == '<')
    957     {
    958       str->buf[o++] = str->buf[i++];
    959       if (!strncasecmp(str->buf + i, "textarea", 8))
    960       {
    961 	ch = str->buf + i;
    962 	do
    963 	{
    964 	  ch = strchr(ch, '<');
    965 	  if (ch == NULL)
    966 	  {
    967 	    memmove(str->buf + o, str->buf + i, str->len - i);
    968 	    str->len = o + str->len - i;
    969 	    str->buf[str->len] = '\0';
    970 	    return;
    971 	  }
    972 	  ch++;
    973 	} while (strncasecmp(ch, "/textarea>", 10));
    974 	ch += 10;
    975 	l = ch - str->buf - i;
    976 	memmove(str->buf + o, str->buf + i, l);
    977 	o += l;
    978 	i += l;
    979       }
    980       else if (!strncasecmp(str->buf + i, "pre", 3))
    981       {
    982 	ch = str->buf + i;
    983 	do
    984 	{
    985 	  ch = strchr(ch, '<');
    986 	  if (ch == NULL)
    987 	  {
    988 	    memmove(str->buf + o, str->buf + i, str->len - i);
    989 	    str->len = o + str->len - i;
    990 	    str->buf[str->len] = '\0';
    991 	    return;
    992 	  }
    993 	  ch++;
    994 	} while (strncasecmp(ch, "/pre>", 5));
    995 	ch += 5;
    996 	l = ch - str->buf - i;
    997 	memmove(str->buf + o, str->buf + i, l);
    998 	o += l;
    999 	i += l;
   1000       }
   1001       else
   1002       {
   1003 	ch = strchr(str->buf + i, '>');
   1004 	if (ch == NULL)
   1005 	{
   1006 	  memmove(str->buf + o, str->buf + i, str->len - i);
   1007 	  str->len = o + str->len - i;
   1008 	  str->buf[str->len] = '\0';
   1009 	  return;
   1010 	}
   1011 	ch++;
   1012 	/* debug_output("copying tag", str->buf + i, ch - str->buf - i);
   1013 	 * */
   1014 	l = ch - str->buf - i;
   1015 	memmove(str->buf + o, str->buf + i, l);
   1016 	o += l;
   1017 	i += l;
   1018       }
   1019       /* debug_output("result", str->buf, o); */
   1020       seen_nonws = 1;
   1021       ws = 0;
   1022     }
   1023     else if (str->buf[i] == '\n')
   1024     {
   1025       /* This has the effect of erasing all whitespace at the eol plus
   1026        * erasing all blank lines */
   1027       while (o && isspace(str->buf[o-1])) o--;
   1028       str->buf[o++] = str->buf[i++];
   1029       ws = level > 1;
   1030       seen_nonws = level > 1;
   1031     }
   1032     else if (seen_nonws && isspace(str->buf[i]))
   1033     {
   1034       if (ws)
   1035       {
   1036 	i++;
   1037       }
   1038       else
   1039       {
   1040 	str->buf[o++] = str->buf[i++];
   1041 	ws = 1;
   1042       }
   1043     }
   1044     else
   1045     {
   1046       seen_nonws = 1;
   1047       ws = 0;
   1048       str->buf[o++] = str->buf[i++];
   1049     }
   1050   }
   1051 
   1052   str->len = o;
   1053   str->buf[str->len] = '\0';
   1054 }
   1055 
   1056 NEOERR *cgi_output (CGI *cgi, STRING *str)
   1057 {
   1058   NEOERR *err = STATUS_OK;
   1059   double dis;
   1060   int is_html = 0;
   1061   int use_deflate = 0;
   1062   int use_gzip = 0;
   1063   int do_debug = 0;
   1064   int do_timefooter = 0;
   1065   int ws_strip_level = 0;
   1066   char *s, *e;
   1067 
   1068   s = hdf_get_value (cgi->hdf, "Query.debug", NULL);
   1069   e = hdf_get_value (cgi->hdf, "Config.DebugPassword", NULL);
   1070   if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
   1071       s && e && !strcmp(s, e)) do_debug = 1;
   1072   do_timefooter = hdf_get_int_value (cgi->hdf, "Config.TimeFooter", 1);
   1073   ws_strip_level = hdf_get_int_value (cgi->hdf, "Config.WhiteSpaceStrip", 1);
   1074 
   1075   dis = ne_timef();
   1076   s = hdf_get_value (cgi->hdf, "cgiout.ContentType", "text/html");
   1077   if (!strcasecmp(s, "text/html"))
   1078     is_html = 1;
   1079 
   1080 #if defined(HTML_COMPRESSION)
   1081   /* Determine whether or not we can compress the output */
   1082   if (is_html && hdf_get_int_value (cgi->hdf, "Config.CompressionEnabled", 0))
   1083   {
   1084     err = hdf_get_copy (cgi->hdf, "HTTP.AcceptEncoding", &s, NULL);
   1085     if (err != STATUS_OK) return nerr_pass (err);
   1086     if (s)
   1087     {
   1088       char *next;
   1089 
   1090       e = strtok_r (s, ",", &next);
   1091       while (e && !use_deflate)
   1092       {
   1093 	if (strstr(e, "deflate") != NULL)
   1094 	{
   1095 	  use_deflate = 1;
   1096 	  use_gzip = 0;
   1097 	}
   1098 	else if (strstr(e, "gzip") != NULL)
   1099 	  use_gzip = 1;
   1100 	e = strtok_r (NULL, ",", &next);
   1101       }
   1102       free (s);
   1103     }
   1104     s = hdf_get_value (cgi->hdf, "HTTP.UserAgent", NULL);
   1105     if (s)
   1106     {
   1107       if (strstr(s, "MSIE 4") || strstr(s, "MSIE 5") || strstr(s, "MSIE 6"))
   1108       {
   1109 	e = hdf_get_value (cgi->hdf, "HTTP.Accept", NULL);
   1110 	if (e && !strcmp(e, "*/*"))
   1111 	{
   1112 	  use_deflate = 0;
   1113 	  use_gzip = 0;
   1114 	}
   1115       }
   1116       else
   1117       {
   1118 	if (strncasecmp(s, "mozilla/5.", 10))
   1119 	{
   1120 	  use_deflate = 0;
   1121 	  use_gzip = 0;
   1122 	}
   1123       }
   1124     }
   1125     else
   1126     {
   1127       use_deflate = 0;
   1128       use_gzip = 0;
   1129     }
   1130     if (use_deflate)
   1131     {
   1132       err = hdf_set_value (cgi->hdf, "cgiout.other.encoding",
   1133 	  "Content-Encoding: deflate");
   1134     }
   1135     else if (use_gzip)
   1136     {
   1137       err = hdf_set_value (cgi->hdf, "cgiout.other.encoding",
   1138 	  "Content-Encoding: gzip");
   1139     }
   1140     if (err != STATUS_OK) return nerr_pass(err);
   1141   }
   1142 #endif
   1143 
   1144   err = cgi_headers(cgi);
   1145   if (err != STATUS_OK) return nerr_pass(err);
   1146 
   1147   if (is_html)
   1148   {
   1149     char buf[50];
   1150     int x;
   1151 
   1152     if (do_timefooter)
   1153     {
   1154       snprintf (buf, sizeof(buf), "\n<!-- %5.3f:%d -->\n",
   1155 	  dis - cgi->time_start, use_deflate || use_gzip);
   1156       err = string_append (str, buf);
   1157       if (err != STATUS_OK) return nerr_pass(err);
   1158     }
   1159 
   1160     if (ws_strip_level)
   1161     {
   1162       cgi_html_ws_strip(str, ws_strip_level);
   1163     }
   1164 
   1165     if (do_debug)
   1166     {
   1167       err = string_append (str, "<hr>");
   1168       if (err != STATUS_OK) return nerr_pass(err);
   1169       x = 0;
   1170       while (1)
   1171       {
   1172 	char *k, *v;
   1173 	err = cgiwrap_iterenv (x, &k, &v);
   1174 	if (err != STATUS_OK) return nerr_pass(err);
   1175 	if (k == NULL) break;
   1176 	err =string_appendf (str, "%s = %s<br>", k, v);
   1177 	if (err != STATUS_OK) return nerr_pass(err);
   1178 	free(k);
   1179 	free(v);
   1180 	x++;
   1181       }
   1182       err = string_append (str, "<pre>");
   1183       if (err != STATUS_OK) return nerr_pass(err);
   1184       err = hdf_dump_str (cgi->hdf, NULL, 0, str);
   1185       if (err != STATUS_OK) return nerr_pass(err);
   1186     }
   1187   }
   1188 
   1189 #if defined(HTML_COMPRESSION)
   1190     if (is_html && (use_deflate || use_gzip))
   1191     {
   1192       char *dest;
   1193       static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
   1194       char gz_buf[20]; /* gzip header/footer buffer, len of header is 10 bytes */
   1195       unsigned int crc = 0;
   1196       int len2;
   1197 
   1198       if (use_gzip)
   1199       {
   1200 	crc = crc32(0L, Z_NULL, 0);
   1201 	crc = crc32(crc, (const Bytef *)(str->buf), str->len);
   1202       }
   1203       len2 = str->len * 2;
   1204       dest = (char *) malloc (sizeof(char) * len2);
   1205       if (dest != NULL)
   1206       {
   1207 	do {
   1208 	  err = cgi_compress (str, dest, &len2);
   1209 	  if (err == STATUS_OK)
   1210 	  {
   1211 	    if (use_gzip)
   1212 	    {
   1213 	      /* I'm using sprintf instead of cgiwrap_writef since
   1214 	       * the wrapper writef might not handle values with
   1215 	       * embedded NULLs... though I should fix the python one
   1216 	       * now as well */
   1217 	      sprintf(gz_buf, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1],
   1218 		  Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/,
   1219 		  OS_CODE);
   1220 	      err = cgiwrap_write(gz_buf, 10);
   1221 	    }
   1222 	    if (err != STATUS_OK) break;
   1223 	    err = cgiwrap_write(dest, len2);
   1224 	    if (err != STATUS_OK) break;
   1225 
   1226 	    if (use_gzip)
   1227 	    {
   1228 	      /* write crc and len in network order */
   1229 	      sprintf(gz_buf, "%c%c%c%c%c%c%c%c",
   1230 		  (0xff & (crc >> 0)),
   1231 		  (0xff & (crc >> 8)),
   1232 		  (0xff & (crc >> 16)),
   1233 		  (0xff & (crc >> 24)),
   1234 		  (0xff & (str->len >> 0)),
   1235 		  (0xff & (str->len >> 8)),
   1236 		  (0xff & (str->len >> 16)),
   1237 		  (0xff & (str->len >> 24)));
   1238 	      err = cgiwrap_write(gz_buf, 8);
   1239 	      if (err != STATUS_OK) break;
   1240 	    }
   1241 	  }
   1242 	  else
   1243 	  {
   1244 	    nerr_log_error (err);
   1245 	    err = cgiwrap_write(str->buf, str->len);
   1246 	  }
   1247 	} while (0);
   1248 	free (dest);
   1249       }
   1250       else
   1251       {
   1252 	err = cgiwrap_write(str->buf, str->len);
   1253       }
   1254     }
   1255     else
   1256 #endif
   1257     {
   1258       err = cgiwrap_write(str->buf, str->len);
   1259     }
   1260 
   1261   return nerr_pass(err);
   1262 }
   1263 
   1264 NEOERR *cgi_html_escape_strfunc(const char *str, char **ret)
   1265 {
   1266   return nerr_pass(html_escape_alloc(str, strlen(str), ret));
   1267 }
   1268 
   1269 NEOERR *cgi_html_strip_strfunc(const char *str, char **ret)
   1270 {
   1271   return nerr_pass(html_strip_alloc(str, strlen(str), ret));
   1272 }
   1273 
   1274 NEOERR *cgi_text_html_strfunc(const char *str, char **ret)
   1275 {
   1276   return nerr_pass(convert_text_html_alloc(str, strlen(str), ret));
   1277 }
   1278 
   1279 NEOERR *cgi_register_strfuncs(CSPARSE *cs)
   1280 {
   1281   NEOERR *err;
   1282 
   1283   err = cs_register_esc_strfunc(cs, "url_escape", cgi_url_escape);
   1284   if (err != STATUS_OK) return nerr_pass(err);
   1285   err = cs_register_esc_strfunc(cs, "html_escape", cgi_html_escape_strfunc);
   1286   if (err != STATUS_OK) return nerr_pass(err);
   1287   err = cs_register_strfunc(cs, "text_html", cgi_text_html_strfunc);
   1288   if (err != STATUS_OK) return nerr_pass(err);
   1289   err = cs_register_esc_strfunc(cs, "js_escape", cgi_js_escape);
   1290   if (err != STATUS_OK) return nerr_pass(err);
   1291   err = cs_register_strfunc(cs, "html_strip", cgi_html_strip_strfunc);
   1292   if (err != STATUS_OK) return nerr_pass(err);
   1293   err = cs_register_esc_strfunc(cs, "url_validate", cgi_url_validate);
   1294   if (err != STATUS_OK) return nerr_pass(err);
   1295   return STATUS_OK;
   1296 }
   1297 
   1298 NEOERR *cgi_cs_init(CGI *cgi, CSPARSE **cs)
   1299 {
   1300   NEOERR *err;
   1301 
   1302   *cs = NULL;
   1303 
   1304   do
   1305   {
   1306     err = cs_init (cs, cgi->hdf);
   1307     if (err != STATUS_OK) break;
   1308     err = cgi_register_strfuncs(*cs);
   1309     if (err != STATUS_OK) break;
   1310   } while (0);
   1311 
   1312   if (err && *cs) cs_destroy(cs);
   1313   return nerr_pass(err);
   1314 }
   1315 
   1316 NEOERR *cgi_display (CGI *cgi, const char *cs_file)
   1317 {
   1318   NEOERR *err = STATUS_OK;
   1319   char *debug;
   1320   CSPARSE *cs = NULL;
   1321   STRING str;
   1322   int do_dump = 0;
   1323   char *t;
   1324 
   1325   string_init(&str);
   1326 
   1327   debug = hdf_get_value (cgi->hdf, "Query.debug", NULL);
   1328   t = hdf_get_value (cgi->hdf, "Config.DumpPassword", NULL);
   1329   if (hdf_get_int_value(cgi->hdf, "Config.DebugEnabled", 0) &&
   1330       debug && t && !strcmp (debug, t)) do_dump = 1;
   1331 
   1332   do
   1333   {
   1334     err = cs_init (&cs, cgi->hdf);
   1335     if (err != STATUS_OK) break;
   1336     err = cgi_register_strfuncs(cs);
   1337     if (err != STATUS_OK) break;
   1338     err = cs_parse_file (cs, cs_file);
   1339     if (err != STATUS_OK) break;
   1340     if (do_dump)
   1341     {
   1342       cgiwrap_writef("Content-Type: text/plain\n\n");
   1343       hdf_dump_str(cgi->hdf, "", 0, &str);
   1344       cs_dump(cs, &str, render_cb);
   1345       cgiwrap_writef("%s", str.buf);
   1346       break;
   1347     }
   1348     else
   1349     {
   1350       err = cs_render (cs, &str, render_cb);
   1351       if (err != STATUS_OK) break;
   1352     }
   1353     err = cgi_output(cgi, &str);
   1354     if (err != STATUS_OK) break;
   1355   } while (0);
   1356 
   1357   cs_destroy(&cs);
   1358   string_clear (&str);
   1359   return nerr_pass(err);
   1360 }
   1361 
   1362 void cgi_neo_error (CGI *cgi, NEOERR *err)
   1363 {
   1364   STRING str;
   1365 
   1366   string_init(&str);
   1367   cgiwrap_writef("Status: 500\n");
   1368   cgiwrap_writef("Content-Type: text/html\n\n");
   1369 
   1370   cgiwrap_writef("<html><body>\nAn error occured:<pre>");
   1371   nerr_error_traceback(err, &str);
   1372   cgiwrap_write(str.buf, str.len);
   1373   cgiwrap_writef("</pre></body></html>\n");
   1374 }
   1375 
   1376 void cgi_error (CGI *cgi, const char *fmt, ...)
   1377 {
   1378   va_list ap;
   1379 
   1380   cgiwrap_writef("Status: 500\n");
   1381   cgiwrap_writef("Content-Type: text/html\n\n");
   1382   cgiwrap_writef("<html><body>\nAn error occured:<pre>");
   1383   va_start (ap, fmt);
   1384   cgiwrap_writevf (fmt, ap);
   1385   va_end (ap);
   1386   cgiwrap_writef("</pre></body></html>\n");
   1387 }
   1388 
   1389 void cgi_debug_init (int argc, char **argv)
   1390 {
   1391   FILE *fp;
   1392   char line[4096];
   1393   char *v, *k;
   1394 
   1395   Argv0 = argv[0];
   1396 
   1397   if (argc)
   1398   {
   1399     fp = fopen(argv[1], "r");
   1400     if (fp == NULL)
   1401       return;
   1402 
   1403     while (fgets(line, sizeof(line), fp) != NULL)
   1404     {
   1405       v = strchr(line, '=');
   1406       if (v != NULL)
   1407       {
   1408 	*v = '\0';
   1409 	v = neos_strip(v+1);
   1410 	k = neos_strip(line);
   1411 	cgiwrap_putenv (line, v);
   1412       }
   1413     }
   1414     fclose(fp);
   1415   }
   1416 }
   1417 
   1418 void cgi_vredirect (CGI *cgi, int uri, const char *fmt, va_list ap)
   1419 {
   1420   cgiwrap_writef ("Status: 302\r\n");
   1421   cgiwrap_writef ("Content-Type: text/html\r\n");
   1422   cgiwrap_writef ("Pragma: no-cache\r\n");
   1423   cgiwrap_writef ("Expires: Fri, 01 Jan 1999 00:00:00 GMT\r\n");
   1424   cgiwrap_writef ("Cache-control: no-cache, no-cache=\"Set-Cookie\", private\r\n");
   1425 
   1426   if (uri)
   1427   {
   1428     cgiwrap_writef ("Location: ");
   1429   }
   1430   else
   1431   {
   1432     char *host;
   1433     int https = 0;
   1434 
   1435     if (!strcmp(hdf_get_value(cgi->hdf, "CGI.HTTPS", "off"), "on"))
   1436     {
   1437       https = 1;
   1438     }
   1439 
   1440     host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL);
   1441     if (host == NULL)
   1442       host = hdf_get_value (cgi->hdf, "CGI.ServerName", "localhost");
   1443 
   1444     cgiwrap_writef ("Location: %s://%s", https ? "https" : "http", host);
   1445 
   1446     if ((strchr(host, ':') == NULL)) {
   1447       int port = hdf_get_int_value(cgi->hdf, "CGI.ServerPort", 80);
   1448 
   1449       if (!((https && port == 443) || (!https && port == 80)))
   1450       {
   1451 	cgiwrap_writef(":%d", port);
   1452       }
   1453     }
   1454   }
   1455   cgiwrap_writevf (fmt, ap);
   1456   cgiwrap_writef ("\r\n\r\n");
   1457   cgiwrap_writef ("Redirect page<br><br>\n");
   1458 #if 0
   1459   /* Apparently this crashes on some computers... I don't know if its
   1460    * legal to reuse the va_list */
   1461   cgiwrap_writef ("  Destination: <A HREF=\"");
   1462   cgiwrap_writevf (fmt, ap);
   1463   cgiwrap_writef ("\">");
   1464   cgiwrap_writevf (fmt, ap);
   1465   cgiwrap_writef ("</A><BR>\n<BR>\n");
   1466 #endif
   1467   cgiwrap_writef ("There is nothing to see here, please move along...");
   1468 
   1469 }
   1470 
   1471 void cgi_redirect (CGI *cgi, const char *fmt, ...)
   1472 {
   1473   va_list ap;
   1474 
   1475   va_start(ap, fmt);
   1476   cgi_vredirect (cgi, 0, fmt, ap);
   1477   va_end(ap);
   1478   return;
   1479 }
   1480 
   1481 void cgi_redirect_uri (CGI *cgi, const char *fmt, ...)
   1482 {
   1483   va_list ap;
   1484 
   1485   va_start(ap, fmt);
   1486   cgi_vredirect (cgi, 1, fmt, ap);
   1487   va_end(ap);
   1488   return;
   1489 }
   1490 
   1491 char *cgi_cookie_authority (CGI *cgi, const char *host)
   1492 {
   1493   HDF *obj;
   1494   char *domain;
   1495   int hlen = 0, dlen = 0;
   1496 
   1497   if (host == NULL)
   1498   {
   1499     host = hdf_get_value (cgi->hdf, "HTTP.Host", NULL);
   1500   }
   1501   if (host == NULL) return NULL;
   1502 
   1503   while (host[hlen] && host[hlen] != ':') hlen++;
   1504 
   1505   obj = hdf_get_obj (cgi->hdf, "CookieAuthority");
   1506   if (obj == NULL) return NULL;
   1507   for (obj = hdf_obj_child (obj);
   1508        obj;
   1509        obj = hdf_obj_next (obj))
   1510   {
   1511     domain = hdf_obj_value (obj);
   1512     dlen = strlen(domain);
   1513     if (hlen >= dlen)
   1514     {
   1515       if (!strncasecmp (host + hlen - dlen, domain, dlen))
   1516 	return domain;
   1517     }
   1518   }
   1519 
   1520   return NULL;
   1521 }
   1522 
   1523 /* For more information about Cookies, see:
   1524  * The original Netscape Cookie Spec:
   1525  * http://wp.netscape.com/newsref/std/cookie_spec.html
   1526  *
   1527  * HTTP State Management Mechanism
   1528  * http://www.ietf.org/rfc/rfc2109.txt
   1529  */
   1530 
   1531 NEOERR *cgi_cookie_set (CGI *cgi, const char *name, const char *value,
   1532                         const char *path, const char *domain,
   1533                         const char *time_str, int persistent, int secure)
   1534 {
   1535   NEOERR *err;
   1536   STRING str;
   1537   char my_time[256];
   1538 
   1539   if (path == NULL) path = "/";
   1540 
   1541   string_init(&str);
   1542   do {
   1543     err = string_appendf(&str, "Set-Cookie: %s=%s; path=%s", name, value, path);
   1544     if (err) break;
   1545 
   1546     if (persistent)
   1547     {
   1548       if (time_str == NULL)
   1549       {
   1550 	/* Expires in one year */
   1551 	time_t exp_date = time(NULL) + 31536000;
   1552 
   1553 	strftime (my_time, 48, "%A, %d-%b-%Y 23:59:59 GMT",
   1554 	    gmtime (&exp_date));
   1555 	time_str = my_time;
   1556       }
   1557       err = string_appendf(&str, "; expires=%s", time_str);
   1558       if (err) break;
   1559     }
   1560     if (domain)
   1561     {
   1562       err = string_appendf(&str, "; domain=%s", domain);
   1563       if (err) break;
   1564     }
   1565     if (secure)
   1566     {
   1567       err = string_append(&str, "; secure");
   1568       if (err) break;
   1569     }
   1570     err = string_append(&str, "\r\n");
   1571   } while (0);
   1572   if (err)
   1573   {
   1574     string_clear(&str);
   1575     return nerr_pass(err);
   1576   }
   1577   cgiwrap_write(str.buf, str.len);
   1578   string_clear(&str);
   1579   return STATUS_OK;
   1580 }
   1581 
   1582 /* This will actually issue up to three set cookies, attempting to clear
   1583  * the damn thing. */
   1584 NEOERR *cgi_cookie_clear (CGI *cgi, const char *name, const char *domain,
   1585                           const char *path)
   1586 {
   1587   if (path == NULL) path = "/";
   1588   if (domain)
   1589   {
   1590     if (domain[0] == '.')
   1591     {
   1592       cgiwrap_writef ("Set-Cookie: %s=; path=%s; domain=%s;"
   1593 	  "expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path,
   1594 	  domain + 1);
   1595     }
   1596     cgiwrap_writef("Set-Cookie: %s=; path=%s; domain=%s;"
   1597 	"expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path,
   1598 	domain);
   1599   }
   1600   cgiwrap_writef("Set-Cookie: %s=; path=%s; "
   1601       "expires=Thursday, 01-Jan-1970 00:00:00 GMT\r\n", name, path);
   1602 
   1603   return STATUS_OK;
   1604 }
   1605