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