1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "apr.h" 18#include "apr_lib.h" 19#include "apr_strings.h" 20 21#define APR_WANT_STRFUNC 22#include "apr_want.h" 23 24#if APR_HAVE_STDLIB_H 25#include <stdlib.h> 26#endif 27 28#include "ap_config.h" 29#include "httpd.h" 30#include "http_config.h" 31#include "http_main.h" 32#include "http_log.h" 33#include "http_core.h" 34#include "http_protocol.h" 35#include "http_request.h" /* for sub_req_lookup_uri() */ 36#include "util_script.h" 37#include "apr_date.h" /* For apr_date_parse_http() */ 38#include "util_ebcdic.h" 39 40#ifdef OS2 41#define INCL_DOS 42#include <os2.h> 43#endif 44 45/* 46 * Various utility functions which are common to a whole lot of 47 * script-type extensions mechanisms, and might as well be gathered 48 * in one place (if only to avoid creating inter-module dependancies 49 * where there don't have to be). 50 */ 51 52/* we know core's module_index is 0 */ 53#undef APLOG_MODULE_INDEX 54#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX 55 56static char *http2env(request_rec *r, const char *w) 57{ 58 char *res = (char *)apr_palloc(r->pool, sizeof("HTTP_") + strlen(w)); 59 char *cp = res; 60 char c; 61 62 *cp++ = 'H'; 63 *cp++ = 'T'; 64 *cp++ = 'T'; 65 *cp++ = 'P'; 66 *cp++ = '_'; 67 68 while ((c = *w++) != 0) { 69 if (apr_isalnum(c)) { 70 *cp++ = apr_toupper(c); 71 } 72 else if (c == '-') { 73 *cp++ = '_'; 74 } 75 else { 76 if (APLOGrtrace1(r)) 77 ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r, 78 "Not exporting header with invalid name as envvar: %s", 79 ap_escape_logitem(r->pool, w)); 80 return NULL; 81 } 82 } 83 *cp = 0; 84 85 return res; 86} 87 88static void add_unless_null(apr_table_t *table, const char *name, const char *val) 89{ 90 if (name && val) { 91 apr_table_addn(table, name, val); 92 } 93} 94 95static void env2env(apr_table_t *table, const char *name) 96{ 97 add_unless_null(table, name, getenv(name)); 98} 99 100AP_DECLARE(char **) ap_create_environment(apr_pool_t *p, apr_table_t *t) 101{ 102 const apr_array_header_t *env_arr = apr_table_elts(t); 103 const apr_table_entry_t *elts = (const apr_table_entry_t *) env_arr->elts; 104 char **env = (char **) apr_palloc(p, (env_arr->nelts + 2) * sizeof(char *)); 105 int i, j; 106 char *tz; 107 char *whack; 108 109 j = 0; 110 if (!apr_table_get(t, "TZ")) { 111 tz = getenv("TZ"); 112 if (tz != NULL) { 113 env[j++] = apr_pstrcat(p, "TZ=", tz, NULL); 114 } 115 } 116 for (i = 0; i < env_arr->nelts; ++i) { 117 if (!elts[i].key) { 118 continue; 119 } 120 env[j] = apr_pstrcat(p, elts[i].key, "=", elts[i].val, NULL); 121 whack = env[j]; 122 if (apr_isdigit(*whack)) { 123 *whack++ = '_'; 124 } 125 while (*whack != '=') { 126 if (!apr_isalnum(*whack)) { 127 *whack = '_'; 128 } 129 ++whack; 130 } 131 ++j; 132 } 133 134 env[j] = NULL; 135 return env; 136} 137 138AP_DECLARE(void) ap_add_common_vars(request_rec *r) 139{ 140 apr_table_t *e; 141 server_rec *s = r->server; 142 conn_rec *c = r->connection; 143 const char *env_temp; 144 const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); 145 const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts; 146 int i; 147 apr_port_t rport; 148 char *q; 149 150 /* use a temporary apr_table_t which we'll overlap onto 151 * r->subprocess_env later 152 * (exception: if r->subprocess_env is empty at the start, 153 * write directly into it) 154 */ 155 if (apr_is_empty_table(r->subprocess_env)) { 156 e = r->subprocess_env; 157 } 158 else { 159 e = apr_table_make(r->pool, 25 + hdrs_arr->nelts); 160 } 161 162 /* First, add environment vars from headers... this is as per 163 * CGI specs, though other sorts of scripting interfaces see 164 * the same vars... 165 */ 166 167 for (i = 0; i < hdrs_arr->nelts; ++i) { 168 if (!hdrs[i].key) { 169 continue; 170 } 171 172 /* A few headers are special cased --- Authorization to prevent 173 * rogue scripts from capturing passwords; content-type and -length 174 * for no particular reason. 175 */ 176 177 if (!strcasecmp(hdrs[i].key, "Content-type")) { 178 apr_table_addn(e, "CONTENT_TYPE", hdrs[i].val); 179 } 180 else if (!strcasecmp(hdrs[i].key, "Content-length")) { 181 apr_table_addn(e, "CONTENT_LENGTH", hdrs[i].val); 182 } 183 /* 184 * You really don't want to disable this check, since it leaves you 185 * wide open to CGIs stealing passwords and people viewing them 186 * in the environment with "ps -e". But, if you must... 187 */ 188#ifndef SECURITY_HOLE_PASS_AUTHORIZATION 189 else if (!strcasecmp(hdrs[i].key, "Authorization") 190 || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) { 191 continue; 192 } 193#endif 194 else 195 add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val); 196 } 197 198 env_temp = apr_table_get(r->subprocess_env, "PATH"); 199 if (env_temp == NULL) { 200 env_temp = getenv("PATH"); 201 } 202 if (env_temp == NULL) { 203 env_temp = DEFAULT_PATH; 204 } 205 apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_temp)); 206 207#if defined(WIN32) 208 env2env(e, "SystemRoot"); 209 env2env(e, "COMSPEC"); 210 env2env(e, "PATHEXT"); 211 env2env(e, "WINDIR"); 212#elif defined(OS2) 213 env2env(e, "COMSPEC"); 214 env2env(e, "ETC"); 215 env2env(e, "DPATH"); 216 env2env(e, "PERLLIB_PREFIX"); 217#elif defined(BEOS) 218 env2env(e, "LIBRARY_PATH"); 219#elif defined(DARWIN) 220 env2env(e, "DYLD_LIBRARY_PATH"); 221#elif defined(_AIX) 222 env2env(e, "LIBPATH"); 223#elif defined(__HPUX__) 224 /* HPUX PARISC 2.0W knows both, otherwise redundancy is harmless */ 225 env2env(e, "SHLIB_PATH"); 226 env2env(e, "LD_LIBRARY_PATH"); 227#else /* Some Unix */ 228 env2env(e, "LD_LIBRARY_PATH"); 229#endif 230 231 apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); 232 apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_banner()); 233 apr_table_addn(e, "SERVER_NAME", 234 ap_escape_html(r->pool, ap_get_server_name_for_url(r))); 235 apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ 236 apr_table_addn(e, "SERVER_PORT", 237 apr_psprintf(r->pool, "%u", ap_get_server_port(r))); 238 add_unless_null(e, "REMOTE_HOST", 239 ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL)); 240 apr_table_addn(e, "REMOTE_ADDR", r->useragent_ip); 241 apr_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ 242 apr_table_setn(e, "REQUEST_SCHEME", ap_http_scheme(r)); 243 apr_table_addn(e, "CONTEXT_PREFIX", ap_context_prefix(r)); 244 apr_table_addn(e, "CONTEXT_DOCUMENT_ROOT", ap_context_document_root(r)); 245 apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ 246 if (apr_table_get(r->notes, "proxy-noquery") && (q = ap_strchr(r->filename, '?'))) { 247 *q = '\0'; 248 apr_table_addn(e, "SCRIPT_FILENAME", apr_pstrdup(r->pool, r->filename)); 249 *q = '?'; 250 } 251 else { 252 apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ 253 } 254 255 rport = c->client_addr->port; 256 apr_table_addn(e, "REMOTE_PORT", apr_itoa(r->pool, rport)); 257 258 if (r->user) { 259 apr_table_addn(e, "REMOTE_USER", r->user); 260 } 261 else if (r->prev) { 262 request_rec *back = r->prev; 263 264 while (back) { 265 if (back->user) { 266 apr_table_addn(e, "REDIRECT_REMOTE_USER", back->user); 267 break; 268 } 269 back = back->prev; 270 } 271 } 272 add_unless_null(e, "AUTH_TYPE", r->ap_auth_type); 273 env_temp = ap_get_remote_logname(r); 274 if (env_temp) { 275 apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, env_temp)); 276 } 277 278 /* Apache custom error responses. If we have redirected set two new vars */ 279 280 if (r->prev) { 281 add_unless_null(e, "REDIRECT_QUERY_STRING", r->prev->args); 282 add_unless_null(e, "REDIRECT_URL", r->prev->uri); 283 } 284 285 if (e != r->subprocess_env) { 286 apr_table_overlap(r->subprocess_env, e, APR_OVERLAP_TABLES_SET); 287 } 288} 289 290/* This "cute" little function comes about because the path info on 291 * filenames and URLs aren't always the same. So we take the two, 292 * and find as much of the two that match as possible. 293 */ 294 295AP_DECLARE(int) ap_find_path_info(const char *uri, const char *path_info) 296{ 297 int lu = strlen(uri); 298 int lp = strlen(path_info); 299 300 while (lu-- && lp-- && uri[lu] == path_info[lp]) { 301 if (path_info[lp] == '/') { 302 while (lu && uri[lu-1] == '/') lu--; 303 } 304 } 305 306 if (lu == -1) { 307 lu = 0; 308 } 309 310 while (uri[lu] != '\0' && uri[lu] != '/') { 311 lu++; 312 } 313 return lu; 314} 315 316/* Obtain the Request-URI from the original request-line, returning 317 * a new string from the request pool containing the URI or "". 318 */ 319static char *original_uri(request_rec *r) 320{ 321 char *first, *last; 322 323 if (r->the_request == NULL) { 324 return (char *) apr_pcalloc(r->pool, 1); 325 } 326 327 first = r->the_request; /* use the request-line */ 328 329 while (*first && !apr_isspace(*first)) { 330 ++first; /* skip over the method */ 331 } 332 while (apr_isspace(*first)) { 333 ++first; /* and the space(s) */ 334 } 335 336 last = first; 337 while (*last && !apr_isspace(*last)) { 338 ++last; /* end at next whitespace */ 339 } 340 341 return apr_pstrmemdup(r->pool, first, last - first); 342} 343 344AP_DECLARE(void) ap_add_cgi_vars(request_rec *r) 345{ 346 apr_table_t *e = r->subprocess_env; 347 348 apr_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); 349 apr_table_setn(e, "SERVER_PROTOCOL", r->protocol); 350 apr_table_setn(e, "REQUEST_METHOD", r->method); 351 apr_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); 352 apr_table_setn(e, "REQUEST_URI", original_uri(r)); 353 354 /* Note that the code below special-cases scripts run from includes, 355 * because it "knows" that the sub_request has been hacked to have the 356 * args and path_info of the original request, and not any that may have 357 * come with the script URI in the include command. Ugh. 358 */ 359 360 if (!strcmp(r->protocol, "INCLUDED")) { 361 apr_table_setn(e, "SCRIPT_NAME", r->uri); 362 if (r->path_info && *r->path_info) { 363 apr_table_setn(e, "PATH_INFO", r->path_info); 364 } 365 } 366 else if (!r->path_info || !*r->path_info) { 367 apr_table_setn(e, "SCRIPT_NAME", r->uri); 368 } 369 else { 370 int path_info_start = ap_find_path_info(r->uri, r->path_info); 371 372 apr_table_setn(e, "SCRIPT_NAME", 373 apr_pstrndup(r->pool, r->uri, path_info_start)); 374 375 apr_table_setn(e, "PATH_INFO", r->path_info); 376 } 377 378 if (r->path_info && r->path_info[0]) { 379 /* 380 * To get PATH_TRANSLATED, treat PATH_INFO as a URI path. 381 * Need to re-escape it for this, since the entire URI was 382 * un-escaped before we determined where the PATH_INFO began. 383 */ 384 request_rec *pa_req; 385 386 pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, 387 NULL); 388 389 if (pa_req->filename) { 390 char *pt = apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, 391 NULL); 392#ifdef WIN32 393 /* We need to make this a real Windows path name */ 394 apr_filepath_merge(&pt, "", pt, APR_FILEPATH_NATIVE, r->pool); 395#endif 396 apr_table_setn(e, "PATH_TRANSLATED", pt); 397 } 398 ap_destroy_sub_req(pa_req); 399 } 400} 401 402 403static int set_cookie_doo_doo(void *v, const char *key, const char *val) 404{ 405 apr_table_addn(v, key, val); 406 return 1; 407} 408 409#define HTTP_UNSET (-HTTP_OK) 410#define SCRIPT_LOG_MARK __FILE__,__LINE__,module_index 411 412AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer, 413 int (*getsfunc) (char *, int, void *), 414 void *getsfunc_data, 415 int module_index) 416{ 417 char x[MAX_STRING_LEN]; 418 char *w, *l; 419 int p; 420 int cgi_status = HTTP_UNSET; 421 apr_table_t *merge; 422 apr_table_t *cookie_table; 423 int trace_log = APLOG_R_MODULE_IS_LEVEL(r, module_index, APLOG_TRACE1); 424 int first_header = 1; 425 426 if (buffer) { 427 *buffer = '\0'; 428 } 429 w = buffer ? buffer : x; 430 431 /* temporary place to hold headers to merge in later */ 432 merge = apr_table_make(r->pool, 10); 433 434 /* The HTTP specification says that it is legal to merge duplicate 435 * headers into one. Some browsers that support Cookies don't like 436 * merged headers and prefer that each Set-Cookie header is sent 437 * separately. Lets humour those browsers by not merging. 438 * Oh what a pain it is. 439 */ 440 cookie_table = apr_table_make(r->pool, 2); 441 apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); 442 443 while (1) { 444 445 int rv = (*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data); 446 if (rv == 0) { 447 const char *msg = "Premature end of script headers"; 448 if (first_header) 449 msg = "End of script output before headers"; 450 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, 451 "%s: %s", msg, 452 apr_filepath_name_get(r->filename)); 453 return HTTP_INTERNAL_SERVER_ERROR; 454 } 455 else if (rv == -1) { 456 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, 457 "Script timed out before returning headers: %s", 458 apr_filepath_name_get(r->filename)); 459 return HTTP_GATEWAY_TIME_OUT; 460 } 461 462 /* Delete terminal (CR?)LF */ 463 464 p = strlen(w); 465 /* Indeed, the host's '\n': 466 '\012' for UNIX; '\015' for MacOS; '\025' for OS/390 467 -- whatever the script generates. 468 */ 469 if (p > 0 && w[p - 1] == '\n') { 470 if (p > 1 && w[p - 2] == CR) { 471 w[p - 2] = '\0'; 472 } 473 else { 474 w[p - 1] = '\0'; 475 } 476 } 477 478 /* 479 * If we've finished reading the headers, check to make sure any 480 * HTTP/1.1 conditions are met. If so, we're done; normal processing 481 * will handle the script's output. If not, just return the error. 482 * The appropriate thing to do would be to send the script process a 483 * SIGPIPE to let it know we're ignoring it, close the channel to the 484 * script process, and *then* return the failed-to-meet-condition 485 * error. Otherwise we'd be waiting for the script to finish 486 * blithering before telling the client the output was no good. 487 * However, we don't have the information to do that, so we have to 488 * leave it to an upper layer. 489 */ 490 if (w[0] == '\0') { 491 int cond_status = OK; 492 493 /* PR#38070: This fails because it gets confused when a 494 * CGI Status header overrides ap_meets_conditions. 495 * 496 * We can fix that by dropping ap_meets_conditions when 497 * Status has been set. Since this is the only place 498 * cgi_status gets used, let's test it explicitly. 499 * 500 * The alternative would be to ignore CGI Status when 501 * ap_meets_conditions returns anything interesting. 502 * That would be safer wrt HTTP, but would break CGI. 503 */ 504 if ((cgi_status == HTTP_UNSET) && (r->method_number == M_GET)) { 505 cond_status = ap_meets_conditions(r); 506 } 507 apr_table_overlap(r->err_headers_out, merge, 508 APR_OVERLAP_TABLES_MERGE); 509 if (!apr_is_empty_table(cookie_table)) { 510 /* the cookies have already been copied to the cookie_table */ 511 apr_table_unset(r->err_headers_out, "Set-Cookie"); 512 r->err_headers_out = apr_table_overlay(r->pool, 513 r->err_headers_out, cookie_table); 514 } 515 return cond_status; 516 } 517 518 if (trace_log) { 519 if (first_header) 520 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, 521 "Headers from script '%s':", 522 apr_filepath_name_get(r->filename)); 523 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE4, 0, r, " %s", w); 524 } 525 526 /* if we see a bogus header don't ignore it. Shout and scream */ 527 528#if APR_CHARSET_EBCDIC 529 /* Chances are that we received an ASCII header text instead of 530 * the expected EBCDIC header lines. Try to auto-detect: 531 */ 532 if (!(l = strchr(w, ':'))) { 533 int maybeASCII = 0, maybeEBCDIC = 0; 534 unsigned char *cp, native; 535 apr_size_t inbytes_left, outbytes_left; 536 537 for (cp = w; *cp != '\0'; ++cp) { 538 native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp); 539 if (apr_isprint(*cp) && !apr_isprint(native)) 540 ++maybeEBCDIC; 541 if (!apr_isprint(*cp) && apr_isprint(native)) 542 ++maybeASCII; 543 } 544 if (maybeASCII > maybeEBCDIC) { 545 ap_log_error(SCRIPT_LOG_MARK, APLOG_ERR, 0, r->server, 546 "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", 547 r->filename); 548 inbytes_left = outbytes_left = cp - w; 549 apr_xlate_conv_buffer(ap_hdrs_from_ascii, 550 w, &inbytes_left, w, &outbytes_left); 551 } 552 } 553#endif /*APR_CHARSET_EBCDIC*/ 554 if (!(l = strchr(w, ':'))) { 555 if (!buffer) { 556 /* Soak up all the script output - may save an outright kill */ 557 while ((*getsfunc)(w, MAX_STRING_LEN - 1, getsfunc_data) > 0) { 558 continue; 559 } 560 } 561 562 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, 563 "malformed header from script '%s': Bad header: %.30s", 564 apr_filepath_name_get(r->filename), w); 565 return HTTP_INTERNAL_SERVER_ERROR; 566 } 567 568 *l++ = '\0'; 569 while (apr_isspace(*l)) { 570 ++l; 571 } 572 573 if (!strcasecmp(w, "Content-type")) { 574 char *tmp; 575 576 /* Nuke trailing whitespace */ 577 578 char *endp = l + strlen(l) - 1; 579 while (endp > l && apr_isspace(*endp)) { 580 *endp-- = '\0'; 581 } 582 583 tmp = apr_pstrdup(r->pool, l); 584 ap_content_type_tolower(tmp); 585 ap_set_content_type(r, tmp); 586 } 587 /* 588 * If the script returned a specific status, that's what 589 * we'll use - otherwise we assume 200 OK. 590 */ 591 else if (!strcasecmp(w, "Status")) { 592 r->status = cgi_status = atoi(l); 593 if (!ap_is_HTTP_VALID_RESPONSE(cgi_status)) 594 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, 595 "Invalid status line from script '%s': %.30s", 596 apr_filepath_name_get(r->filename), l); 597 else 598 if (APLOGrtrace1(r)) 599 ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r, 600 "Status line from script '%s': %.30s", 601 apr_filepath_name_get(r->filename), l); 602 r->status_line = apr_pstrdup(r->pool, l); 603 } 604 else if (!strcasecmp(w, "Location")) { 605 apr_table_set(r->headers_out, w, l); 606 } 607 else if (!strcasecmp(w, "Content-Length")) { 608 apr_table_set(r->headers_out, w, l); 609 } 610 else if (!strcasecmp(w, "Content-Range")) { 611 apr_table_set(r->headers_out, w, l); 612 } 613 else if (!strcasecmp(w, "Transfer-Encoding")) { 614 apr_table_set(r->headers_out, w, l); 615 } 616 else if (!strcasecmp(w, "ETag")) { 617 apr_table_set(r->headers_out, w, l); 618 } 619 /* 620 * If the script gave us a Last-Modified header, we can't just 621 * pass it on blindly because of restrictions on future values. 622 */ 623 else if (!strcasecmp(w, "Last-Modified")) { 624 ap_update_mtime(r, apr_date_parse_http(l)); 625 ap_set_last_modified(r); 626 } 627 else if (!strcasecmp(w, "Set-Cookie")) { 628 apr_table_add(cookie_table, w, l); 629 } 630 else { 631 apr_table_add(merge, w, l); 632 } 633 first_header = 0; 634 } 635 /* never reached - we leave this function within the while loop above */ 636 return OK; 637} 638 639AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, 640 int (*getsfunc) (char *, int, void *), 641 void *getsfunc_data) 642{ 643 return ap_scan_script_header_err_core_ex(r, buffer, getsfunc, 644 getsfunc_data, 645 APLOG_MODULE_INDEX); 646} 647 648static int getsfunc_FILE(char *buf, int len, void *f) 649{ 650 return apr_file_gets(buf, len, (apr_file_t *) f) == APR_SUCCESS; 651} 652 653AP_DECLARE(int) ap_scan_script_header_err(request_rec *r, apr_file_t *f, 654 char *buffer) 655{ 656 return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f, 657 APLOG_MODULE_INDEX); 658} 659 660AP_DECLARE(int) ap_scan_script_header_err_ex(request_rec *r, apr_file_t *f, 661 char *buffer, int module_index) 662{ 663 return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_FILE, f, 664 module_index); 665} 666 667 668static int getsfunc_BRIGADE(char *buf, int len, void *arg) 669{ 670 apr_bucket_brigade *bb = (apr_bucket_brigade *)arg; 671 const char *dst_end = buf + len - 1; /* leave room for terminating null */ 672 char *dst = buf; 673 apr_bucket *e = APR_BRIGADE_FIRST(bb); 674 apr_status_t rv; 675 int done = 0; 676 677 while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb) 678 && !APR_BUCKET_IS_EOS(e)) { 679 const char *bucket_data; 680 apr_size_t bucket_data_len; 681 const char *src; 682 const char *src_end; 683 apr_bucket * next; 684 685 rv = apr_bucket_read(e, &bucket_data, &bucket_data_len, 686 APR_BLOCK_READ); 687 if (rv != APR_SUCCESS || (bucket_data_len == 0)) { 688 *dst = '\0'; 689 return APR_STATUS_IS_TIMEUP(rv) ? -1 : 0; 690 } 691 src = bucket_data; 692 src_end = bucket_data + bucket_data_len; 693 while ((src < src_end) && (dst < dst_end) && !done) { 694 if (*src == '\n') { 695 done = 1; 696 } 697 else if (*src != '\r') { 698 *dst++ = *src; 699 } 700 src++; 701 } 702 703 if (src < src_end) { 704 apr_bucket_split(e, src - bucket_data); 705 } 706 next = APR_BUCKET_NEXT(e); 707 APR_BUCKET_REMOVE(e); 708 apr_bucket_destroy(e); 709 e = next; 710 } 711 *dst = 0; 712 return done; 713} 714 715AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r, 716 apr_bucket_brigade *bb, 717 char *buffer) 718{ 719 return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb, 720 APLOG_MODULE_INDEX); 721} 722 723AP_DECLARE(int) ap_scan_script_header_err_brigade_ex(request_rec *r, 724 apr_bucket_brigade *bb, 725 char *buffer, 726 int module_index) 727{ 728 return ap_scan_script_header_err_core_ex(r, buffer, getsfunc_BRIGADE, bb, 729 module_index); 730} 731 732 733struct vastrs { 734 va_list args; 735 int arg; 736 const char *curpos; 737}; 738 739static int getsfunc_STRING(char *w, int len, void *pvastrs) 740{ 741 struct vastrs *strs = (struct vastrs*) pvastrs; 742 const char *p; 743 int t; 744 745 if (!strs->curpos || !*strs->curpos) { 746 w[0] = '\0'; 747 return 0; 748 } 749 p = ap_strchr_c(strs->curpos, '\n'); 750 if (p) 751 ++p; 752 else 753 p = ap_strchr_c(strs->curpos, '\0'); 754 t = p - strs->curpos; 755 if (t > len) 756 t = len; 757 strncpy (w, strs->curpos, t); 758 w[t] = '\0'; 759 if (!strs->curpos[t]) { 760 ++strs->arg; 761 strs->curpos = va_arg(strs->args, const char *); 762 } 763 else 764 strs->curpos += t; 765 return t; 766} 767 768/* ap_scan_script_header_err_strs() accepts additional const char* args... 769 * each is treated as one or more header lines, and the first non-header 770 * character is returned to **arg, **data. (The first optional arg is 771 * counted as 0.) 772 */ 773AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs_ex(request_rec *r, 774 char *buffer, 775 int module_index, 776 const char **termch, 777 int *termarg, ...) 778{ 779 struct vastrs strs; 780 int res; 781 782 va_start(strs.args, termarg); 783 strs.arg = 0; 784 strs.curpos = va_arg(strs.args, char*); 785 res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING, 786 (void *) &strs, module_index); 787 if (termch) 788 *termch = strs.curpos; 789 if (termarg) 790 *termarg = strs.arg; 791 va_end(strs.args); 792 return res; 793} 794 795AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, 796 char *buffer, 797 const char **termch, 798 int *termarg, ...) 799{ 800 struct vastrs strs; 801 int res; 802 803 va_start(strs.args, termarg); 804 strs.arg = 0; 805 strs.curpos = va_arg(strs.args, char*); 806 res = ap_scan_script_header_err_core_ex(r, buffer, getsfunc_STRING, 807 (void *) &strs, APLOG_MODULE_INDEX); 808 if (termch) 809 *termch = strs.curpos; 810 if (termarg) 811 *termarg = strs.arg; 812 va_end(strs.args); 813 return res; 814} 815 816static void 817argstr_to_table(char *str, apr_table_t *parms) 818{ 819 char *key; 820 char *value; 821 char *strtok_state; 822 823 if (str == NULL) { 824 return; 825 } 826 827 key = apr_strtok(str, "&", &strtok_state); 828 while (key) { 829 value = strchr(key, '='); 830 if (value) { 831 *value = '\0'; /* Split the string in two */ 832 value++; /* Skip passed the = */ 833 } 834 else { 835 value = "1"; 836 } 837 ap_unescape_url(key); 838 ap_unescape_url(value); 839 apr_table_set(parms, key, value); 840 key = apr_strtok(NULL, "&", &strtok_state); 841 } 842} 843 844AP_DECLARE(void) ap_args_to_table(request_rec *r, apr_table_t **table) 845{ 846 apr_table_t *t = apr_table_make(r->pool, 10); 847 argstr_to_table(apr_pstrdup(r->pool, r->args), t); 848 *table = t; 849} 850