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