X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=server%2Futil_script.c;h=1c05fedd7b132e1a732d392ceaee8a1c151315e6;hb=aa21671e13767135f0ee3f88d6a3ff6d039e6534;hp=ccc27b15689181ec93239329b1a35ec0c6752a72;hpb=dd1f752b82eec0c6f712ac919cc3d38d81e4d41c;p=apache diff --git a/server/util_script.c b/server/util_script.c index ccc27b1568..1c05fedd7b 100644 --- a/server/util_script.c +++ b/server/util_script.c @@ -1,61 +1,74 @@ /* ==================================================================== - * Copyright (c) 1995-1999 The Apache Group. All rights reserved. + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2000-2003 The Apache Software Foundation. All rights + * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * - * 3. All advertising materials mentioning features or use of this - * software must display the following acknowledgment: - * "This product includes software developed by the Apache Group - * for use in the Apache HTTP server project (http://www.apache.org/)." - * - * 4. The names "Apache Server" and "Apache Group" must not be used to - * endorse or promote products derived from this software without - * prior written permission. For written permission, please contact - * apache@apache.org. + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. + * 4. The names "Apache" and "Apache Software Foundation" must + * not be used to endorse or promote products derived from this + * software without prior written permission. For written + * permission, please contact apache@apache.org. * - * 6. Redistributions of any form whatsoever must retain the following - * acknowledgment: - * "This product includes software developed by the Apache Group - * for use in the Apache HTTP server project (http://www.apache.org/)." + * 5. Products derived from this software may not be called "Apache", + * nor may "Apache" appear in their name, without prior written + * permission of the Apache Software Foundation. * - * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY - * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - * OF THE POSSIBILITY OF SUCH DAMAGE. + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Group and was originally based - * on public domain software written at the National Center for - * Supercomputing Applications, University of Illinois, Urbana-Champaign. - * For more information on the Apache Group and the Apache HTTP server - * project, please see . + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . * + * Portions of this software are based upon public domain software + * originally written at the National Center for Supercomputing Applications, + * University of Illinois, Urbana-Champaign. */ +#include "apr.h" +#include "apr_lib.h" +#include "apr_strings.h" + +#define APR_WANT_STRFUNC +#include "apr_want.h" + +#if APR_HAVE_STDLIB_H +#include +#endif + #define CORE_PRIVATE +#include "ap_config.h" #include "httpd.h" #include "http_config.h" #include "http_main.h" @@ -64,7 +77,8 @@ #include "http_protocol.h" #include "http_request.h" /* for sub_req_lookup_uri() */ #include "util_script.h" -#include "util_date.h" /* For parseHTTPdate() */ +#include "apr_date.h" /* For apr_date_parse_http() */ +#include "util_ebcdic.h" #ifdef OS2 #define INCL_DOS @@ -81,106 +95,58 @@ #define MALFORMED_MESSAGE "malformed header from script. Bad header=" #define MALFORMED_HEADER_LENGTH_TO_SHOW 30 -/* If a request includes query info in the URL (stuff after "?"), and - * the query info does not contain "=" (indicative of a FORM submission), - * then this routine is called to create the argument list to be passed - * to the CGI script. When suexec is enabled, the suexec path, user, and - * group are the first three arguments to be passed; if not, all three - * must be NULL. The query info is split into separate arguments, where - * "+" is the separator between keyword arguments. - * - * XXXX: note that the WIN32 code uses one of the suexec strings - * to pass an interpreter name. Remember this if changing the way they - * are handled in create_argv. - * - */ -static char **create_argv(ap_context_t *p, char *path, char *user, char *group, - char *av0, const char *args) -{ - int x, numwords; - char **av; - char *w; - int idx = 0; - - /* count the number of keywords */ - - for (x = 0, numwords = 1; args[x]; x++) { - if (args[x] == '+') { - ++numwords; - } - } - - if (numwords > APACHE_ARG_MAX - 5) { - numwords = APACHE_ARG_MAX - 5; /* Truncate args to prevent overrun */ - } - av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *)); - - if (path) { - av[idx++] = path; - } - if (user) { - av[idx++] = user; - } - if (group) { - av[idx++] = group; - } - - av[idx++] = av0; - - for (x = 1; x <= numwords; x++) { - w = ap_getword_nulls(p, &args, '+'); - ap_unescape_url(w); - av[idx++] = ap_escape_shell_cmd(p, w); - } - av[idx] = NULL; - return av; -} - - -static char *http2env(ap_context_t *a, char *w) +static char *http2env(apr_pool_t *a, const char *w) { - char *res = ap_pstrcat(a, "HTTP_", w, NULL); + char *res = (char *)apr_palloc(a, sizeof("HTTP_") + strlen(w)); char *cp = res; + char c; - while (*++cp) { - if (!ap_isalnum(*cp) && *cp != '_') { - *cp = '_'; - } - else { - *cp = ap_toupper(*cp); - } - } + *cp++ = 'H'; + *cp++ = 'T'; + *cp++ = 'T'; + *cp++ = 'P'; + *cp++ = '_'; + while ((c = *w++) != 0) { + if (!apr_isalnum(c)) { + *cp++ = '_'; + } + else { + *cp++ = apr_toupper(c); + } + } + *cp = 0; + return res; } -API_EXPORT(char **) ap_create_environment(ap_context_t *p, ap_table_t *t) +AP_DECLARE(char **) ap_create_environment(apr_pool_t *p, apr_table_t *t) { - ap_array_header_t *env_arr = ap_table_elts(t); - ap_table_entry_t *elts = (ap_table_entry_t *) env_arr->elts; - char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *)); + const apr_array_header_t *env_arr = apr_table_elts(t); + const apr_table_entry_t *elts = (const apr_table_entry_t *) env_arr->elts; + char **env = (char **) apr_palloc(p, (env_arr->nelts + 2) * sizeof(char *)); int i, j; char *tz; char *whack; j = 0; - if (!ap_table_get(t, "TZ")) { + if (!apr_table_get(t, "TZ")) { tz = getenv("TZ"); if (tz != NULL) { - env[j++] = ap_pstrcat(p, "TZ=", tz, NULL); + env[j++] = apr_pstrcat(p, "TZ=", tz, NULL); } } for (i = 0; i < env_arr->nelts; ++i) { if (!elts[i].key) { continue; } - env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL); + env[j] = apr_pstrcat(p, elts[i].key, "=", elts[i].val, NULL); whack = env[j]; - if (ap_isdigit(*whack)) { + if (apr_isdigit(*whack)) { *whack++ = '_'; } while (*whack != '=') { - if (!ap_isalnum(*whack) && *whack != '_') { + if (!apr_isalnum(*whack) && *whack != '_') { *whack = '_'; } ++whack; @@ -192,25 +158,33 @@ API_EXPORT(char **) ap_create_environment(ap_context_t *p, ap_table_t *t) return env; } -API_EXPORT(void) ap_add_common_vars(request_rec *r) +AP_DECLARE(void) ap_add_common_vars(request_rec *r) { - ap_table_t *e; + apr_table_t *e; server_rec *s = r->server; conn_rec *c = r->connection; const char *rem_logname; char *env_path; -#ifdef WIN32 +#if defined(WIN32) || defined(OS2) || defined(BEOS) char *env_temp; #endif const char *host; - ap_array_header_t *hdrs_arr = ap_table_elts(r->headers_in); - ap_table_entry_t *hdrs = (ap_table_entry_t *) hdrs_arr->elts; + const apr_array_header_t *hdrs_arr = apr_table_elts(r->headers_in); + const apr_table_entry_t *hdrs = (const apr_table_entry_t *) hdrs_arr->elts; int i; + apr_port_t rport; - /* use a temporary ap_table_t which we'll overlap onto + /* use a temporary apr_table_t which we'll overlap onto * r->subprocess_env later + * (exception: if r->subprocess_env is empty at the start, + * write directly into it) */ - e = ap_make_table(r->pool, 25 + hdrs_arr->nelts); + if (apr_is_empty_table(r->subprocess_env)) { + e = r->subprocess_env; + } + else { + e = apr_table_make(r->pool, 25 + hdrs_arr->nelts); + } /* First, add environment vars from headers... this is as per * CGI specs, though other sorts of scripting interfaces see @@ -228,10 +202,10 @@ API_EXPORT(void) ap_add_common_vars(request_rec *r) */ if (!strcasecmp(hdrs[i].key, "Content-type")) { - ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val); + apr_table_addn(e, "CONTENT_TYPE", hdrs[i].val); } else if (!strcasecmp(hdrs[i].key, "Content-length")) { - ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val); + apr_table_addn(e, "CONTENT_LENGTH", hdrs[i].val); } /* * You really don't want to disable this check, since it leaves you @@ -245,68 +219,106 @@ API_EXPORT(void) ap_add_common_vars(request_rec *r) } #endif else { - ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val); + apr_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val); } } if (!(env_path = getenv("PATH"))) { env_path = DEFAULT_PATH; } + apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_path)); #ifdef WIN32 if (env_temp = getenv("SystemRoot")) { - ap_table_addn(e, "SystemRoot", env_temp); + apr_table_addn(e, "SystemRoot", env_temp); } if (env_temp = getenv("COMSPEC")) { - ap_table_addn(e, "COMSPEC", env_temp); + apr_table_addn(e, "COMSPEC", env_temp); + } + if (env_temp = getenv("PATHEXT")) { + apr_table_addn(e, "PATHEXT", env_temp); } if (env_temp = getenv("WINDIR")) { - ap_table_addn(e, "WINDIR", env_temp); + apr_table_addn(e, "WINDIR", env_temp); + } +#endif + +#ifdef OS2 + if ((env_temp = getenv("COMSPEC")) != NULL) { + apr_table_addn(e, "COMSPEC", env_temp); + } + if ((env_temp = getenv("ETC")) != NULL) { + apr_table_addn(e, "ETC", env_temp); + } + if ((env_temp = getenv("DPATH")) != NULL) { + apr_table_addn(e, "DPATH", env_temp); + } + if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) { + apr_table_addn(e, "PERLLIB_PREFIX", env_temp); } #endif - ap_table_addn(e, "PATH", env_path); - ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); - ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); - ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r)); - ap_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ - ap_table_addn(e, "SERVER_PORT", - ap_psprintf(r->pool, "%u", ap_get_server_port(r))); - host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST); +#ifdef BEOS + if ((env_temp = getenv("LIBRARY_PATH")) != NULL) { + apr_table_addn(e, "LIBRARY_PATH", env_temp); + } +#endif + + apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r)); + apr_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version()); + apr_table_addn(e, "SERVER_NAME", + ap_escape_html(r->pool, ap_get_server_name(r))); + apr_table_addn(e, "SERVER_ADDR", r->connection->local_ip); /* Apache */ + apr_table_addn(e, "SERVER_PORT", + apr_psprintf(r->pool, "%u", ap_get_server_port(r))); + host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST, NULL); if (host) { - ap_table_addn(e, "REMOTE_HOST", host); + apr_table_addn(e, "REMOTE_HOST", host); } - ap_table_addn(e, "REMOTE_ADDR", c->remote_ip); - ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ - ap_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ - ap_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ + apr_table_addn(e, "REMOTE_ADDR", c->remote_ip); + apr_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r)); /* Apache */ + apr_table_addn(e, "SERVER_ADMIN", s->server_admin); /* Apache */ + apr_table_addn(e, "SCRIPT_FILENAME", r->filename); /* Apache */ - ap_table_addn(e, "REMOTE_PORT", - ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port))); + apr_sockaddr_port_get(&rport, c->remote_addr); + apr_table_addn(e, "REMOTE_PORT", apr_itoa(r->pool, rport)); if (r->user) { - ap_table_addn(e, "REMOTE_USER", r->user); + apr_table_addn(e, "REMOTE_USER", r->user); + } + else if (r->prev) { + request_rec *back = r->prev; + + while (back) { + if (back->user) { + apr_table_addn(e, "REDIRECT_REMOTE_USER", back->user); + break; + } + back = back->prev; + } } if (r->ap_auth_type) { - ap_table_addn(e, "AUTH_TYPE", r->ap_auth_type); + apr_table_addn(e, "AUTH_TYPE", r->ap_auth_type); } rem_logname = ap_get_remote_logname(r); if (rem_logname) { - ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname)); + apr_table_addn(e, "REMOTE_IDENT", apr_pstrdup(r->pool, rem_logname)); } /* Apache custom error responses. If we have redirected set two new vars */ if (r->prev) { if (r->prev->args) { - ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args); + apr_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args); } if (r->prev->uri) { - ap_table_addn(e, "REDIRECT_URL", r->prev->uri); + apr_table_addn(e, "REDIRECT_URL", r->prev->uri); } } - ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET); + if (e != r->subprocess_env) { + apr_table_overlap(r->subprocess_env, e, APR_OVERLAP_TABLES_SET); + } } /* This "cute" little function comes about because the path info on @@ -314,7 +326,7 @@ API_EXPORT(void) ap_add_common_vars(request_rec *r) * and find as much of the two that match as possible. */ -API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info) +AP_DECLARE(int) ap_find_path_info(const char *uri, const char *path_info) { int lu = strlen(uri); int lp = strlen(path_info); @@ -339,35 +351,35 @@ static char *original_uri(request_rec *r) char *first, *last; if (r->the_request == NULL) { - return (char *) ap_pcalloc(r->pool, 1); + return (char *) apr_pcalloc(r->pool, 1); } first = r->the_request; /* use the request-line */ - while (*first && !ap_isspace(*first)) { + while (*first && !apr_isspace(*first)) { ++first; /* skip over the method */ } - while (ap_isspace(*first)) { + while (apr_isspace(*first)) { ++first; /* and the space(s) */ } last = first; - while (*last && !ap_isspace(*last)) { + while (*last && !apr_isspace(*last)) { ++last; /* end at next whitespace */ } - return ap_pstrndup(r->pool, first, last - first); + return apr_pstrmemdup(r->pool, first, last - first); } -API_EXPORT(void) ap_add_cgi_vars(request_rec *r) +AP_DECLARE(void) ap_add_cgi_vars(request_rec *r) { - ap_table_t *e = r->subprocess_env; + apr_table_t *e = r->subprocess_env; - ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); - ap_table_setn(e, "SERVER_PROTOCOL", r->protocol); - ap_table_setn(e, "REQUEST_METHOD", r->method); - ap_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); - ap_table_setn(e, "REQUEST_URI", original_uri(r)); + apr_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1"); + apr_table_setn(e, "SERVER_PROTOCOL", r->protocol); + apr_table_setn(e, "REQUEST_METHOD", r->method); + apr_table_setn(e, "QUERY_STRING", r->args ? r->args : ""); + apr_table_setn(e, "REQUEST_URI", original_uri(r)); /* Note that the code below special-cases scripts run from includes, * because it "knows" that the sub_request has been hacked to have the @@ -376,21 +388,21 @@ API_EXPORT(void) ap_add_cgi_vars(request_rec *r) */ if (!strcmp(r->protocol, "INCLUDED")) { - ap_table_setn(e, "SCRIPT_NAME", r->uri); + apr_table_setn(e, "SCRIPT_NAME", r->uri); if (r->path_info && *r->path_info) { - ap_table_setn(e, "PATH_INFO", r->path_info); + apr_table_setn(e, "PATH_INFO", r->path_info); } } else if (!r->path_info || !*r->path_info) { - ap_table_setn(e, "SCRIPT_NAME", r->uri); + apr_table_setn(e, "SCRIPT_NAME", r->uri); } else { int path_info_start = ap_find_path_info(r->uri, r->path_info); - ap_table_setn(e, "SCRIPT_NAME", - ap_pstrndup(r->pool, r->uri, path_info_start)); + apr_table_setn(e, "SCRIPT_NAME", + apr_pstrndup(r->pool, r->uri, path_info_start)); - ap_table_setn(e, "PATH_INFO", r->path_info); + apr_table_setn(e, "PATH_INFO", r->path_info); } if (r->path_info && r->path_info[0]) { @@ -401,21 +413,17 @@ API_EXPORT(void) ap_add_cgi_vars(request_rec *r) */ request_rec *pa_req; - pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r); + pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, + NULL); if (pa_req->filename) { -#ifdef WIN32 - char buffer[HUGE_STRING_LEN]; -#endif - char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info, + char *pt = apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL); #ifdef WIN32 /* We need to make this a real Windows path name */ - GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL); - ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer)); -#else - ap_table_setn(e, "PATH_TRANSLATED", pt); + apr_filepath_merge(&pt, "", pt, APR_FILEPATH_NATIVE, r->pool); #endif + apr_table_setn(e, "PATH_TRANSLATED", pt); } ap_destroy_sub_req(pa_req); } @@ -424,11 +432,11 @@ API_EXPORT(void) ap_add_cgi_vars(request_rec *r) static int set_cookie_doo_doo(void *v, const char *key, const char *val) { - ap_table_addn(v, key, val); + apr_table_addn(v, key, val); return 1; } -API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, +AP_DECLARE(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, int (*getsfunc) (char *, int, void *), void *getsfunc_data) { @@ -436,8 +444,8 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, char *w, *l; int p; int cgi_status = HTTP_OK; - ap_table_t *merge; - ap_table_t *cookie_table; + apr_table_t *merge; + apr_table_t *cookie_table; if (buffer) { *buffer = '\0'; @@ -445,7 +453,7 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, w = buffer ? buffer : x; /* temporary place to hold headers to merge in later */ - merge = ap_make_table(r->pool, 10); + merge = apr_table_make(r->pool, 10); /* The HTTP specification says that it is legal to merge duplicate * headers into one. Some browsers that support Cookies don't like @@ -453,22 +461,27 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, * separately. Lets humour those browsers by not merging. * Oh what a pain it is. */ - cookie_table = ap_make_table(r->pool, 2); - ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); + cookie_table = apr_table_make(r->pool, 2); + apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL); while (1) { if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) { - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "Premature end of script headers: %s", r->filename); + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "Premature end of script headers: %s", + apr_filepath_name_get(r->filename)); return HTTP_INTERNAL_SERVER_ERROR; } /* Delete terminal (CR?)LF */ p = strlen(w); + /* Indeed, the host's '\n': + '\012' for UNIX; '\015' for MacOS; '\025' for OS/390 + -- whatever the script generates. + */ if (p > 0 && w[p - 1] == '\n') { - if (p > 1 && w[p - 2] == '\015') { + if (p > 1 && w[p - 2] == CR) { w[p - 2] = '\0'; } else { @@ -494,12 +507,12 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) { cond_status = ap_meets_conditions(r); } - ap_overlap_tables(r->err_headers_out, merge, - AP_OVERLAP_TABLES_MERGE); - if (!ap_is_empty_table(cookie_table)) { + apr_table_overlap(r->err_headers_out, merge, + APR_OVERLAP_TABLES_MERGE); + if (!apr_is_empty_table(cookie_table)) { /* the cookies have already been copied to the cookie_table */ - ap_table_unset(r->err_headers_out, "Set-Cookie"); - r->err_headers_out = ap_overlay_tables(r->pool, + apr_table_unset(r->err_headers_out, "Set-Cookie"); + r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out, cookie_table); } return cond_status; @@ -507,27 +520,32 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, /* if we see a bogus header don't ignore it. Shout and scream */ -#ifdef CHARSET_EBCDIC +#if APR_CHARSET_EBCDIC /* Chances are that we received an ASCII header text instead of * the expected EBCDIC header lines. Try to auto-detect: */ if (!(l = strchr(w, ':'))) { int maybeASCII = 0, maybeEBCDIC = 0; - char *cp; + unsigned char *cp, native; + apr_size_t inbytes_left, outbytes_left; for (cp = w; *cp != '\0'; ++cp) { - if (isprint(*cp) && !isprint(os_toebcdic[*cp])) + native = apr_xlate_conv_byte(ap_hdrs_from_ascii, *cp); + if (apr_isprint(*cp) && !apr_isprint(native)) ++maybeEBCDIC; - if (!isprint(*cp) && isprint(os_toebcdic[*cp])) + if (!apr_isprint(*cp) && apr_isprint(native)) ++maybeASCII; - } + } if (maybeASCII > maybeEBCDIC) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, - "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename); - ascii2ebcdic(w, w, cp - w); + ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, + "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", + r->filename); + inbytes_left = outbytes_left = cp - w; + apr_xlate_conv_buffer(ap_hdrs_from_ascii, + w, &inbytes_left, w, &outbytes_left); } } -#endif +#endif /*APR_CHARSET_EBCDIC*/ if (!(l = strchr(w, ':'))) { char malformed[(sizeof MALFORMED_MESSAGE) + 1 + MALFORMED_HEADER_LENGTH_TO_SHOW]; @@ -542,13 +560,14 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, } } - ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, - "%s: %s", malformed, r->filename); + ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r, + "%s: %s", malformed, + apr_filepath_name_get(r->filename)); return HTTP_INTERNAL_SERVER_ERROR; } *l++ = '\0'; - while (*l && ap_isspace(*l)) { + while (*l && apr_isspace(*l)) { ++l; } @@ -558,13 +577,13 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, /* Nuke trailing whitespace */ char *endp = l + strlen(l) - 1; - while (endp > l && ap_isspace(*endp)) { + while (endp > l && apr_isspace(*endp)) { *endp-- = '\0'; } - tmp = ap_pstrdup(r->pool, l); + tmp = apr_pstrdup(r->pool, l); ap_content_type_tolower(tmp); - r->content_type = tmp; + ap_set_content_type(r, tmp); } /* * If the script returned a specific status, that's what @@ -572,526 +591,153 @@ API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer, */ else if (!strcasecmp(w, "Status")) { r->status = cgi_status = atoi(l); - r->status_line = ap_pstrdup(r->pool, l); + r->status_line = apr_pstrdup(r->pool, l); } else if (!strcasecmp(w, "Location")) { - ap_table_set(r->headers_out, w, l); + apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "Content-Length")) { - ap_table_set(r->headers_out, w, l); + apr_table_set(r->headers_out, w, l); } else if (!strcasecmp(w, "Transfer-Encoding")) { - ap_table_set(r->headers_out, w, l); + apr_table_set(r->headers_out, w, l); } /* * If the script gave us a Last-Modified header, we can't just * pass it on blindly because of restrictions on future values. */ else if (!strcasecmp(w, "Last-Modified")) { - time_t mtime = ap_parseHTTPdate(l); - - ap_update_mtime(r, mtime); + ap_update_mtime(r, apr_date_parse_http(l)); ap_set_last_modified(r); } else if (!strcasecmp(w, "Set-Cookie")) { - ap_table_add(cookie_table, w, l); + apr_table_add(cookie_table, w, l); } else { - ap_table_add(merge, w, l); + apr_table_add(merge, w, l); } } + + return OK; } static int getsfunc_FILE(char *buf, int len, void *f) { - return fgets(buf, len, (FILE *) f) != NULL; + return apr_file_gets(buf, len, (apr_file_t *) f) == APR_SUCCESS; } -API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f, +AP_DECLARE(int) ap_scan_script_header_err(request_rec *r, apr_file_t *f, char *buffer) { return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f); } -static int getsfunc_BUFF(char *w, int len, void *fb) -{ - return ap_bgets(w, len, (BUFF *) fb) > 0; -} - -API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb, - char *buffer) -{ - return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb); -} - - -API_EXPORT(void) ap_send_size(size_t size, request_rec *r) -{ - /* XXX: this -1 thing is a gross hack */ - if (size == (size_t)-1) { - ap_rputs(" -", r); - } - else if (!size) { - ap_rputs(" 0k", r); - } - else if (size < 1024) { - ap_rputs(" 1k", r); - } - else if (size < 1048576) { - ap_rprintf(r, "%4dk", (size + 512) / 1024); - } - else if (size < 103809024) { - ap_rprintf(r, "%4.1fM", size / 1048576.0); - } - else { - ap_rprintf(r, "%4dM", (size + 524288) / 1048576); - } -} - -#if defined(OS2) || defined(WIN32) -static char **create_argv_cmd(ap_context_t *p, char *av0, const char *args, char *path) +static int getsfunc_BRIGADE(char *buf, int len, void *arg) { - register int x, n; - char **av; - char *w; - - for (x = 0, n = 2; args[x]; x++) { - if (args[x] == '+') { - ++n; - } - } - - /* Add extra strings to array. */ - n = n + 2; - - av = (char **) ap_palloc(p, (n + 1) * sizeof(char *)); - av[0] = av0; - - /* Now insert the extra strings we made room for above. */ - av[1] = strdup("/C"); - av[2] = strdup(path); - - for (x = (1 + 2); x < n; x++) { - w = ap_getword(p, &args, '+'); - ap_unescape_url(w); - av[x] = ap_escape_shell_cmd(p, w); - } - av[n] = NULL; - return av; -} -#endif - -/* ZZZ need to look at this in more depth and convert to an AP func so we - can get rid of OS specific code. - */ -#if 0 -API_EXPORT(int) ap_call_exec(request_rec *r, ap_child_info_t *pinfo, char *argv0, - char **env, int shellcmd) -{ - int pid = 0; - int errfileno = STDERR_FILENO; - -#if !defined(WIN32) && !defined(OS2) - /* the fd on r->server->error_log is closed, but we need somewhere to - * put the error messages from the log_* functions. So, we use stderr, - * since that is better than allowing errors to go unnoticed. Don't do - * this on Win32, though, since we haven't fork()'d. - */ - ap_put_os_file(&r->server->error_log, &errfileno, r->pool); -#endif - - /* TODO: all that RLimit stuff should become part of the spawning API, - * and not something in the core_dir_config... define a special rlimit - * structure and pass it in here. - */ - -#ifdef OS2 - { - /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */ - int is_script = 0; - char interpreter[2048]; /* hope it's enough for the interpreter path */ - char error_object[260]; - FILE *program; - char *cmdline = r->filename, *cmdline_pos; - int cmdlen; - char *args = "", *args_end; - ULONG rc; - RESULTCODES rescodes; - int env_len, e; - char *env_block, *env_block_pos; - - if (r->args && r->args[0] && !strchr(r->args, '=')) - args = r->args; - - program = fopen(r->filename, "rt"); - - if (!program) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed", - r->filename); - return (pid); - } - - fgets(interpreter, sizeof(interpreter), program); - fclose(program); - - if (!strncmp(interpreter, "#!", 2)) { - is_script = 1; - interpreter[strlen(interpreter) - 1] = '\0'; - if (interpreter[2] != '/' && interpreter[2] != '\\' && interpreter[3] != ':') { - char buffer[300]; - if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { - strcpy(interpreter+2, buffer); - } else { - strcat(interpreter, ".exe"); - if (DosSearchPath(SEARCH_ENVIRONMENT, "PATH", interpreter+2, buffer, sizeof(buffer)) == 0) { - strcpy(interpreter+2, buffer); - } - } - } - } - - if (is_script) { - cmdline = ap_pstrcat(r->pool, interpreter+2, " ", r->filename, NULL); - } - else if (strstr(strupr(r->filename), ".CMD") > 0) { - /* Special case to allow use of REXX commands as scripts. */ - os2pathname(r->filename); - cmdline = ap_pstrcat(r->pool, SHELL_PATH, " /C ", r->filename, NULL); - } - else { - cmdline = r->filename; - } - - args = ap_pstrdup(r->pool, args); - ap_unescape_url(args); - args = ap_double_quotes(r->pool, args); - args_end = args + strlen(args); - - if (args_end - args > 4000) { /* cmd.exe won't handle lines longer than 4k */ - args_end = args + 4000; - *args_end = 0; - } - - /* +4 = 1 space between progname and args, 2 for double null at end, 2 for possible quote on first arg */ - cmdlen = strlen(cmdline) + strlen(args) + 4; - cmdline_pos = cmdline; - - while (*cmdline_pos) { - cmdlen += 2 * (*cmdline_pos == '+'); /* Allow space for each arg to be quoted */ - cmdline_pos++; - } - - cmdline = ap_pstrndup(r->pool, cmdline, cmdlen); - cmdline_pos = cmdline + strlen(cmdline); - - while (args < args_end) { - char *arg; - - arg = ap_getword_nc(r->pool, &args, '+'); - - if (strpbrk(arg, "&|<> ")) - arg = ap_pstrcat(r->pool, "\"", arg, "\"", NULL); - - *(cmdline_pos++) = ' '; - strcpy(cmdline_pos, arg); - cmdline_pos += strlen(cmdline_pos); - } - - *(++cmdline_pos) = 0; /* Add required second terminator */ - args = strchr(cmdline, ' '); - - if (args) { - *args = 0; - args++; - } - - /* Create environment block from list of envariables */ - for (env_len=1, e=0; env[e]; e++) - env_len += strlen(env[e]) + 1; - - env_block = ap_palloc(r->pool, env_len); - env_block_pos = env_block; - - for (e=0; env[e]; e++) { - strcpy(env_block_pos, env[e]); - env_block_pos += strlen(env_block_pos) + 1; + apr_bucket_brigade *bb = (apr_bucket_brigade *)arg; + const char *dst_end = buf + len - 1; /* leave room for terminating null */ + char *dst = buf; + apr_bucket *e = APR_BRIGADE_FIRST(bb); + apr_status_t rv; + int done = 0; + + while ((dst < dst_end) && !done && !APR_BUCKET_IS_EOS(e)) { + const char *bucket_data; + apr_size_t bucket_data_len; + const char *src; + const char *src_end; + apr_bucket * next; + + rv = apr_bucket_read(e, &bucket_data, &bucket_data_len, + APR_BLOCK_READ); + if (!APR_STATUS_IS_SUCCESS(rv) || (bucket_data_len == 0)) { + return 0; } - - *env_block_pos = 0; /* environment block is terminated by a double null */ - - rc = DosExecPgm(error_object, sizeof(error_object), EXEC_ASYNC, cmdline, env_block, &rescodes, cmdline); - - if (rc) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "DosExecPgm(%s %s) failed, %s - %s", - cmdline, args ? args : "", ap_os_error_message(rc), error_object ); - return -1; - } - - return rescodes.codeTerminate; - } -#elif defined(WIN32) - { - /* Adapted from Alec Kloss' work for OS/2 */ - char *interpreter = NULL; - char *arguments = NULL; - char *ext = NULL; - char *exename = NULL; - char *s = NULL; - char *quoted_filename; - char *pCommand; - char *pEnvBlock, *pNext; - - int i; - int iEnvBlockLen; - - file_type_e fileType; - - STARTUPINFO si; - PROCESS_INFORMATION pi; - - memset(&si, 0, sizeof(si)); - memset(&pi, 0, sizeof(pi)); - - pid = -1; - - if (!shellcmd) { - - fileType = ap_get_win32_interpreter(r, &interpreter); - - if (fileType == eFileTypeUNKNOWN) { - ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r, - "%s is not executable; ensure interpreted scripts have " - "\"#!\" first line", - r->filename); - return (pid); + src = bucket_data; + src_end = bucket_data + bucket_data_len; + while ((src < src_end) && (dst < dst_end) && !done) { + if (*src == '\n') { + done = 1; } - - /* - * Look at the arguments... - */ - arguments = ""; - if ((r->args) && (r->args[0]) && !strchr(r->args, '=')) { - /* If we are in this leg, there are some other arguments - * that we must include in the execution of the CGI. - * Because CreateProcess is the way it is, we have to - * create a command line like format for the execution - * of the CGI. This means we need to create on long - * string with the executable and arguments. - * - * The arguments string comes in the request structure, - * and each argument is separated by a '+'. We'll replace - * these pluses with spaces. - */ - - int iStringSize = 0; - int x; - - /* - * Duplicate the request structure string so we don't change it. - */ - arguments = ap_pstrdup(r->pool, r->args); - - /* - * Change the '+' to ' ' - */ - for (x=0; arguments[x]; x++) { - if ('+' == arguments[x]) { - arguments[x] = ' '; - } - } - - /* - * We need to unescape any characters that are - * in the arguments list. - */ - ap_unescape_url(arguments); - arguments = ap_escape_shell_cmd(r->pool, arguments); - } - - /* - * We have the interpreter (if there is one) and we have - * the arguments (if there are any). - * Build the command string to pass to CreateProcess. - */ - quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL); - if (interpreter && *interpreter) { - pCommand = ap_pstrcat(r->pool, interpreter, " ", - quoted_filename, " ", arguments, NULL); + else if (*src != '\r') { + *dst++ = *src; } - else { - pCommand = ap_pstrcat(r->pool, quoted_filename, " ", arguments, NULL); - } - - } else { - - char *shell_cmd = "CMD.EXE /C "; - OSVERSIONINFO osver; - osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - /* - * Use CMD.EXE for NT, COMMAND.COM for WIN95 - */ - if (GetVersionEx(&osver)) { - if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) { - shell_cmd = "COMMAND.COM /C "; - } - } - pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL); + src++; } - /* - * Make child process use hPipeOutputWrite as standard out, - * and make sure it does not show on screen. - */ - si.cb = sizeof(si); - si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; - si.wShowWindow = SW_HIDE; - si.hStdInput = pinfo->hPipeInputRead; - si.hStdOutput = pinfo->hPipeOutputWrite; - si.hStdError = pinfo->hPipeErrorWrite; - - /* - * Win32's CreateProcess call requires that the environment - * be passed in an environment block, a null terminated block of - * null terminated strings. - */ - i = 0; - iEnvBlockLen = 1; - while (env[i]) { - iEnvBlockLen += strlen(env[i]) + 1; - i++; + if (src < src_end) { + apr_bucket_split(e, src - bucket_data); } - - pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen); - - i = 0; - pNext = pEnvBlock; - while (env[i]) { - strcpy(pNext, env[i]); - pNext = pNext + strlen(pNext) + 1; - i++; - } - - if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, DETACHED_PROCESS, pEnvBlock, - ap_make_dirstr_parent(r->pool, r->filename), - &si, &pi)) { - if (fileType == eFileTypeEXE16) { - /* Hack to get 16-bit CGI's working. It works for all the - * standard modules shipped with Apache. pi.dwProcessId is 0 - * for 16-bit CGIs and all the Unix specific code that calls - * ap_call_exec interprets this as a failure case. And we can't - * use -1 either because it is mapped to 0 by the caller. - */ - pid = -2; - } - else { - pid = pi.dwProcessId; - /* - * We must close the handles to the new process and its main thread - * to prevent handle and memory leaks. - */ - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - } - } - return (pid); + next = APR_BUCKET_NEXT(e); + APR_BUCKET_REMOVE(e); + apr_bucket_destroy(e); + e = next; } + *dst = 0; + return 1; +} -#else - /* TODO: reimplement suexec */ -#if 0 - if (ap_suexec_enabled - && ((r->server->server_uid != ap_user_id) - || (r->server->server_gid != ap_group_id) - || (!strncmp("/~", r->uri, 2)))) { - - char *execuser, *grpname; - struct passwd *pw; - struct group *gr; - - if (!strncmp("/~", r->uri, 2)) { - gid_t user_gid; - char *username = ap_pstrdup(r->pool, r->uri + 2); - char *pos = strchr(username, '/'); - - if (pos) { - *pos = '\0'; - } - - if ((pw = getpwnam(username)) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "getpwnam: invalid username %s", username); - return (pid); - } - execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL); - user_gid = pw->pw_gid; - - if ((gr = getgrgid(user_gid)) == NULL) { - if ((grpname = ap_palloc(r->pool, 16)) == NULL) { - return (pid); - } - else { - ap_snprintf(grpname, 16, "%ld", (long) user_gid); - } - } - else { - grpname = gr->gr_name; - } - } - else { - if ((pw = getpwuid(r->server->server_uid)) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "getpwuid: invalid userid %ld", - (long) r->server->server_uid); - return (pid); - } - execuser = ap_pstrdup(r->pool, pw->pw_name); - - if ((gr = getgrgid(r->server->server_gid)) == NULL) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, r, - "getgrgid: invalid groupid %ld", - (long) r->server->server_gid); - return (pid); - } - grpname = gr->gr_name; - } - - if (shellcmd) { - execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, - NULL, env); - } - - else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { - execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0, - NULL, env); - } +AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r, + apr_bucket_brigade *bb, + char *buffer) +{ + return ap_scan_script_header_err_core(r, buffer, getsfunc_BRIGADE, bb); +} - else { - execve(SUEXEC_BIN, - create_argv(r->pool, SUEXEC_BIN, execuser, grpname, - argv0, r->args), - env); - } - } - else { -#endif - if (shellcmd) { - execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env); - } +struct vastrs { + va_list args; + int arg; + const char *curpos; +}; - else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { - execle(r->filename, argv0, NULL, env); - } +static int getsfunc_STRING(char *w, int len, void *pvastrs) +{ + struct vastrs *strs = (struct vastrs*) pvastrs; + const char *p; + int t; + + if (!strs->curpos || !*strs->curpos) + return 0; + p = ap_strchr_c(strs->curpos, '\n'); + if (p) + ++p; + else + p = ap_strchr_c(strs->curpos, '\0'); + t = p - strs->curpos; + if (t > len) + t = len; + strncpy (w, strs->curpos, t); + w[t] = '\0'; + if (!strs->curpos[t]) { + ++strs->arg; + strs->curpos = va_arg(strs->args, const char *); + } + else + strs->curpos += t; + return t; +} - else { - execve(r->filename, - create_argv(r->pool, NULL, NULL, NULL, argv0, r->args), - env); - } -#if 0 - } -#endif - return (pid); -#endif +/* ap_scan_script_header_err_strs() accepts additional const char* args... + * each is treated as one or more header lines, and the first non-header + * character is returned to **arg, **data. (The first optional arg is + * counted as 0.) + */ +AP_DECLARE_NONSTD(int) ap_scan_script_header_err_strs(request_rec *r, + char *buffer, + const char **termch, + int *termarg, ...) +{ + struct vastrs strs; + int res; + + va_start(strs.args, termarg); + strs.arg = 0; + strs.curpos = va_arg(strs.args, char*); + res = ap_scan_script_header_err_core(r, buffer, getsfunc_STRING, (void *) &strs); + if (termch) + *termch = strs.curpos; + if (termarg) + *termarg = strs.arg; + va_end(strs.args); + return res; } -#endif