• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
_add_cgi_env_var(CGI * cgi,char * env,char * name)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 
cgi_url_unescape(char * value)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 
cgi_js_escape(const char * in,char ** esc)267 NEOERR *cgi_js_escape (const char *in, char **esc)
268 {
269   return nerr_pass(neos_js_escape(in, esc));
270 }
271 
cgi_url_escape(const char * buf,char ** esc)272 NEOERR *cgi_url_escape (const char *buf, char **esc)
273 {
274   return nerr_pass(neos_url_escape(buf, esc, NULL));
275 }
276 
cgi_url_escape_more(const char * in,char ** esc,const char * other)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 
cgi_url_validate(const char * buf,char ** esc)283 NEOERR *cgi_url_validate (const char *buf, char **esc)
284 {
285   return nerr_pass(neos_url_validate(buf, esc));
286 }
287 
_parse_query(CGI * cgi,char * query)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? */
_parse_post_form(CGI * cgi)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 
_parse_cookie(CGI * cgi)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 
_launch_debugger(CGI * cgi,char * display)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 
cgi_pre_parse(CGI * cgi)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 
cgi_register_parse_cb(CGI * cgi,const char * method,const char * ctype,void * rock,CGI_PARSE_CB parse_cb)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 
cgi_parse(CGI * cgi)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 
cgi_init(CGI ** cgi,HDF * hdf)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 
_destroy_tmp_file(char * filename)787 static void _destroy_tmp_file(char *filename)
788 {
789   unlink(filename);
790   free(filename);
791 }
792 
cgi_destroy(CGI ** cgi)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 
render_cb(void * ctx,char * buf)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 
cgi_headers(CGI * cgi)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 
cgi_compress(STRING * str,char * obuf,int * olen)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 
cgi_html_ws_strip(STRING * str,int level)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 
cgi_output(CGI * cgi,STRING * str)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 
cgi_html_escape_strfunc(const char * str,char ** ret)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 
cgi_html_strip_strfunc(const char * str,char ** ret)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 
cgi_text_html_strfunc(const char * str,char ** ret)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 
cgi_register_strfuncs(CSPARSE * cs)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 
cgi_cs_init(CGI * cgi,CSPARSE ** cs)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 
cgi_display(CGI * cgi,const char * cs_file)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 
cgi_neo_error(CGI * cgi,NEOERR * err)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 
cgi_error(CGI * cgi,const char * fmt,...)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 
cgi_debug_init(int argc,char ** argv)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 
cgi_vredirect(CGI * cgi,int uri,const char * fmt,va_list ap)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 
cgi_redirect(CGI * cgi,const char * fmt,...)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 
cgi_redirect_uri(CGI * cgi,const char * fmt,...)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 
cgi_cookie_authority(CGI * cgi,const char * host)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 
cgi_cookie_set(CGI * cgi,const char * name,const char * value,const char * path,const char * domain,const char * time_str,int persistent,int secure)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. */
cgi_cookie_clear(CGI * cgi,const char * name,const char * domain,const char * path)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