home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / main / util_script.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  34.1 KB  |  1,191 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. #define CORE_PRIVATE
  59. #include "httpd.h"
  60. #include "http_config.h"
  61. #include "http_conf_globals.h"
  62. #include "http_main.h"
  63. #include "http_log.h"
  64. #include "http_protocol.h"
  65. #include "http_core.h"        /* For document_root.  Sigh... */
  66. #include "http_request.h"    /* for sub_req_lookup_uri() */
  67. #include "util_script.h"
  68. #include "util_date.h"        /* For parseHTTPdate() */
  69.  
  70. /*
  71.  * Various utility functions which are common to a whole lot of
  72.  * script-type extensions mechanisms, and might as well be gathered
  73.  * in one place (if only to avoid creating inter-module dependancies
  74.  * where there don't have to be).
  75.  */
  76.  
  77. #define MALFORMED_MESSAGE "malformed header from script. Bad header="
  78. #define MALFORMED_HEADER_LENGTH_TO_SHOW 30
  79.  
  80. /* If a request includes query info in the URL (stuff after "?"), and
  81.  * the query info does not contain "=" (indicative of a FORM submission),
  82.  * then this routine is called to create the argument list to be passed
  83.  * to the CGI script.  When suexec is enabled, the suexec path, user, and
  84.  * group are the first three arguments to be passed; if not, all three
  85.  * must be NULL.  The query info is split into separate arguments, where
  86.  * "+" is the separator between keyword arguments.
  87.  *
  88.  * XXXX: note that the WIN32 code uses one of the suexec strings 
  89.  * to pass an interpreter name.  Remember this if changing the way they
  90.  * are handled in create_argv.
  91.  *
  92.  */
  93. static char **create_argv(pool *p, char *path, char *user, char *group,
  94.               char *av0, const char *args)
  95. {
  96.     int x, numwords;
  97.     char **av;
  98.     char *w;
  99.     int idx = 0;
  100.  
  101.     /* count the number of keywords */
  102.  
  103.     for (x = 0, numwords = 1; args[x]; x++) {
  104.         if (args[x] == '+') {
  105.         ++numwords;
  106.     }
  107.     }
  108.  
  109.     if (numwords > APACHE_ARG_MAX - 5) {
  110.     numwords = APACHE_ARG_MAX - 5;    /* Truncate args to prevent overrun */
  111.     }
  112.     av = (char **) ap_palloc(p, (numwords + 5) * sizeof(char *));
  113.  
  114.     if (path) {
  115.     av[idx++] = path;
  116.     }
  117.     if (user) {
  118.     av[idx++] = user;
  119.     }
  120.     if (group) {
  121.     av[idx++] = group;
  122.     }
  123.  
  124.     av[idx++] = av0;
  125.  
  126.     for (x = 1; x <= numwords; x++) {
  127.     w = ap_getword_nulls(p, &args, '+');
  128.     ap_unescape_url(w);
  129.     av[idx++] = ap_escape_shell_cmd(p, w);
  130.     }
  131.     av[idx] = NULL;
  132.     return av;
  133. }
  134.  
  135.  
  136. static char *http2env(pool *a, char *w)
  137. {
  138.     char *res = ap_pstrcat(a, "HTTP_", w, NULL);
  139.     char *cp = res;
  140.  
  141.     while (*++cp) {
  142.     if (!ap_isalnum(*cp) && *cp != '_') {
  143.         *cp = '_';
  144.     }
  145.     else {
  146.         *cp = ap_toupper(*cp);
  147.     }
  148.     }
  149.  
  150.     return res;
  151. }
  152.  
  153. API_EXPORT(char **) ap_create_environment(pool *p, table *t)
  154. {
  155.     array_header *env_arr = ap_table_elts(t);
  156.     table_entry *elts = (table_entry *) env_arr->elts;
  157.     char **env = (char **) ap_palloc(p, (env_arr->nelts + 2) * sizeof(char *));
  158.     int i, j;
  159.     char *tz;
  160.     char *whack;
  161.  
  162.     j = 0;
  163.     if (!ap_table_get(t, "TZ")) {
  164.     tz = getenv("TZ");
  165.     if (tz != NULL) {
  166.         env[j++] = ap_pstrcat(p, "TZ=", tz, NULL);
  167.     }
  168.     }
  169.     for (i = 0; i < env_arr->nelts; ++i) {
  170.         if (!elts[i].key) {
  171.         continue;
  172.     }
  173.     env[j] = ap_pstrcat(p, elts[i].key, "=", elts[i].val, NULL);
  174.     whack = env[j];
  175.     if (ap_isdigit(*whack)) {
  176.         *whack++ = '_';
  177.     }
  178.     while (*whack != '=') {
  179.         if (!ap_isalnum(*whack) && *whack != '_') {
  180.         *whack = '_';
  181.         }
  182.         ++whack;
  183.     }
  184.     ++j;
  185.     }
  186.  
  187.     env[j] = NULL;
  188.     return env;
  189. }
  190.  
  191. API_EXPORT(void) ap_add_common_vars(request_rec *r)
  192. {
  193.     table *e;
  194.     server_rec *s = r->server;
  195.     conn_rec *c = r->connection;
  196.     const char *rem_logname;
  197.     char *env_path;
  198. #ifdef WIN32
  199.     char *env_temp;
  200. #endif
  201.     const char *host;
  202.     array_header *hdrs_arr = ap_table_elts(r->headers_in);
  203.     table_entry *hdrs = (table_entry *) hdrs_arr->elts;
  204.     int i;
  205.  
  206.     /* use a temporary table which we'll overlap onto
  207.      * r->subprocess_env later
  208.      */
  209.     e = ap_make_table(r->pool, 25 + hdrs_arr->nelts);
  210.  
  211.     /* First, add environment vars from headers... this is as per
  212.      * CGI specs, though other sorts of scripting interfaces see
  213.      * the same vars...
  214.      */
  215.  
  216.     for (i = 0; i < hdrs_arr->nelts; ++i) {
  217.         if (!hdrs[i].key) {
  218.         continue;
  219.     }
  220.  
  221.     /* A few headers are special cased --- Authorization to prevent
  222.      * rogue scripts from capturing passwords; content-type and -length
  223.      * for no particular reason.
  224.      */
  225.  
  226.     if (!strcasecmp(hdrs[i].key, "Content-type")) {
  227.         ap_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
  228.     }
  229.     else if (!strcasecmp(hdrs[i].key, "Content-length")) {
  230.         ap_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
  231.     }
  232.     /*
  233.      * You really don't want to disable this check, since it leaves you
  234.      * wide open to CGIs stealing passwords and people viewing them
  235.      * in the environment with "ps -e".  But, if you must...
  236.      */
  237. #ifndef SECURITY_HOLE_PASS_AUTHORIZATION
  238.     else if (!strcasecmp(hdrs[i].key, "Authorization") 
  239.          || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
  240.         continue;
  241.     }
  242. #endif
  243.     else {
  244.         ap_table_addn(e, http2env(r->pool, hdrs[i].key), hdrs[i].val);
  245.     }
  246.     }
  247.  
  248.     if (!(env_path = getenv("PATH"))) {
  249.     env_path = DEFAULT_PATH;
  250.     }
  251.  
  252. #ifdef WIN32
  253.     if (env_temp = getenv("SystemRoot")) {
  254.         ap_table_addn(e, "SystemRoot", env_temp);         
  255.     }
  256.     if (env_temp = getenv("COMSPEC")) {
  257.         ap_table_addn(e, "COMSPEC", env_temp);            
  258.     }
  259.     if (env_temp = getenv("WINDIR")) {
  260.         ap_table_addn(e, "WINDIR", env_temp);
  261.     }
  262. #endif
  263.  
  264.     ap_table_addn(e, "PATH", env_path);
  265.     ap_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
  266.     ap_table_addn(e, "SERVER_SOFTWARE", ap_get_server_version());
  267.     ap_table_addn(e, "SERVER_NAME", ap_get_server_name(r));
  268.     ap_table_addn(e, "SERVER_PORT",
  269.           ap_psprintf(r->pool, "%u", ap_get_server_port(r)));
  270.     host = ap_get_remote_host(c, r->per_dir_config, REMOTE_HOST);
  271.     if (host) {
  272.     ap_table_addn(e, "REMOTE_HOST", host);
  273.     }
  274.     ap_table_addn(e, "REMOTE_ADDR", c->remote_ip);
  275.     ap_table_addn(e, "DOCUMENT_ROOT", ap_document_root(r));    /* Apache */
  276.     ap_table_addn(e, "SERVER_ADMIN", s->server_admin);    /* Apache */
  277.     ap_table_addn(e, "SCRIPT_FILENAME", r->filename);    /* Apache */
  278.  
  279.     ap_table_addn(e, "REMOTE_PORT",
  280.           ap_psprintf(r->pool, "%d", ntohs(c->remote_addr.sin_port)));
  281.  
  282.     if (c->user) {
  283.     ap_table_addn(e, "REMOTE_USER", c->user);
  284.     }
  285.     if (c->ap_auth_type) {
  286.     ap_table_addn(e, "AUTH_TYPE", c->ap_auth_type);
  287.     }
  288.     rem_logname = ap_get_remote_logname(r);
  289.     if (rem_logname) {
  290.     ap_table_addn(e, "REMOTE_IDENT", ap_pstrdup(r->pool, rem_logname));
  291.     }
  292.  
  293.     /* Apache custom error responses. If we have redirected set two new vars */
  294.  
  295.     if (r->prev) {
  296.         if (r->prev->args) {
  297.         ap_table_addn(e, "REDIRECT_QUERY_STRING", r->prev->args);
  298.     }
  299.     if (r->prev->uri) {
  300.         ap_table_addn(e, "REDIRECT_URL", r->prev->uri);
  301.     }
  302.     }
  303.  
  304.     ap_overlap_tables(r->subprocess_env, e, AP_OVERLAP_TABLES_SET);
  305. }
  306.  
  307. /* This "cute" little function comes about because the path info on
  308.  * filenames and URLs aren't always the same. So we take the two,
  309.  * and find as much of the two that match as possible.
  310.  */
  311.  
  312. API_EXPORT(int) ap_find_path_info(const char *uri, const char *path_info)
  313. {
  314.     int lu = strlen(uri);
  315.     int lp = strlen(path_info);
  316.  
  317.     while (lu-- && lp-- && uri[lu] == path_info[lp]);
  318.  
  319.     if (lu == -1) {
  320.     lu = 0;
  321.     }
  322.  
  323.     while (uri[lu] != '\0' && uri[lu] != '/') {
  324.         lu++;
  325.     }
  326.     return lu;
  327. }
  328.  
  329. /* Obtain the Request-URI from the original request-line, returning
  330.  * a new string from the request pool containing the URI or "".
  331.  */
  332. static char *original_uri(request_rec *r)
  333. {
  334.     char *first, *last;
  335.  
  336.     if (r->the_request == NULL) {
  337.     return (char *) ap_pcalloc(r->pool, 1);
  338.     }
  339.  
  340.     first = r->the_request;    /* use the request-line */
  341.  
  342.     while (*first && !ap_isspace(*first)) {
  343.     ++first;        /* skip over the method */
  344.     }
  345.     while (ap_isspace(*first)) {
  346.     ++first;        /*   and the space(s)   */
  347.     }
  348.  
  349.     last = first;
  350.     while (*last && !ap_isspace(*last)) {
  351.     ++last;            /* end at next whitespace */
  352.     }
  353.  
  354.     return ap_pstrndup(r->pool, first, last - first);
  355. }
  356.  
  357. API_EXPORT(void) ap_add_cgi_vars(request_rec *r)
  358. {
  359.     table *e = r->subprocess_env;
  360.  
  361.     ap_table_setn(e, "GATEWAY_INTERFACE", "CGI/1.1");
  362.     ap_table_setn(e, "SERVER_PROTOCOL", r->protocol);
  363.     ap_table_setn(e, "REQUEST_METHOD", r->method);
  364.     ap_table_setn(e, "QUERY_STRING", r->args ? r->args : "");
  365.     ap_table_setn(e, "REQUEST_URI", original_uri(r));
  366.  
  367.     /* Note that the code below special-cases scripts run from includes,
  368.      * because it "knows" that the sub_request has been hacked to have the
  369.      * args and path_info of the original request, and not any that may have
  370.      * come with the script URI in the include command.  Ugh.
  371.      */
  372.  
  373.     if (!strcmp(r->protocol, "INCLUDED")) {
  374.     ap_table_setn(e, "SCRIPT_NAME", r->uri);
  375.     if (r->path_info && *r->path_info) {
  376.         ap_table_setn(e, "PATH_INFO", r->path_info);
  377.     }
  378.     }
  379.     else if (!r->path_info || !*r->path_info) {
  380.     ap_table_setn(e, "SCRIPT_NAME", r->uri);
  381.     }
  382.     else {
  383.     int path_info_start = ap_find_path_info(r->uri, r->path_info);
  384.  
  385.     ap_table_setn(e, "SCRIPT_NAME",
  386.               ap_pstrndup(r->pool, r->uri, path_info_start));
  387.  
  388.     ap_table_setn(e, "PATH_INFO", r->path_info);
  389.     }
  390.  
  391.     if (r->path_info && r->path_info[0]) {
  392.     /*
  393.      * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
  394.      * Need to re-escape it for this, since the entire URI was
  395.      * un-escaped before we determined where the PATH_INFO began.
  396.      */
  397.     request_rec *pa_req;
  398.  
  399.     pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
  400.  
  401.     if (pa_req->filename) {
  402. #ifdef WIN32
  403.         char buffer[HUGE_STRING_LEN];
  404. #endif
  405.         char *pt = ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
  406.                   NULL);
  407. #ifdef WIN32
  408.         /* We need to make this a real Windows path name */
  409.         GetFullPathName(pt, HUGE_STRING_LEN, buffer, NULL);
  410.         ap_table_setn(e, "PATH_TRANSLATED", ap_pstrdup(r->pool, buffer));
  411. #else
  412.         ap_table_setn(e, "PATH_TRANSLATED", pt);
  413. #endif
  414.     }
  415.     ap_destroy_sub_req(pa_req);
  416.     }
  417. }
  418.  
  419.  
  420. static int set_cookie_doo_doo(void *v, const char *key, const char *val)
  421. {
  422.     ap_table_addn(v, key, val);
  423.     return 1;
  424. }
  425.  
  426. API_EXPORT(int) ap_scan_script_header_err_core(request_rec *r, char *buffer,
  427.                        int (*getsfunc) (char *, int, void *),
  428.                        void *getsfunc_data)
  429. {
  430.     char x[MAX_STRING_LEN];
  431.     char *w, *l;
  432.     int p;
  433.     int cgi_status = HTTP_OK;
  434.     table *merge;
  435.     table *cookie_table;
  436.  
  437.     if (buffer) {
  438.     *buffer = '\0';
  439.     }
  440.     w = buffer ? buffer : x;
  441.  
  442.     ap_hard_timeout("read script header", r);
  443.  
  444.     /* temporary place to hold headers to merge in later */
  445.     merge = ap_make_table(r->pool, 10);
  446.  
  447.     /* The HTTP specification says that it is legal to merge duplicate
  448.      * headers into one.  Some browsers that support Cookies don't like
  449.      * merged headers and prefer that each Set-Cookie header is sent
  450.      * separately.  Lets humour those browsers by not merging.
  451.      * Oh what a pain it is.
  452.      */
  453.     cookie_table = ap_make_table(r->pool, 2);
  454.     ap_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out, "Set-Cookie", NULL);
  455.  
  456.     while (1) {
  457.  
  458.     if ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data) == 0) {
  459.         ap_kill_timeout(r);
  460.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  461.               "Premature end of script headers: %s", r->filename);
  462.         return HTTP_INTERNAL_SERVER_ERROR;
  463.     }
  464.  
  465.     /* Delete terminal (CR?)LF */
  466.  
  467.     p = strlen(w);
  468.     if (p > 0 && w[p - 1] == '\n') {
  469.         if (p > 1 && w[p - 2] == '\015') {
  470.         w[p - 2] = '\0';
  471.         }
  472.         else {
  473.         w[p - 1] = '\0';
  474.         }
  475.     }
  476.  
  477.     /*
  478.      * If we've finished reading the headers, check to make sure any
  479.      * HTTP/1.1 conditions are met.  If so, we're done; normal processing
  480.      * will handle the script's output.  If not, just return the error.
  481.      * The appropriate thing to do would be to send the script process a
  482.      * SIGPIPE to let it know we're ignoring it, close the channel to the
  483.      * script process, and *then* return the failed-to-meet-condition
  484.      * error.  Otherwise we'd be waiting for the script to finish
  485.      * blithering before telling the client the output was no good.
  486.      * However, we don't have the information to do that, so we have to
  487.      * leave it to an upper layer.
  488.      */
  489.     if (w[0] == '\0') {
  490.         int cond_status = OK;
  491.  
  492.         ap_kill_timeout(r);
  493.         if ((cgi_status == HTTP_OK) && (r->method_number == M_GET)) {
  494.         cond_status = ap_meets_conditions(r);
  495.         }
  496.         ap_overlap_tables(r->err_headers_out, merge,
  497.         AP_OVERLAP_TABLES_MERGE);
  498.         if (!ap_is_empty_table(cookie_table)) {
  499.         r->err_headers_out = ap_overlay_tables(r->pool,
  500.             r->err_headers_out, cookie_table);
  501.         }
  502.         return cond_status;
  503.     }
  504.  
  505.     /* if we see a bogus header don't ignore it. Shout and scream */
  506.  
  507. #ifdef CHARSET_EBCDIC
  508.         /* Chances are that we received an ASCII header text instead of
  509.          * the expected EBCDIC header lines. Try to auto-detect:
  510.          */
  511.     if (!(l = strchr(w, ':'))) {
  512.         int maybeASCII = 0, maybeEBCDIC = 0;
  513.         char *cp;
  514.  
  515.         for (cp = w; *cp != '\0'; ++cp) {
  516.         if (isprint(*cp) && !isprint(os_toebcdic[*cp]))
  517.             ++maybeEBCDIC;
  518.         if (!isprint(*cp) && isprint(os_toebcdic[*cp]))
  519.             ++maybeASCII;
  520.         }
  521.         if (maybeASCII > maybeEBCDIC) {
  522.         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
  523.              "CGI Interface Error: Script headers apparently ASCII: (CGI = %s)", r->filename);
  524.         ascii2ebcdic(w, w, cp - w);
  525.         }
  526.     }
  527. #endif
  528.     if (!(l = strchr(w, ':'))) {
  529.         char malformed[(sizeof MALFORMED_MESSAGE) + 1
  530.                + MALFORMED_HEADER_LENGTH_TO_SHOW];
  531.  
  532.         strcpy(malformed, MALFORMED_MESSAGE);
  533.         strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
  534.  
  535.         if (!buffer) {
  536.         /* Soak up all the script output - may save an outright kill */
  537.             while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
  538.             continue;
  539.         }
  540.         }
  541.  
  542.         ap_kill_timeout(r);
  543.         ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  544.               "%s: %s", malformed, r->filename);
  545.         return HTTP_INTERNAL_SERVER_ERROR;
  546.     }
  547.  
  548.     *l++ = '\0';
  549.     while (*l && ap_isspace(*l)) {
  550.         ++l;
  551.     }
  552.  
  553.     if (!strcasecmp(w, "Content-type")) {
  554.         char *tmp;
  555.  
  556.         /* Nuke trailing whitespace */
  557.  
  558.         char *endp = l + strlen(l) - 1;
  559.         while (endp > l && ap_isspace(*endp)) {
  560.         *endp-- = '\0';
  561.         }
  562.  
  563.         tmp = ap_pstrdup(r->pool, l);
  564.         ap_content_type_tolower(tmp);
  565.         r->content_type = tmp;
  566.     }
  567.     /*
  568.      * If the script returned a specific status, that's what
  569.      * we'll use - otherwise we assume 200 OK.
  570.      */
  571.     else if (!strcasecmp(w, "Status")) {
  572.         r->status = cgi_status = atoi(l);
  573.         r->status_line = ap_pstrdup(r->pool, l);
  574.     }
  575.     else if (!strcasecmp(w, "Location")) {
  576.         ap_table_set(r->headers_out, w, l);
  577.     }
  578.     else if (!strcasecmp(w, "Content-Length")) {
  579.         ap_table_set(r->headers_out, w, l);
  580.     }
  581.     else if (!strcasecmp(w, "Transfer-Encoding")) {
  582.         ap_table_set(r->headers_out, w, l);
  583.     }
  584.     /*
  585.      * If the script gave us a Last-Modified header, we can't just
  586.      * pass it on blindly because of restrictions on future values.
  587.      */
  588.     else if (!strcasecmp(w, "Last-Modified")) {
  589.         time_t mtime = ap_parseHTTPdate(l);
  590.  
  591.         ap_update_mtime(r, mtime);
  592.         ap_set_last_modified(r);
  593.     }
  594.     else if (!strcasecmp(w, "Set-Cookie")) {
  595.         ap_table_add(cookie_table, w, l);
  596.     }
  597.     else {
  598.         ap_table_add(merge, w, l);
  599.     }
  600.     }
  601. }
  602.  
  603. static int getsfunc_FILE(char *buf, int len, void *f)
  604. {
  605.     return fgets(buf, len, (FILE *) f) != NULL;
  606. }
  607.  
  608. API_EXPORT(int) ap_scan_script_header_err(request_rec *r, FILE *f,
  609.                       char *buffer)
  610. {
  611.     return ap_scan_script_header_err_core(r, buffer, getsfunc_FILE, f);
  612. }
  613.  
  614. static int getsfunc_BUFF(char *w, int len, void *fb)
  615. {
  616.     return ap_bgets(w, len, (BUFF *) fb) > 0;
  617. }
  618.  
  619. API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *fb,
  620.                            char *buffer)
  621. {
  622.     return ap_scan_script_header_err_core(r, buffer, getsfunc_BUFF, fb);
  623. }
  624.  
  625.  
  626. API_EXPORT(void) ap_send_size(size_t size, request_rec *r)
  627. {
  628.     /* XXX: this -1 thing is a gross hack */
  629.     if (size == (size_t)-1) {
  630.     ap_rputs("    -", r);
  631.     }
  632.     else if (!size) {
  633.     ap_rputs("   0k", r);
  634.     }
  635.     else if (size < 1024) {
  636.     ap_rputs("   1k", r);
  637.     }
  638.     else if (size < 1048576) {
  639.     ap_rprintf(r, "%4dk", (size + 512) / 1024);
  640.     }
  641.     else if (size < 103809024) {
  642.     ap_rprintf(r, "%4.1fM", size / 1048576.0);
  643.     }
  644.     else {
  645.     ap_rprintf(r, "%4dM", (size + 524288) / 1048576);
  646.     }
  647. }
  648.  
  649. #if defined(OS2) || defined(WIN32)
  650. static char **create_argv_cmd(pool *p, char *av0, const char *args, char *path)
  651. {
  652.     register int x, n;
  653.     char **av;
  654.     char *w;
  655.  
  656.     for (x = 0, n = 2; args[x]; x++) {
  657.         if (args[x] == '+') {
  658.         ++n;
  659.     }
  660.     }
  661.  
  662.     /* Add extra strings to array. */
  663.     n = n + 2;
  664.  
  665.     av = (char **) ap_palloc(p, (n + 1) * sizeof(char *));
  666.     av[0] = av0;
  667.  
  668.     /* Now insert the extra strings we made room for above. */
  669.     av[1] = strdup("/C");
  670.     av[2] = strdup(path);
  671.  
  672.     for (x = (1 + 2); x < n; x++) {
  673.     w = ap_getword(p, &args, '+');
  674.     ap_unescape_url(w);
  675.     av[x] = ap_escape_shell_cmd(p, w);
  676.     }
  677.     av[n] = NULL;
  678.     return av;
  679. }
  680. #endif
  681.  
  682.  
  683. API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0,
  684.                  char **env, int shellcmd)
  685. {
  686.     int pid = 0;
  687. #if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
  688.     defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
  689.  
  690.     core_dir_config *conf;
  691.     conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
  692.                             &core_module);
  693.  
  694. #endif
  695.  
  696. #ifndef WIN32
  697.     /* the fd on r->server->error_log is closed, but we need somewhere to
  698.      * put the error messages from the log_* functions. So, we use stderr,
  699.      * since that is better than allowing errors to go unnoticed.  Don't do
  700.      * this on Win32, though, since we haven't fork()'d.
  701.      */
  702.     r->server->error_log = stderr;
  703. #endif
  704.  
  705. #ifdef RLIMIT_CPU
  706.     if (conf->limit_cpu != NULL) {
  707.         if ((setrlimit(RLIMIT_CPU, conf->limit_cpu)) != 0) {
  708.         ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  709.              "setrlimit: failed to set CPU usage limit");
  710.     }
  711.     }
  712. #endif
  713. #ifdef RLIMIT_NPROC
  714.     if (conf->limit_nproc != NULL) {
  715.         if ((setrlimit(RLIMIT_NPROC, conf->limit_nproc)) != 0) {
  716.         ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  717.              "setrlimit: failed to set process limit");
  718.     }
  719.     }
  720. #endif
  721. #if defined(RLIMIT_AS)
  722.     if (conf->limit_mem != NULL) {
  723.         if ((setrlimit(RLIMIT_AS, conf->limit_mem)) != 0) {
  724.         ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  725.              "setrlimit(RLIMIT_AS): failed to set memory "
  726.              "usage limit");
  727.     }
  728.     }
  729. #elif defined(RLIMIT_DATA)
  730.     if (conf->limit_mem != NULL) {
  731.         if ((setrlimit(RLIMIT_DATA, conf->limit_mem)) != 0) {
  732.         ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  733.              "setrlimit(RLIMIT_DATA): failed to set memory "
  734.              "usage limit");
  735.     }
  736.     }
  737. #elif defined(RLIMIT_VMEM)
  738.     if (conf->limit_mem != NULL) {
  739.         if ((setrlimit(RLIMIT_VMEM, conf->limit_mem)) != 0) {
  740.         ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  741.              "setrlimit(RLIMIT_VMEM): failed to set memory "
  742.              "usage limit");
  743.     }
  744.     }
  745. #endif
  746.  
  747. #ifdef OS2
  748.     {
  749.     /* Additions by Alec Kloss, to allow exec'ing of scripts under OS/2 */
  750.     int is_script;
  751.     char interpreter[2048];    /* hope it's enough for the interpreter path */
  752.     FILE *program;
  753.  
  754.     program = fopen(r->filename, "rt");
  755.     if (!program) {
  756.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "fopen(%s) failed",
  757.              r->filename);
  758.         return (pid);
  759.     }
  760.     fgets(interpreter, sizeof(interpreter), program);
  761.     fclose(program);
  762.     if (!strncmp(interpreter, "#!", 2)) {
  763.         is_script = 1;
  764.         interpreter[strlen(interpreter) - 1] = '\0';
  765.     }
  766.     else {
  767.         is_script = 0;
  768.     }
  769.  
  770.     if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
  771.         int emxloop;
  772.         char *emxtemp;
  773.  
  774.         /* For OS/2 place the variables in the current
  775.          * environment then it will be inherited. This way
  776.          * the program will also get all of OS/2's other SETs.
  777.          */
  778.         for (emxloop = 0; ((emxtemp = env[emxloop]) != NULL); emxloop++) {
  779.         putenv(emxtemp);
  780.         }
  781.  
  782.         /* More additions by Alec Kloss for OS/2 */
  783.         if (is_script) {
  784.         /* here's the stuff to run the interpreter */
  785.         execl(interpreter + 2, interpreter + 2, r->filename, NULL);
  786.         }
  787.         else if (strstr(strupr(r->filename), ".CMD") > 0) {
  788.         /* Special case to allow use of REXX commands as scripts. */
  789.         os2pathname(r->filename);
  790.         execl(SHELL_PATH, SHELL_PATH, "/C", r->filename, NULL);
  791.         }
  792.         else {
  793.         execl(r->filename, argv0, NULL);
  794.         }
  795.     }
  796.     else {
  797.         int emxloop;
  798.         char *emxtemp;
  799.  
  800.         /* For OS/2 place the variables in the current
  801.          * environment so that they will be inherited. This way
  802.          * the program will also get all of OS/2's other SETs.
  803.          */
  804.         for (emxloop = 0; ((emxtemp = env[emxloop]) != NULL); emxloop++) {
  805.         putenv(emxtemp);
  806.         }
  807.  
  808.         if (strstr(strupr(r->filename), ".CMD") > 0) {
  809.         /* Special case to allow use of REXX commands as scripts. */
  810.         os2pathname(r->filename);
  811.         execv(SHELL_PATH, create_argv_cmd(r->pool, argv0, r->args,
  812.                           r->filename));
  813.         }
  814.         else {
  815.         execv(r->filename,
  816.               create_argv(r->pool, NULL, NULL, NULL, argv0, r->args));
  817.         }
  818.     }
  819.     return (pid);
  820.     }
  821. #elif defined(WIN32)
  822.     {
  823.     /* Adapted from Alec Kloss' work for OS/2 */
  824.     int is_script = 0;
  825.     int is_binary = 0;
  826.     char interpreter[2048];    /* hope it's enough for the interpreter path */
  827.     FILE *program;
  828.     int i, sz;
  829.     char *dot;
  830.     char *exename;
  831.         char *quoted_filename;
  832.     int is_exe = 0;
  833.     STARTUPINFO si;
  834.     PROCESS_INFORMATION pi;
  835.         char *pCommand;
  836.         char *pEnvBlock, *pNext;
  837.         int iEnvBlockLen;
  838.  
  839.     memset(&si, 0, sizeof(si));
  840.     memset(&pi, 0, sizeof(pi));
  841.  
  842.     interpreter[0] = 0;
  843.     pid = -1;
  844.  
  845.         quoted_filename = ap_pstrcat(r->pool, "\"", r->filename, "\"", NULL);
  846.  
  847.         if (!shellcmd) {
  848.             exename = strrchr(r->filename, '/');
  849.             if (!exename) {
  850.                 exename = strrchr(r->filename, '\\');
  851.             }
  852.             if (!exename) {
  853.                 exename = r->filename;
  854.             }
  855.             else {
  856.                 exename++;
  857.             }
  858.             dot = strrchr(exename, '.');
  859.             if (dot) {
  860.                 if (!strcasecmp(dot, ".BAT")
  861.                     || !strcasecmp(dot, ".CMD")
  862.                     || !strcasecmp(dot, ".EXE")
  863.                     ||  !strcasecmp(dot, ".COM")) {
  864.                     is_exe = 1;
  865.                 }
  866.             }
  867.  
  868.             if (!is_exe) {
  869.                 program = fopen(r->filename, "rb");
  870.                 if (!program) {
  871.                     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  872.                                  "fopen(%s) failed", r->filename);
  873.                     return (pid);
  874.                 }
  875.                 sz = fread(interpreter, 1, sizeof(interpreter) - 1, program);
  876.                 if (sz < 0) {
  877.                     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  878.                                  "fread of %s failed", r->filename);
  879.                     fclose(program);
  880.                     return (pid);
  881.                 }
  882.                 interpreter[sz] = 0;
  883.                 fclose(program);
  884.                 if (!strncmp(interpreter, "#!", 2)) {
  885.                     is_script = 1;
  886.                     for (i = 2; i < sizeof(interpreter); i++) {
  887.                         if ((interpreter[i] == '\r')
  888.                             || (interpreter[i] == '\n')) {
  889.                             break;
  890.                         }
  891.                     }
  892.                     interpreter[i] = 0;
  893.                     for (i = 2; interpreter[i] == ' '; ++i)
  894.                         ;
  895.                     memmove(interpreter+2,interpreter+i,strlen(interpreter+i)+1);
  896.                 }
  897.                 else {
  898.                     /* Check to see if it's a executable */
  899.                     IMAGE_DOS_HEADER *hdr = (IMAGE_DOS_HEADER*)interpreter;
  900.                     if (hdr->e_magic == IMAGE_DOS_SIGNATURE && hdr->e_cblp < 512) {
  901.                         is_binary = 1;
  902.                     }
  903.                 }
  904.             }
  905.             /* Bail out if we haven't figured out what kind of
  906.              * file this is by now..
  907.              */
  908.             if (!is_exe && !is_script && !is_binary) {
  909.                 ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  910.                              "%s is not executable; ensure interpreted scripts have "
  911.                              "\"#!\" first line", 
  912.                              r->filename);
  913.                 return (pid);
  914.             }
  915.         }
  916.  
  917.         if (shellcmd) {
  918.             char *shell_cmd = "CMD.EXE /C ";
  919.             OSVERSIONINFO osver;
  920.             osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  921.          
  922.             /*
  923.              * Use CMD.EXE for NT, COMMAND.COM for WIN95
  924.              */
  925.             if (GetVersionEx(&osver)) {
  926.                 if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT) {
  927.                     shell_cmd = "COMMAND.COM /C ";
  928.                 }
  929.             }       
  930.             pCommand = ap_pstrcat(r->pool, shell_cmd, argv0, NULL);
  931.         }
  932.      else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { 
  933.         if (is_exe || is_binary) {
  934.             /*
  935.              * When the CGI is a straight binary executable, 
  936.          * we can run it as is
  937.              */
  938.             pCommand = quoted_filename;
  939.         }
  940.         else if (is_script) {
  941.                 /* When an interpreter is needed, we need to create 
  942.                  * a command line that has the interpreter name
  943.                  * followed by the CGI script name.  
  944.          */
  945.             pCommand = ap_pstrcat(r->pool, interpreter + 2, " ", 
  946.                       quoted_filename, NULL);
  947.         }
  948.         else {
  949.             /* If not an executable or script, just execute it
  950.                  * from a command prompt.  
  951.                  */
  952.             pCommand = ap_pstrcat(r->pool, SHELL_PATH, " /C ", 
  953.                       quoted_filename, NULL);
  954.         }
  955.     }
  956.     else {
  957.  
  958.             /* If we are in this leg, there are some other arguments
  959.              * that we must include in the execution of the CGI.
  960.              * Because CreateProcess is the way it is, we have to
  961.              * create a command line like format for the execution
  962.              * of the CGI.  This means we need to create on long
  963.              * string with the executable and arguments.
  964.              *
  965.              * The arguments string comes in the request structure,
  966.              * and each argument is separated by a '+'.  We'll replace
  967.              * these pluses with spaces.
  968.          */
  969.         char *arguments=NULL;
  970.         int iStringSize = 0;
  971.         int x;
  972.         
  973.         /*
  974.          *  Duplicate the request structure string so we don't change it.
  975.          */                                   
  976.         arguments = ap_pstrdup(r->pool, r->args);
  977.        
  978.         /*
  979.          *  Change the '+' to ' '
  980.          */
  981.         for (x=0; arguments[x]; x++) {
  982.             if ('+' == arguments[x]) {
  983.           arguments[x] = ' ';
  984.         }
  985.         }
  986.        
  987.         /*
  988.          * We need to unescape any characters that are 
  989.              * in the arguments list.
  990.          */
  991.         ap_unescape_url(arguments);
  992.         arguments = ap_escape_shell_cmd(r->pool, arguments);
  993.            
  994.         /*
  995.          * The argument list should now be good to use, 
  996.          * so now build the command line.
  997.          */
  998.         if (is_exe || is_binary) {
  999.             pCommand = ap_pstrcat(r->pool, quoted_filename, " ", 
  1000.                       arguments, NULL);
  1001.         }
  1002.         else if (is_script) {
  1003.             pCommand = ap_pstrcat(r->pool, interpreter + 2, " ", 
  1004.                       quoted_filename, " ", arguments, NULL);
  1005.         }
  1006.         else {
  1007.             pCommand = ap_pstrcat(r->pool, SHELL_PATH, " /C ", 
  1008.                       quoted_filename, " ", arguments, NULL);
  1009.         }
  1010.     }
  1011.  
  1012.     /*
  1013.      * Make child process use hPipeOutputWrite as standard out,
  1014.      * and make sure it does not show on screen.
  1015.      */
  1016.     si.cb = sizeof(si);
  1017.     si.dwFlags     = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
  1018.     si.wShowWindow = SW_HIDE;
  1019.     si.hStdInput   = pinfo->hPipeInputRead;
  1020.     si.hStdOutput  = pinfo->hPipeOutputWrite;
  1021.     si.hStdError   = pinfo->hPipeErrorWrite;
  1022.   
  1023.         /*
  1024.          * Win32's CreateProcess call requires that the environment
  1025.          * be passed in an environment block, a null terminated block of
  1026.          * null terminated strings.
  1027.          */  
  1028.         i = 0;
  1029.         iEnvBlockLen = 1;
  1030.         while (env[i]) {
  1031.             iEnvBlockLen += strlen(env[i]) + 1;
  1032.             i++;
  1033.         }
  1034.   
  1035.         pEnvBlock = (char *)ap_pcalloc(r->pool,iEnvBlockLen);
  1036.     
  1037.         i = 0;
  1038.         pNext = pEnvBlock;
  1039.         while (env[i]) {
  1040.             strcpy(pNext, env[i]);
  1041.             pNext = pNext + strlen(pNext) + 1;
  1042.             i++;
  1043.         }
  1044.  
  1045.         if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, pEnvBlock,
  1046.                           ap_make_dirstr_parent(r->pool, r->filename),
  1047.                           &si, &pi)) {
  1048.             pid = pi.dwProcessId;
  1049.             /*
  1050.              * We must close the handles to the new process and its main thread
  1051.              * to prevent handle and memory leaks.
  1052.              */ 
  1053.             CloseHandle(pi.hProcess);
  1054.             CloseHandle(pi.hThread);
  1055.         } else {
  1056.         if (is_script) {
  1057.         /* since we are doing magic to find what we are executing
  1058.          * if running a script, log what we think we should have
  1059.          * executed
  1060.          */
  1061.         ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_WIN32ERROR, r,
  1062.                  "could not run script interpreter: %s", pCommand);
  1063.         }
  1064.     }
  1065. #if 0
  1066.     if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
  1067.         if (is_exe || is_binary) {
  1068.         pid = spawnle(_P_NOWAIT, r->filename, r->filename, NULL, env);
  1069.         }
  1070.         else if (is_script) {
  1071.         pid = spawnle(_P_NOWAIT, interpreter + 2, interpreter + 2,
  1072.                   r->filename, NULL, env);
  1073.         }
  1074.         else {
  1075.         pid = spawnle(_P_NOWAIT, SHELL_PATH, SHELL_PATH, "/C",
  1076.                   r->filename, NULL, env);
  1077.         }
  1078.     }
  1079.     else {
  1080.         if (is_exe || is_binary) {
  1081.         pid = spawnve(_P_NOWAIT, r->filename,
  1082.                   create_argv(r->pool, NULL, NULL, NULL, argv0, 
  1083.                       r->args), env);
  1084.         }
  1085.         else if (is_script) {
  1086.         pid = spawnve(_P_NOWAIT, interpreter + 2,
  1087.                   create_argv(r->pool, interpreter + 2, NULL, NULL,
  1088.                       r->filename, r->args), env);
  1089.         }
  1090.         else {
  1091.         pid = spawnve(_P_NOWAIT, SHELL_PATH,
  1092.                   create_argv_cmd(r->pool, argv0, r->args,
  1093.                           r->filename), env);
  1094.         }
  1095.     }
  1096. #endif
  1097.     return (pid);
  1098.     }
  1099. #else
  1100.     if (ap_suexec_enabled
  1101.     && ((r->server->server_uid != ap_user_id)
  1102.         || (r->server->server_gid != ap_group_id)
  1103.         || (!strncmp("/~", r->uri, 2)))) {
  1104.  
  1105.     char *execuser, *grpname;
  1106.     struct passwd *pw;
  1107.     struct group *gr;
  1108.  
  1109.     if (!strncmp("/~", r->uri, 2)) {
  1110.         gid_t user_gid;
  1111.         char *username = ap_pstrdup(r->pool, r->uri + 2);
  1112.         char *pos = strchr(username, '/');
  1113.  
  1114.         if (pos) {
  1115.         *pos = '\0';
  1116.         }
  1117.  
  1118.         if ((pw = getpwnam(username)) == NULL) {
  1119.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1120.                  "getpwnam: invalid username %s", username);
  1121.         return (pid);
  1122.         }
  1123.         execuser = ap_pstrcat(r->pool, "~", pw->pw_name, NULL);
  1124.         user_gid = pw->pw_gid;
  1125.  
  1126.         if ((gr = getgrgid(user_gid)) == NULL) {
  1127.             if ((grpname = ap_palloc(r->pool, 16)) == NULL) {
  1128.             return (pid);
  1129.         }
  1130.         else {
  1131.             ap_snprintf(grpname, 16, "%ld", (long) user_gid);
  1132.         }
  1133.         }
  1134.         else {
  1135.         grpname = gr->gr_name;
  1136.         }
  1137.     }
  1138.     else {
  1139.         if ((pw = getpwuid(r->server->server_uid)) == NULL) {
  1140.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1141.                  "getpwuid: invalid userid %ld",
  1142.                  (long) r->server->server_uid);
  1143.         return (pid);
  1144.         }
  1145.         execuser = ap_pstrdup(r->pool, pw->pw_name);
  1146.  
  1147.         if ((gr = getgrgid(r->server->server_gid)) == NULL) {
  1148.         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1149.                  "getgrgid: invalid groupid %ld",
  1150.                  (long) r->server->server_gid);
  1151.         return (pid);
  1152.         }
  1153.         grpname = gr->gr_name;
  1154.     }
  1155.  
  1156.     if (shellcmd) {
  1157.         execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
  1158.            NULL, env);
  1159.     }
  1160.  
  1161.     else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
  1162.         execle(SUEXEC_BIN, SUEXEC_BIN, execuser, grpname, argv0,
  1163.            NULL, env);
  1164.     }
  1165.  
  1166.     else {
  1167.         execve(SUEXEC_BIN,
  1168.            create_argv(r->pool, SUEXEC_BIN, execuser, grpname,
  1169.                    argv0, r->args),
  1170.            env);
  1171.     }
  1172.     }
  1173.     else {
  1174.         if (shellcmd) {
  1175.         execle(SHELL_PATH, SHELL_PATH, "-c", argv0, NULL, env);
  1176.     }
  1177.  
  1178.     else if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) {
  1179.         execle(r->filename, argv0, NULL, env);
  1180.     }
  1181.  
  1182.     else {
  1183.         execve(r->filename,
  1184.            create_argv(r->pool, NULL, NULL, NULL, argv0, r->args),
  1185.            env);
  1186.     }
  1187.     }
  1188.     return (pid);
  1189. #endif
  1190. }
  1191.