]> granicus.if.org Git - apache/commitdiff
Move the logic for creating CGI processes from mod_include to mod_cgi(d).
authorRyan Bloom <rbb@apache.org>
Mon, 5 Feb 2001 22:57:21 +0000 (22:57 +0000)
committerRyan Bloom <rbb@apache.org>
Mon, 5 Feb 2001 22:57:21 +0000 (22:57 +0000)
This removes a good deal of duplicate logic for creating CGI scripts.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@87984 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
modules/filters/mod_include.c
modules/filters/mod_include.h
modules/generators/mod_cgi.c
modules/generators/mod_cgid.c

diff --git a/CHANGES b/CHANGES
index 674fe385afb4e83ec8c8cc79fcef467ede795e49..dd2b5bafc073f339179d44e910b5a4d1beef2651 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
 Changes with Apache 2.0b1
 
+  *) Move the CGI creation logic from mod_include to mod_cgi(d).  This
+     should reduce the amount of duplicate code that is required to
+     create CGI processes.
+     [Paul J. Reder <rederpj@raleigh.ibm.com>]
+
   *) ap_new_connection() closes the socket and returns NULL if a socket
      call fails.  Usually this is due to a connection which has been 
      reset.  [Jeff Trawick]
index 6aeef3da82d99e08699d0d9bdfb60cad5ebeed69..fb40cb8c18af037f64d0dcf4666cb196f6a0ee65 100644 (file)
@@ -83,6 +83,7 @@
 #include "http_main.h"
 #include "util_script.h"
 #include "http_core.h"
+#include "apr_optional.h"
 #include "mod_include.h"
 #ifdef HAVE_STRING_H
 #include <string.h>
@@ -94,6 +95,8 @@
 
 
 static apr_hash_t *include_hash;
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
+
 
 /* ------------------------ Environment function -------------------------- */
 
@@ -496,7 +499,7 @@ otilde\365oslash\370ugrave\371uacute\372yacute\375"     /* 6 */
 
 #define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++
 
-static void get_tag_and_value(include_ctx_t *ctx, char **tag,
+static void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
                               char **tag_val, int dodecode)
 {
     char *c = ctx->curr_tag_pos;
@@ -574,8 +577,8 @@ static void get_tag_and_value(include_ctx_t *ctx, char **tag,
 /*
  * Do variable substitution on strings
  */
-static void parse_string(request_rec *r, const char *in, char *out,
-                       size_t length, int leave_name)
+static void ap_ssi_parse_string(request_rec *r, const char *in, char *out,
+                         size_t length, int leave_name)
 {
     char ch;
     char *next = out;
@@ -601,7 +604,6 @@ static void parse_string(request_rec *r, const char *in, char *out,
             break;
         case '$':
             {
-/* pjr hack     char var[MAX_STRING_LEN]; */
                const char *start_of_var_name;
                char *end_of_var_name;  /* end of var name + 1 */
                const char *expansion, *temp_end, *val;
@@ -637,17 +639,10 @@ static void parse_string(request_rec *r, const char *in, char *out,
                 * pass a non-nul terminated string */
                l = end_of_var_name - start_of_var_name;
                if (l != 0) {
-/* pjr - this is a test hack to avoid a memcpy. Make sure that this works...
-*                  l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
-*                  memcpy(var, start_of_var_name, l);
-*                  var[l] = '\0';
-*
-*                  val = apr_table_get(r->subprocess_env, var);
-*/
-/* pjr hack */      tmp_store        = *end_of_var_name;
-/* pjr hack */      *end_of_var_name = '\0';
-/* pjr hack */      val = apr_table_get(r->subprocess_env, start_of_var_name);
-/* pjr hack */      *end_of_var_name = tmp_store;
+                    tmp_store        = *end_of_var_name;
+                    *end_of_var_name = '\0';
+                    val = apr_table_get(r->subprocess_env, start_of_var_name);
+                    *end_of_var_name = tmp_store;
 
                    if (val) {
                        expansion = val;
@@ -685,68 +680,6 @@ static void parse_string(request_rec *r, const char *in, char *out,
 
 /* --------------------------- Action handlers ---------------------------- */
 
-static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
-                       apr_bucket *head_ptr, apr_bucket **inserted_head)
-{
-    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
-    int rr_status;
-    apr_bucket  *tmp_buck, *tmp2_buck;
-
-    if (rr->status != HTTP_OK) {
-        return -1;
-    }
-
-    /* No hardwired path info or query allowed */
-
-    if ((rr->path_info && rr->path_info[0]) || rr->args) {
-        return -1;
-    }
-    if (rr->finfo.filetype == 0) {
-        return -1;
-    }
-
-    /* Script gets parameters of the *document*, for back compatibility */
-
-    rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
-    rr->args = r->args;
-
-    /* Force sub_req to be treated as a CGI request, even if ordinary
-     * typing rules would have called it something else.
-     */
-
-    rr->content_type = CGI_MAGIC_TYPE;
-
-    /* Run it. */
-
-    rr_status = ap_run_sub_req(rr);
-    if (ap_is_HTTP_REDIRECT(rr_status)) {
-        apr_size_t len_loc, h_wrt;
-        const char *location = apr_table_get(rr->headers_out, "Location");
-
-        location = ap_escape_html(rr->pool, location);
-        len_loc = strlen(location);
-
-        tmp_buck = apr_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
-        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_immortal("\">", sizeof("\">"));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = apr_bucket_create_immortal("</A>", sizeof("</A>"));
-        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-
-        if (*inserted_head == NULL) {
-            *inserted_head = tmp_buck;
-        }
-    }
-
-    ap_destroy_sub_req(rr);
-
-    return 0;
-}
-
 /* ensure that path is relative, and does not contain ".." elements
  * ensentially ensure that it does not match the regex:
  * (^/|(^|/)\.\.(/|$))
@@ -798,7 +731,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return (0);
@@ -811,7 +744,7 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
                 request_rec *rr = NULL;
                 char *error_fmt = NULL;
 
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (tag[0] == 'f') {
                     /* be safe; only files in this directory or below allowed */
                if (!is_only_below(parsed_string)) {
@@ -884,10 +817,9 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
                ap_set_module_config(rr->request_config, &includes_module, r);
 
                 if (!error_fmt) {
-                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-/*
-                    rr->output_filters = f->next;
-*/                    if (ap_run_sub_req(rr)) {
+                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+                    
+                    if (ap_run_sub_req(rr)) {
                         error_fmt = "unable to include \"%s\" in parsed file %s";
                     }
                 }
@@ -915,213 +847,6 @@ static int handle_include(include_ctx_t *ctx, apr_bucket_brigade **bb, request_r
     return 0;
 }
 
-typedef struct {
-#ifdef TPF
-    TPF_FORK_CHILD t;
-#endif
-    request_rec *r;
-    char *s;
-} include_cmd_arg;
-
-
-
-static apr_status_t build_argv_list(char ***argv, request_rec *r, apr_pool_t *p)
-{
-    int numwords, x, idx;
-    char *w;
-    const char *args = r->args;
-
-    if (!args || !args[0] || ap_strchr_c(args, '=')) {
-       numwords = 1;
-    }
-    else {
-        /* count the number of keywords */
-        for (x = 0, numwords = 1; args[x]; x++) {
-            if (args[x] == '+') {
-                ++numwords;
-            }
-        }
-    }
-    /* Everything is - 1 to account for the first parameter which is the
-     * program name.  We didn't used to have to do this, but APR wants it.
-     */
-    if (numwords > APACHE_ARG_MAX - 1) {
-        numwords = APACHE_ARG_MAX - 1; /* Truncate args to prevent overrun */
-    }
-    *argv = (char **) apr_palloc(p, (numwords + 2) * sizeof(char *));
-    for (x = 1, idx = 1; x < numwords; x++) {
-        w = ap_getword_nulls(p, &args, '+');
-        ap_unescape_url(w);
-        (*argv)[idx++] = ap_escape_shell_cmd(p, w);
-    }
-    (*argv)[idx] = NULL;
-
-    return APR_SUCCESS;
-}
-
-
-
-static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *s,
-                       request_rec *r, ap_filter_t *f)
-{
-    include_cmd_arg arg;
-    apr_procattr_t *procattr;
-    apr_proc_t *procnew;
-    apr_status_t rc;
-    apr_table_t *env = r->subprocess_env;
-    char **argv;
-    apr_file_t *file = NULL;
-#if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
-    defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
-    core_dir_config *conf; 
-    conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,
-                                                    &core_module);
-#endif
-
-    arg.r = r;
-    arg.s = s;
-#ifdef TPF
-    arg.t.filename = r->filename;
-    arg.t.subprocess_env = r->subprocess_env;
-    arg.t.prog_type = FORK_FILE;
-#endif
-
-    if (r->path_info && r->path_info[0] != '\0') {
-        request_rec *pa_req;
-
-        apr_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
-
-        pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, f->next);
-        if (pa_req->filename) {
-            apr_table_setn(env, "PATH_TRANSLATED",
-                      apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
-                              NULL));
-        }
-    }
-
-    if (r->args) {
-        char *arg_copy = apr_pstrdup(r->pool, r->args);
-
-        apr_table_setn(env, "QUERY_STRING", r->args);
-        ap_unescape_url(arg_copy);
-        apr_table_setn(env, "QUERY_STRING_UNESCAPED",
-                  ap_escape_shell_cmd(r->pool, arg_copy));
-    }
-
-    if (((rc = apr_createprocattr_init(&procattr, r->pool)) != APR_SUCCESS) ||
-        ((rc = apr_setprocattr_io(procattr, APR_NO_PIPE, 
-                                  APR_FULL_BLOCK, APR_NO_PIPE)) != APR_SUCCESS) ||
-        ((rc = apr_setprocattr_dir(procattr, ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) ||
-#ifdef RLIMIT_CPU
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_CPU, conf->limit_cpu)) != APR_SUCCESS) ||
-#endif
-#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_MEM, conf->limit_mem)) != APR_SUCCESS) ||
-#endif
-#ifdef RLIMIT_NPROC
-        ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) ||
-#endif
-        ((rc = apr_setprocattr_cmdtype(procattr, APR_SHELLCMD)) != APR_SUCCESS)) {
-        /* Something bad happened, tell the world. */
-       ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
-            "couldn't initialize proc attributes: %s %s", r->filename, s);
-        rc = !APR_SUCCESS;
-    }
-    else {
-        SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-        build_argv_list(&argv, r, r->pool);
-        argv[0] = apr_pstrdup(r->pool, s);
-        procnew = apr_pcalloc(r->pool, sizeof(*procnew));
-        rc = apr_create_process(procnew, s, (const char * const *) argv,
-                                (const char * const *)ap_create_environment(r->pool, env),
-                                procattr, r->pool);
-
-        if (rc != APR_SUCCESS) {
-            /* Bad things happened. Everyone should have cleaned up. */
-            ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
-                        "couldn't create child process: %d: %s", rc, s);
-        }
-        else {
-            apr_bucket_brigade *bcgi;
-            apr_bucket *b;
-
-            apr_note_subprocess(r->pool, procnew, kill_after_timeout);
-            /* Fill in BUFF structure for parents pipe to child's stdout */
-            file = procnew->out;
-            if (!file)
-                return APR_EBADF;
-            bcgi = apr_brigade_create(r->pool);
-            b = apr_bucket_create_pipe(file);
-            APR_BRIGADE_INSERT_TAIL(bcgi, b);
-            ap_pass_brigade(f->next, bcgi);
-        
-            /* We can't close the pipe here, because we may return before the
-             * full CGI has been sent to the network.  That's okay though,
-             * because we can rely on the pool to close the pipe for us.
-             */
-        }
-    }
-
-    return 0;
-}
-
-static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
-                       ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
-{
-    char *tag     = NULL;
-    char *tag_val = NULL;
-    char *file = r->filename;
-    apr_bucket  *tmp_buck;
-    char parsed_string[MAX_STRING_LEN];
-
-    *inserted_head = NULL;
-    if (ctx->flags & FLAG_PRINTING) {
-        if (ctx->flags & FLAG_NO_EXEC) {
-            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                      "exec used but not allowed in %s", r->filename);
-            CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-        }
-        else {
-            while (1) {
-                get_tag_and_value(ctx, &tag, &tag_val, 1);
-                if (tag_val == NULL) {
-                    if (tag == NULL) {
-                        return (0);
-                    }
-                    else {
-                        return 1;
-                    }
-                }
-                if (!strcmp(tag, "cmd")) {
-                    parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
-                    if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
-                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                    "execution failure for parameter \"%s\" "
-                                    "to tag exec in file %s", tag, r->filename);
-                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                    }
-                    /* just in case some stooge changed directories */
-                }
-                else if (!strcmp(tag, "cgi")) {
-                    parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
-                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx);
-                    if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) {
-                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                    "invalid CGI ref \"%s\" in %s", tag_val, file);
-                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                    }
-                }
-                else {
-                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
-                                "unknown parameter \"%s\" to tag exec in %s", tag, file);
-                    CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
-                }
-            }
-        }
-    }
-    return 0;
-}
 
 static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
                        ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
@@ -1138,7 +863,7 @@ static int handle_echo(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag != NULL) {
                     return 1;
@@ -1206,7 +931,7 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;  /* Reached the end of the string. */
@@ -1216,20 +941,20 @@ static int handle_config(include_ctx_t *ctx, apr_bucket_brigade **bb, request_re
                 }
             }
             if (!strcmp(tag, "errmsg")) {
-                parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0);
+                ap_ssi_parse_string(r, tag_val, ctx->error_str, MAX_STRING_LEN, 0);
                 ctx->error_length = strlen(ctx->error_str);
             }
             else if (!strcmp(tag, "timefmt")) {
                 apr_time_t date = r->request_time;
 
-                parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0);
+                ap_ssi_parse_string(r, tag_val, ctx->time_str, MAX_STRING_LEN, 0);
                 apr_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, ctx->time_str, 0));
                 apr_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, ctx->time_str, 1));
                 apr_table_setn(env, "LAST_MODIFIED",
                                ap_ht_time(r->pool, r->finfo.mtime, ctx->time_str, 0));
             }
             else if (!strcmp(tag, "sizefmt")) {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 decodehtml(parsed_string);
                 if (!strcmp(parsed_string, "bytes")) {
                     ctx->flags |= FLAG_SIZE_IN_BYTES;
@@ -1367,7 +1092,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;
@@ -1377,7 +1102,7 @@ static int handle_fsize(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
                 }
             }
             else {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (!find_file(r, "fsize", tag, parsed_string, &finfo)) {
                     char buff[50];
 
@@ -1429,7 +1154,7 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if (tag_val == NULL) {
                 if (tag == NULL) {
                     return 0;
@@ -1439,7 +1164,7 @@ static int handle_flastmod(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
                 }
             }
             else {
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 if (!find_file(r, "flastmod", tag, parsed_string, &finfo)) {
                     char *t_val;
 
@@ -1981,7 +1706,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                     sizeof ("     Evaluate string\n"));
             debug_pos += sizeof ("     Evaluate string\n");
 #endif
-            parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
+            ap_ssi_parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
            apr_cpystrn(current->token.value, buffer, sizeof(current->token.value));
             current->value = (current->token.value[0] != '\0');
             current->done = 1;
@@ -2006,7 +1731,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
             if (!current->left->done) {
                 switch (current->left->token.type) {
                 case token_string:
-                    parse_string(r, current->left->token.value,
+                    ap_ssi_parse_string(r, current->left->token.value,
                                  buffer, sizeof(buffer), 0);
                     apr_cpystrn(current->left->token.value, buffer,
                             sizeof(current->left->token.value));
@@ -2021,7 +1746,7 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
             if (!current->right->done) {
                 switch (current->right->token.type) {
                 case token_string:
-                    parse_string(r, current->right->token.value,
+                    ap_ssi_parse_string(r, current->right->token.value,
                                  buffer, sizeof(buffer), 0);
                     apr_cpystrn(current->right->token.value, buffer,
                             sizeof(current->right->token.value));
@@ -2070,11 +1795,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                 *was_error = 1;
                 goto RETURN;
             }
-            parse_string(r, current->left->token.value,
+            ap_ssi_parse_string(r, current->left->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->left->token.value, buffer,
                        sizeof(current->left->token.value));
-            parse_string(r, current->right->token.value,
+            ap_ssi_parse_string(r, current->right->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->right->token.value, buffer,
                        sizeof(current->right->token.value));
@@ -2141,11 +1866,11 @@ static int parse_expr(request_rec *r, const char *expr, int *was_error,
                 *was_error = 1;
                 goto RETURN;
             }
-            parse_string(r, current->left->token.value,
+            ap_ssi_parse_string(r, current->left->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->left->token.value, buffer,
                        sizeof(current->left->token.value));
-            parse_string(r, current->right->token.value,
+            ap_ssi_parse_string(r, current->right->token.value,
                          buffer, sizeof(buffer), 0);
             apr_cpystrn(current->right->token.value, buffer,
                        sizeof(current->right->token.value));
@@ -2307,7 +2032,7 @@ static int handle_if(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r
     }
     else {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag == NULL) {
                 if (expr == NULL) {
                     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
@@ -2375,7 +2100,7 @@ static int handle_elif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 0);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 0);
             if (tag == '\0') {
                 LOG_COND_STATUS(ctx, tmp_buck, head_ptr, *inserted_head, " elif");
                 
@@ -2443,7 +2168,7 @@ static int handle_else(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
 
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag != NULL) || (tag_val != NULL)) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                         "else directive does not take tags in %s", r->filename);
@@ -2476,7 +2201,7 @@ static int handle_endif(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec
 
     *inserted_head = NULL;
     if (!ctx->if_nesting_level) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag != NULL) || (tag_val != NULL)) {
             ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
                         "endif directive does not take tags in %s", r->filename);
@@ -2507,7 +2232,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *
     *inserted_head = NULL;
     if (ctx->flags & FLAG_PRINTING) {
         while (1) {
-            get_tag_and_value(ctx, &tag, &tag_val, 1);
+            ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
             if ((tag == NULL) && (tag_val == NULL)) {
                 return 0;
             }
@@ -2525,7 +2250,7 @@ static int handle_set(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *
                     CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
                     return (-1);
                 }
-                parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                ap_ssi_parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
                 apr_table_setn(r->subprocess_env, apr_pstrdup(r->pool, var),
                                apr_pstrdup(r->pool, parsed_string));
             }
@@ -2548,7 +2273,7 @@ static int handle_printenv(include_ctx_t *ctx, apr_bucket_brigade **bb, request_
     apr_bucket *tmp_buck;
 
     if (ctx->flags & FLAG_PRINTING) {
-        get_tag_and_value(ctx, &tag, &tag_val, 1);
+        ap_ssi_get_tag_and_value(ctx, &tag, &tag_val, 1);
         if ((tag == NULL) && (tag_val == NULL)) {
             apr_array_header_t *arr = apr_table_elts(r->subprocess_env);
             apr_table_entry_t *elts = (apr_table_entry_t *)arr->elts;
@@ -3016,7 +2741,7 @@ static int includes_filter(ap_filter_t *f, apr_bucket_brigade *b)
     return OK;
 }
 
-void ap_register_include_handler(char *tag, handler func)
+static void ap_register_include_handler(char *tag, include_handler func)
 {
     apr_hash_set(include_hash, tag, strlen(tag) + 1, (const void *)func);
 }
@@ -3026,18 +2751,21 @@ static void include_post_config(apr_pool_t *p, apr_pool_t *plog,
 {
     include_hash = apr_make_hash(p);
 
-    ap_register_include_handler("if", handle_if);
-    ap_register_include_handler("set", handle_set);
-    ap_register_include_handler("else", handle_else);
-    ap_register_include_handler("elif", handle_elif);
-    ap_register_include_handler("exec", handle_exec);
-    ap_register_include_handler("echo", handle_echo);
-    ap_register_include_handler("endif", handle_endif);
-    ap_register_include_handler("fsize", handle_fsize);
-    ap_register_include_handler("config", handle_config);
-    ap_register_include_handler("include", handle_include);
-    ap_register_include_handler("flastmod", handle_flastmod);
-    ap_register_include_handler("printenv", handle_printenv);
+    ssi_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
+
+    if(ssi_pfn_register) {
+        ssi_pfn_register("if", handle_if);
+        ssi_pfn_register("set", handle_set);
+        ssi_pfn_register("else", handle_else);
+        ssi_pfn_register("elif", handle_elif);
+        ssi_pfn_register("echo", handle_echo);
+        ssi_pfn_register("endif", handle_endif);
+        ssi_pfn_register("fsize", handle_fsize);
+        ssi_pfn_register("config", handle_config);
+        ssi_pfn_register("include", handle_include);
+        ssi_pfn_register("flastmod", handle_flastmod);
+        ssi_pfn_register("printenv", handle_printenv);
+    }
 }
 
 /*
@@ -3052,6 +2780,9 @@ static const command_rec includes_cmds[] =
 
 static void register_hooks(apr_pool_t *p)
 {
+    APR_REGISTER_OPTIONAL_FN(ap_ssi_get_tag_and_value);
+    APR_REGISTER_OPTIONAL_FN(ap_ssi_parse_string);
+    APR_REGISTER_OPTIONAL_FN(ap_register_include_handler);
     ap_hook_post_config(include_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
     ap_register_output_filter("INCLUDES", includes_filter, AP_FTYPE_CONTENT);
 }
index e44fffffe6c71a203390211960c39805d9f484ea..a682201449085e4628b89457e92a62c0ae8a42e9 100644 (file)
@@ -59,8 +59,6 @@
 #ifndef _MOD_INCLUDE_H
 #define _MOD_INCLUDE_H 1
 
-
-
 #define STARTING_SEQUENCE "<!--#"
 #define ENDING_SEQUENCE "-->"
 
@@ -177,29 +175,40 @@ typedef struct include_filter_ctx {
 #define CREATE_ERROR_BUCKET(cntx, t_buck, h_ptr, ins_head)        \
 {                                                                 \
     apr_size_t e_wrt;                                             \
-    t_buck = apr_bucket_create_heap(cntx->error_str,               \
+    t_buck = apr_bucket_create_heap(cntx->error_str,              \
                                    ctx->error_length, 1, &e_wrt); \
-    APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                       \
+    APR_BUCKET_INSERT_BEFORE(h_ptr, t_buck);                      \
                                                                   \
     if (ins_head == NULL) {                                       \
         ins_head = t_buck;                                        \
     }                                                             \
 }
 
-#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt)               \
+#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt, next)          \
 if ((APR_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) &&                \
-    (cntxt->head_start_bucket != NULL)) {                        \
+    (cntxt->head_start_bucket != NULL)) {                         \
     apr_bucket_brigade *tag_plus;                                 \
-                                                                 \
+                                                                  \
     tag_plus = apr_brigade_split(brgd, cntxt->head_start_bucket); \
-    ap_pass_brigade(f->next, brgd);                              \
-    brgd = tag_plus;                                             \
+    ap_pass_brigade(next, brgd);                                  \
+    brgd = tag_plus;                                              \
 }
 
-typedef int (*handler)(include_ctx_t *ctx, apr_bucket_brigade **bb, 
+
+typedef int (*include_handler)(include_ctx_t *ctx, apr_bucket_brigade **bb, 
                        request_rec *r, ap_filter_t *f, apr_bucket *head_ptr, 
                        apr_bucket **inserted_head);
 
-void ap_register_include_handler(char *tag, handler func);
+APR_DECLARE_OPTIONAL_FN(void, ap_ssi_get_tag_and_value, (include_ctx_t *ctx,
+                                                        char **tag,
+                                                        char **tag_val,
+                                                        int dodecode))
+APR_DECLARE_OPTIONAL_FN(void, ap_ssi_parse_string, (request_rec *r,
+                                                    const char *in,
+                                                    char *out,
+                                                    size_t length,
+                                                    int leave_name))
+APR_DECLARE_OPTIONAL_FN(void, ap_register_include_handler, (char *tag,
+                                                         include_handler func))
 
 #endif /* MOD_INCLUDE */
index 4e0767d09d8b56f4d52fa9fca24365a0b7d6a1fd..3d9cb309658fc0a881ec34b8a1e16ef9ec8a22fd 100644 (file)
@@ -88,6 +88,8 @@
 #include "util_script.h"
 #include "ap_mpm.h"
 #include "http_conf_globals.h"
+#include "apr_optional.h"
+#include "../filters/mod_include.h"
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
 
 module AP_MODULE_DECLARE_DATA cgi_module;
 
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgi_pfn_reg_with_ssi;
+static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgi_pfn_gtv;
+static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgi_pfn_ps;
+
+typedef enum {RUN_AS_SSI, RUN_AS_CGI} prog_types;
+
+typedef struct {
+    apr_int32_t          in_pipe;
+    apr_int32_t          out_pipe;
+    apr_int32_t          err_pipe;
+    apr_cmdtype_e        cmd_type;
+    prog_types           prog_type;
+    apr_bucket_brigade **bb;
+    include_ctx_t       *ctx;
+    ap_filter_t         *next;
+} exec_info;
+
 /* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
  * in ScriptAliased directories, which means we need to know if this
  * request came through ScriptAlias or not... so the Alias module
@@ -313,21 +332,52 @@ static int log_script(request_rec *r, cgi_server_conf * conf, int ret,
     return ret;
 }
 
+
+/* This is the special environment used for running the "exec cmd="
+ *   variety of SSI directives.
+ */
+static void add_ssi_vars(request_rec *r, ap_filter_t *next)
+{
+    apr_table_t *e = r->subprocess_env;
+
+    if (r->path_info && r->path_info[0] != '\0') {
+        request_rec *pa_req;
+
+        apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
+
+        pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, next);
+        if (pa_req->filename) {
+            apr_table_setn(e, "PATH_TRANSLATED",
+                           apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL));
+        }
+    }
+
+    if (r->args) {
+        char *arg_copy = apr_pstrdup(r->pool, r->args);
+
+        apr_table_setn(e, "QUERY_STRING", r->args);
+        ap_unescape_url(arg_copy);
+        apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy));
+    }
+}
+
 static apr_status_t run_cgi_child(apr_file_t **script_out,
                                   apr_file_t **script_in,
                                   apr_file_t **script_err, 
                                   const char *command,
                                   const char * const argv[],
-                                  request_rec *r, apr_pool_t *p)
+                                  request_rec *r,
+                                  apr_pool_t *p,
+                                  exec_info *e_info)
 {
     const char * const *env;
     apr_procattr_t *procattr;
-    apr_proc_t *procnew = apr_pcalloc(p, sizeof(*procnew));
+    apr_proc_t *procnew;
     apr_status_t rc = APR_SUCCESS;
 #if defined(RLIMIT_CPU)  || defined(RLIMIT_NPROC) || \
     defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined (RLIMIT_AS)
     core_dir_config *conf;
-    conf = (core_dir_config *) ap_get_module_config(r->per_dir_config,                                                              &core_module);
+    conf = (core_dir_config *) ap_get_module_config(r->per_dir_config, &core_module);
 #endif
 
 
@@ -347,7 +397,13 @@ static apr_status_t run_cgi_child(apr_file_t **script_out,
            r->filename, cld->nph ? "NPH " : "", argv0);
 #endif
 
-    ap_add_cgi_vars(r);
+    if (e_info->prog_type == RUN_AS_CGI) {
+        ap_add_cgi_vars(r);
+    }
+    else /* SSIs want a controlled environment and a special path. */
+    {
+        add_ssi_vars(r, e_info->next);
+    }
     env = (const char * const *)ap_create_environment(p, r->subprocess_env);
 
 #ifdef DEBUG_CGI
@@ -360,10 +416,10 @@ static apr_status_t run_cgi_child(apr_file_t **script_out,
      * NB only ISINDEX scripts get decoded arguments.
      */
     if (((rc = apr_createprocattr_init(&procattr, p)) != APR_SUCCESS) ||
-        ((rc = apr_setprocattr_io(procattr, 
-                                 APR_CHILD_BLOCK, 
-                                 APR_CHILD_BLOCK,
-                                 APR_CHILD_BLOCK)) != APR_SUCCESS) ||
+        ((rc = apr_setprocattr_io(procattr,
+                                  e_info->in_pipe,
+                                  e_info->out_pipe,
+                                  e_info->err_pipe)) != APR_SUCCESS) ||
         ((rc = apr_setprocattr_dir(procattr, 
                                   ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) ||
 #ifdef RLIMIT_CPU
@@ -375,12 +431,17 @@ static apr_status_t run_cgi_child(apr_file_t **script_out,
 #ifdef RLIMIT_NPROC
         ((rc = apr_setprocattr_limit(procattr, APR_LIMIT_NPROC, conf->limit_nproc)) != APR_SUCCESS) ||
 #endif
-        ((rc = apr_setprocattr_cmdtype(procattr, APR_PROGRAM)) != APR_SUCCESS)) {
+        ((rc = apr_setprocattr_cmdtype(procattr, e_info->cmd_type)) != APR_SUCCESS)) {
         /* Something bad happened, tell the world. */
        ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
                      "couldn't set child process attributes: %s", r->filename);
     }
     else {
+        procnew = apr_pcalloc(p, sizeof(*procnew));
+        if (e_info->prog_type == RUN_AS_SSI) {
+            SPLIT_AND_PASS_PRETAG_BUCKETS(*(e_info->bb), e_info->ctx, e_info->next);
+        }
+
         rc = ap_os_create_privileged_process(r, procnew, command, argv, env, procattr, p);
     
         if (rc != APR_SUCCESS) {
@@ -394,20 +455,19 @@ static apr_status_t run_cgi_child(apr_file_t **script_out,
             *script_in = procnew->out;
             if (!script_in)
                 return APR_EBADF;
-            apr_set_pipe_timeout(*script_in, 
-                                 (int)(r->server->timeout * APR_USEC_PER_SEC));
-
-            *script_out = procnew->in;
-            if (!*script_out)
-                return APR_EBADF;
-            apr_set_pipe_timeout(*script_out, 
-                                 (int)(r->server->timeout * APR_USEC_PER_SEC));
-
-            *script_err = procnew->err;
-            if (!*script_err)
-                return APR_EBADF;
-            apr_set_pipe_timeout(*script_err, 
-                                 (int)(r->server->timeout * APR_USEC_PER_SEC));
+            apr_set_pipe_timeout(*script_in, (int)(r->server->timeout * APR_USEC_PER_SEC));
+
+            if (e_info->prog_type == RUN_AS_CGI) {
+                *script_out = procnew->in;
+                if (!*script_out)
+                    return APR_EBADF;
+                apr_set_pipe_timeout(*script_out, (int)(r->server->timeout * APR_USEC_PER_SEC));
+
+                *script_err = procnew->err;
+                if (!*script_err)
+                    return APR_EBADF;
+                apr_set_pipe_timeout(*script_err, (int)(r->server->timeout * APR_USEC_PER_SEC));
+            }
         }
     }
     return (rc);
@@ -516,6 +576,7 @@ static int cgi_handler(request_rec *r)
     apr_pool_t *p;
     cgi_server_conf *conf;
     apr_status_t rv;
+    exec_info e_info;
 
     if(strcmp(r->handler,CGI_MAGIC_TYPE) && strcmp(r->handler,"cgi-script"))
        return DECLINED;
@@ -600,9 +661,18 @@ static int cgi_handler(request_rec *r)
     }
     argv[0] = apr_pstrdup(p, command);
 
+    e_info.cmd_type  = APR_PROGRAM;
+    e_info.in_pipe   = APR_CHILD_BLOCK;
+    e_info.out_pipe  = APR_CHILD_BLOCK;
+    e_info.err_pipe  = APR_CHILD_BLOCK;
+    e_info.prog_type = RUN_AS_CGI;
+    e_info.bb        = NULL;
+    e_info.ctx       = NULL;
+    e_info.next      = NULL;
+
     /* run the script in its own process */
     if ((rv = run_cgi_child(&script_out, &script_in, &script_err,
-                            command, argv, r, p)) != APR_SUCCESS) {
+                            command, argv, r, p, &e_info)) != APR_SUCCESS) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
                       "couldn't spawn child process: %s", r->filename);
         return HTTP_INTERNAL_SERVER_ERROR;
@@ -723,9 +793,204 @@ static int cgi_handler(request_rec *r)
     return OK;                 /* NOT r->status, even if it has changed. */
 }
 
+/*============================================================================
+ *============================================================================
+ * This is the beginning of the cgi filter code moved from mod_include. This
+ *   is the code required to handle the "exec" SSI directive.
+ *============================================================================
+ *============================================================================*/
+static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
+                       apr_bucket *head_ptr, apr_bucket **inserted_head)
+{
+    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
+    int rr_status;
+    apr_bucket  *tmp_buck, *tmp2_buck;
+
+    if (rr->status != HTTP_OK) {
+        return -1;
+    }
+
+    /* No hardwired path info or query allowed */
+
+    if ((rr->path_info && rr->path_info[0]) || rr->args) {
+        return -1;
+    }
+    if (rr->finfo.protection == 0) {
+        return -1;
+    }
+
+    /* Script gets parameters of the *document*, for back compatibility */
+
+    rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
+    rr->args = r->args;
+
+    /* Force sub_req to be treated as a CGI request, even if ordinary
+     * typing rules would have called it something else.
+     */
+
+    rr->content_type = CGI_MAGIC_TYPE;
+
+    /* Run it. */
+
+    rr_status = ap_run_sub_req(rr);
+    if (ap_is_HTTP_REDIRECT(rr_status)) {
+        apr_size_t len_loc, h_wrt;
+        const char *location = apr_table_get(rr->headers_out, "Location");
+
+        location = ap_escape_html(rr->pool, location);
+        len_loc = strlen(location);
+
+        tmp_buck = apr_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
+        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_immortal("\">", sizeof("\">"));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_immortal("</A>", sizeof("</A>"));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+
+        if (*inserted_head == NULL) {
+            *inserted_head = tmp_buck;
+        }
+    }
+
+    ap_destroy_sub_req(rr);
+
+    return 0;
+}
+
+
+static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *command,
+                       request_rec *r, ap_filter_t *f)
+{
+    exec_info      e_info;
+    const char   **argv;
+    apr_file_t    *script_out = NULL, *script_in = NULL, *script_err = NULL;
+    apr_bucket_brigade *bcgi;
+    apr_bucket *b;
+
+    if (build_argv_list(&argv, r, r->pool) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
+                      "couldn't spawn cmd child process: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+    argv[0] = apr_pstrdup(r->pool, command);
+
+    e_info.cmd_type  = APR_SHELLCMD;
+    e_info.in_pipe   = APR_NO_PIPE;
+    e_info.out_pipe  = APR_FULL_BLOCK;
+    e_info.err_pipe  = APR_NO_PIPE;
+    e_info.prog_type = RUN_AS_SSI;
+    e_info.bb        = bb;
+    e_info.ctx       = ctx;
+    e_info.next      = f->next;
+
+    /* run the script in its own process */
+    if (run_cgi_child(&script_out, &script_in, &script_err,
+                      command, argv, r, r->pool, &e_info) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
+                      "couldn't spawn child process: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    bcgi = apr_brigade_create(r->pool);
+    b = apr_bucket_create_pipe(script_in);
+    APR_BRIGADE_INSERT_TAIL(bcgi, b);
+    ap_pass_brigade(f->next, bcgi);
+
+    /* We can't close the pipe here, because we may return before the
+     * full CGI has been sent to the network.  That's okay though,
+     * because we can rely on the pool to close the pipe for us.
+     */
+
+    return 0;
+}
+
+static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
+{
+    char *tag     = NULL;
+    char *tag_val = NULL;
+    char *file = r->filename;
+    apr_bucket  *tmp_buck;
+    char parsed_string[MAX_STRING_LEN];
+
+    *inserted_head = NULL;
+    if (ctx->flags & FLAG_PRINTING) {
+        if (ctx->flags & FLAG_NO_EXEC) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                      "exec used but not allowed in %s", r->filename);
+            CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+        }
+        else {
+            while (1) {
+                cgi_pfn_gtv(ctx, &tag, &tag_val, 1);
+                if (tag_val == NULL) {
+                    if (tag == NULL) {
+                        return (0);
+                    }
+                    else {
+                        return 1;
+                    }
+                }
+                if (!strcmp(tag, "cmd")) {
+                    cgi_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 1);
+                    if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
+                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                    "execution failure for parameter \"%s\" "
+                                    "to tag exec in file %s", tag, r->filename);
+                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                    }
+                }
+                else if (!strcmp(tag, "cgi")) {
+                    cgi_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+                    if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) {
+                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                    "invalid CGI ref \"%s\" in %s", tag_val, file);
+                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                    }
+                }
+                else {
+                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                "unknown parameter \"%s\" to tag exec in %s", tag, file);
+                    CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+
+/*============================================================================
+ *============================================================================
+ * This is the end of the cgi filter code moved from mod_include.
+ *============================================================================
+ *============================================================================*/
+
+
+static void cgi_post_config(apr_pool_t *p, apr_pool_t *plog,
+                                apr_pool_t *ptemp, server_rec *s)
+{
+    cgi_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
+    cgi_pfn_gtv          = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
+    cgi_pfn_ps           = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
+
+    if ((cgi_pfn_reg_with_ssi) && (cgi_pfn_gtv) && (cgi_pfn_ps)) {
+        /* Required by mod_include filter. This is how mod_cgi registers
+         *   with mod_include to provide processing of the exec directive.
+         */
+        cgi_pfn_reg_with_ssi("exec", handle_exec);
+    }
+}
+
 static void register_hooks(apr_pool_t *p)
 {
     ap_hook_handler(cgi_handler, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_config(cgi_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST);
 }
 
 module AP_MODULE_DECLARE_DATA cgi_module =
index a718f71a6589a889e5f96a860062ef60dba2c862..63e35a48a29c1c534a24bc622f178c2d2204f25d 100644 (file)
@@ -92,6 +92,8 @@
 #include "ap_mpm.h"
 #include "unixd.h"
 #include "mod_suexec.h"
+#include "apr_optional.h"
+#include "../filters/mod_include.h"
 #include <sys/stat.h>
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
 module AP_MODULE_DECLARE_DATA cgid_module; 
 
 static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *main_server); 
+static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head);
+
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *cgid_pfn_reg_with_ssi;
+static APR_OPTIONAL_FN_TYPE(ap_ssi_get_tag_and_value) *cgid_pfn_gtv;
+static APR_OPTIONAL_FN_TYPE(ap_ssi_parse_string) *cgid_pfn_ps;
 
 static apr_pool_t *pcgi; 
 static int total_modules = 0;
@@ -132,6 +140,9 @@ static int is_scriptaliased(request_rec *r)
 
 #define SHELL_PATH "/bin/sh"
 
+#define CGI_REQ 1
+#define SSI_REQ 2
+
 /* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's
  * pending connection queue.  If a bunch of cgi requests arrive at about
  * the same time, connections from httpd threads/processes will back up
@@ -228,7 +239,7 @@ static void cgid_maint(int reason, void *data, apr_wait_t status)
 #endif
 }
 
-static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env) 
+static void get_req(int fd, request_rec *r, char **filename, char **argv0, char ***env, int *req_type
 { 
     int i, len, j; 
     unsigned char *data; 
@@ -239,6 +250,7 @@ static void get_req(int fd, request_rec *r, char **filename, char **argv0, char
 
     r->server = apr_pcalloc(r->pool, sizeof(server_rec)); 
 
+    read(fd, req_type, sizeof(int));
     read(fd, &j, sizeof(int)); 
     read(fd, &len, sizeof(int)); 
     data = apr_pcalloc(r->pool, len + 1); /* get a cleared byte for final '\0' */
@@ -321,9 +333,9 @@ static void get_req(int fd, request_rec *r, char **filename, char **argv0, char
 
 
 
-static void send_req(int fd, request_rec *r, char *argv0, char **env) 
+static void send_req(int fd, request_rec *r, char *argv0, char **env, int req_type
 { 
-    int len; 
+    int len, r_type = req_type
     int i = 0; 
     char *data; 
     module *suexec_mod = ap_find_linked_module("mod_suexec.c");
@@ -335,6 +347,13 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env)
         continue; 
     } 
 
+    /* Write the request type (SSI "exec cmd" or cgi). */
+    if (write(fd, &r_type, sizeof(int)) < 0) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r,
+                     "write to cgi daemon process");
+    }
+
+    /* Write the number of entries in the environment. */
     if (write(fd, &i, sizeof(int)) < 0) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
                      "write to cgi daemon process"); 
@@ -345,18 +364,21 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env)
     } 
     data = apr_pstrcat(r->pool, data, r->args, NULL); 
     len = strlen(data); 
+    /* Write the length of the concatenated env string. */
     if (write(fd, &len, sizeof(int)) < 0) { 
         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
                      "write to cgi daemon process"); 
-        }     
+    }
+    /* Write the concatted env string. */     
     if (write(fd, data, len) < 0) {
         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
                      "write to cgi daemon process"); 
-        }     
+    }
+    /* Write module_index id value. */     
     if (write(fd, &core_module.module_index, sizeof(int)) < 0) { 
         ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, 
                      "write to cgi daemon process"); 
-        }     
+    }     
     if (suexec_mod) {
         suexec_config_t *suexec_cfg = ap_get_module_config(r->per_dir_config,
                                                            suexec_mod);
@@ -409,7 +431,7 @@ static void send_req(int fd, request_rec *r, char *argv0, char **env)
 static int cgid_server(void *data) 
 { 
     struct sockaddr_un unix_addr;
-    int sd, sd2, rc;
+    int sd, sd2, rc, req_type;
     mode_t omask;
     apr_socklen_t len;
     server_rec *main_server = data;
@@ -466,6 +488,10 @@ static int cgid_server(void *data)
         char *filename; 
         char **env; 
         const char * const *argv; 
+        apr_int32_t   in_pipe  = APR_CHILD_BLOCK;
+        apr_int32_t   out_pipe = APR_CHILD_BLOCK;
+        apr_int32_t   err_pipe = APR_CHILD_BLOCK;
+        apr_cmdtype_e cmd_type = APR_PROGRAM;
         apr_pool_t *p; 
         request_rec *r; 
         apr_procattr_t *procattr = NULL;
@@ -489,21 +515,29 @@ static int cgid_server(void *data)
         r = apr_pcalloc(p, sizeof(request_rec)); 
         procnew = apr_pcalloc(p, sizeof(*procnew));
         r->pool = p; 
-        get_req(sd2, r, &filename, &argv0, &env); 
+        get_req(sd2, r, &filename, &argv0, &env, &req_type); 
         apr_put_os_file(&r->server->error_log, &errfileno, r->pool);
         apr_put_os_file(&inout, &sd2, r->pool);
 
+        if (req_type == SSI_REQ) {
+            in_pipe  = APR_NO_PIPE;
+            out_pipe = APR_FULL_BLOCK;
+            err_pipe = APR_NO_PIPE;
+            cmd_type = APR_SHELLCMD;
+        }
+
         if (((rc = apr_createprocattr_init(&procattr, p)) != APR_SUCCESS) ||
-            ((rc = apr_setprocattr_io(procattr,
-                                     APR_CHILD_BLOCK,
-                                     APR_CHILD_BLOCK,
-                                     APR_CHILD_BLOCK)) != APR_SUCCESS) ||
-            ((rc = apr_setprocattr_childin(procattr, inout, NULL)) != APR_SUCCESS) ||
+            ((req_type == CGI_REQ) && 
+             (((rc = apr_setprocattr_io(procattr,
+                                        in_pipe,
+                                        out_pipe,
+                                        err_pipe)) != APR_SUCCESS) ||
+              ((rc = apr_setprocattr_childerr(procattr, r->server->error_log, NULL)) != APR_SUCCESS) ||
+              ((rc = apr_setprocattr_childin(procattr, inout, NULL)) != APR_SUCCESS))) ||
             ((rc = apr_setprocattr_childout(procattr, inout, NULL)) != APR_SUCCESS) ||
-            ((rc = apr_setprocattr_childerr(procattr, r->server->error_log, NULL)) != APR_SUCCESS) ||
             ((rc = apr_setprocattr_dir(procattr,
                                   ap_make_dirstr_parent(r->pool, r->filename))) != APR_SUCCESS) ||
-            ((rc = apr_setprocattr_cmdtype(procattr, APR_PROGRAM)) != APR_SUCCESS)) {
+            ((rc = apr_setprocattr_cmdtype(procattr, cmd_type)) != APR_SUCCESS)) {
             /* Something bad happened, tell the world. */
             ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r,
                       "couldn't set child process attributes: %s", r->filename);
@@ -565,6 +599,17 @@ static void cgid_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
 #if APR_HAS_OTHER_CHILD
         apr_register_other_child(procnew, cgid_maint, &procnew->pid, NULL, p);
 #endif
+
+        cgid_pfn_reg_with_ssi = APR_RETRIEVE_OPTIONAL_FN(ap_register_include_handler);
+        cgid_pfn_gtv          = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_get_tag_and_value);
+        cgid_pfn_ps           = APR_RETRIEVE_OPTIONAL_FN(ap_ssi_parse_string);
+
+        if ((cgid_pfn_reg_with_ssi) && (cgid_pfn_gtv) && (cgid_pfn_ps)) {
+            /* Required by mod_include filter. This is how mod_cgid registers
+             *   with mod_include to provide processing of the exec directive.
+             */
+            cgid_pfn_reg_with_ssi("exec", handle_exec);
+        }
     }
 } 
 
@@ -847,7 +892,7 @@ static int cgid_handler(request_rec *r)
                                    "unable to connect to cgi daemon");
     } 
 
-    send_req(sd, r, argv0, env); 
+    send_req(sd, r, argv0, env, CGI_REQ); 
 
     /* We are putting the tempsock variable into a file so that we can use
      * a pipe bucket to send the data to the client.
@@ -964,6 +1009,254 @@ static int cgid_handler(request_rec *r)
     return OK; /* NOT r->status, even if it has changed. */ 
 } 
 
+
+
+
+/*============================================================================
+ *============================================================================
+ * This is the beginning of the cgi filter code moved from mod_include. This
+ *   is the code required to handle the "exec" SSI directive.
+ *============================================================================
+ *============================================================================*/
+static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
+                       apr_bucket *head_ptr, apr_bucket **inserted_head)
+{
+    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
+    int rr_status;
+    apr_bucket  *tmp_buck, *tmp2_buck;
+
+    if (rr->status != HTTP_OK) {
+        return -1;
+    }
+
+    /* No hardwired path info or query allowed */
+
+    if ((rr->path_info && rr->path_info[0]) || rr->args) {
+        return -1;
+    }
+    if (rr->finfo.protection == 0) {
+        return -1;
+    }
+
+    /* Script gets parameters of the *document*, for back compatibility */
+
+    rr->path_info = r->path_info;       /* hard to get right; see mod_cgi.c */
+    rr->args = r->args;
+
+    /* Force sub_req to be treated as a CGI request, even if ordinary
+     * typing rules would have called it something else.
+     */
+
+    rr->content_type = CGI_MAGIC_TYPE;
+
+    /* Run it. */
+
+    rr_status = ap_run_sub_req(rr);
+    if (ap_is_HTTP_REDIRECT(rr_status)) {
+        apr_size_t len_loc, h_wrt;
+        const char *location = apr_table_get(rr->headers_out, "Location");
+
+        location = ap_escape_html(rr->pool, location);
+        len_loc = strlen(location);
+
+        tmp_buck = apr_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
+        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_immortal("\">", sizeof("\">"));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = apr_bucket_create_immortal("</A>", sizeof("</A>"));
+        APR_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+
+        if (*inserted_head == NULL) {
+            *inserted_head = tmp_buck;
+        }
+    }
+
+    ap_destroy_sub_req(rr);
+
+    return 0;
+}
+
+
+/* This is the special environment used for running the "exec cmd="
+ *   variety of SSI directives.
+ */
+static void add_ssi_vars(request_rec *r, ap_filter_t *next)
+{
+    apr_table_t *e = r->subprocess_env;
+
+    if (r->path_info && r->path_info[0] != '\0') {
+        request_rec *pa_req;
+
+        apr_table_setn(e, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
+
+        pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r, next);
+        if (pa_req->filename) {
+            apr_table_setn(e, "PATH_TRANSLATED",
+                           apr_pstrcat(r->pool, pa_req->filename, pa_req->path_info, NULL));
+        }
+    }
+
+    if (r->args) {
+        char *arg_copy = apr_pstrdup(r->pool, r->args);
+
+        apr_table_setn(e, "QUERY_STRING", r->args);
+        ap_unescape_url(arg_copy);
+        apr_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy));
+    }
+}
+
+static int include_cmd(include_ctx_t *ctx, apr_bucket_brigade **bb, char *command,
+                       request_rec *r, ap_filter_t *f)
+{
+    char **env; 
+    const char *location; 
+    int sd;
+    int retval; 
+    apr_bucket_brigade *bcgi;
+    apr_bucket *b;
+    struct sockaddr_un unix_addr;
+    apr_file_t *tempsock = NULL;
+    void *sconf = r->server->module_config; 
+    cgid_server_conf *conf = (cgid_server_conf *) ap_get_module_config(sconf, &cgid_module); 
+
+    add_ssi_vars(r, f->next);
+    env = ap_create_environment(r->pool, r->subprocess_env);
+
+    if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+            return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, 
+                                   "unable to create socket to cgi daemon");
+    }
+
+    memset(&unix_addr, 0, sizeof(unix_addr));
+    unix_addr.sun_family = AF_UNIX;
+    strcpy(unix_addr.sun_path, conf->sockname);
+
+    if (connect(sd, (struct sockaddr *)&unix_addr, sizeof(unix_addr)) < 0) {
+            return log_scripterror(r, conf, HTTP_INTERNAL_SERVER_ERROR, 0, 
+                                   "unable to connect to cgi daemon");
+    } 
+
+    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+
+    send_req(sd, r, command, env, SSI_REQ); 
+
+    /* We are putting the tempsock variable into a file so that we can use
+     * a pipe bucket to send the data to the client.
+     */
+    apr_put_os_file(&tempsock, &sd, r->pool);
+
+    if ((retval = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR))) 
+        return retval; 
+    
+    location = apr_table_get(r->headers_out, "Location"); 
+
+    if (location && location[0] == '/' && r->status == 200) { 
+        char argsbuffer[HUGE_STRING_LEN]; 
+
+        /* Soak up all the script output */ 
+        while (apr_fgets(argsbuffer, HUGE_STRING_LEN, tempsock) > 0) { 
+            continue; 
+        } 
+        /* This redirect needs to be a GET no matter what the original 
+         * method was. 
+         */ 
+        r->method = apr_pstrdup(r->pool, "GET"); 
+        r->method_number = M_GET; 
+
+        /* We already read the message body (if any), so don't allow 
+         * the redirected request to think it has one. We can ignore 
+         * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. 
+         */ 
+        apr_table_unset(r->headers_in, "Content-Length"); 
+
+        ap_internal_redirect_handler(location, r); 
+        return OK; 
+    } 
+    else if (location && r->status == 200) { 
+        /* XX Note that if a script wants to produce its own Redirect 
+         * body, it now has to explicitly *say* "Status: 302" 
+         */ 
+        return HTTP_MOVED_TEMPORARILY; 
+    } 
+
+    ap_send_http_header(r); 
+    if (!r->header_only) { 
+        bcgi = apr_brigade_create(r->pool);
+        b    = apr_bucket_create_pipe(tempsock);
+        APR_BRIGADE_INSERT_TAIL(bcgi, b);
+        ap_pass_brigade(f->next, bcgi);
+    } 
+
+    return 0;
+}
+
+static int handle_exec(include_ctx_t *ctx, apr_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, apr_bucket *head_ptr, apr_bucket **inserted_head)
+{
+    char *tag     = NULL;
+    char *tag_val = NULL;
+    char *file = r->filename;
+    apr_bucket  *tmp_buck;
+    char parsed_string[MAX_STRING_LEN];
+
+    *inserted_head = NULL;
+    if (ctx->flags & FLAG_PRINTING) {
+        if (ctx->flags & FLAG_NO_EXEC) {
+            ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                      "exec used but not allowed in %s", r->filename);
+            CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+        }
+        else {
+            while (1) {
+                cgid_pfn_gtv(ctx, &tag, &tag_val, 1);
+                if (tag_val == NULL) {
+                    if (tag == NULL) {
+                        return (0);
+                    }
+                    else {
+                        return 1;
+                    }
+                }
+                if (!strcmp(tag, "cmd")) {
+                    cgid_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 1);
+                    if (include_cmd(ctx, bb, parsed_string, r, f) == -1) {
+                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                    "execution failure for parameter \"%s\" "
+                                    "to tag exec in file %s", tag, r->filename);
+                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                    }
+                    /* just in case some stooge changed directories */
+                }
+                else if (!strcmp(tag, "cgi")) {
+                    cgid_pfn_ps(r, tag_val, parsed_string, sizeof(parsed_string), 0);
+                    SPLIT_AND_PASS_PRETAG_BUCKETS(*bb, ctx, f->next);
+                    if (include_cgi(parsed_string, r, f->next, head_ptr, inserted_head) == -1) {
+                        ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                    "invalid CGI ref \"%s\" in %s", tag_val, file);
+                        CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                    }
+                }
+                else {
+                    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
+                                "unknown parameter \"%s\" to tag exec in %s", tag, file);
+                    CREATE_ERROR_BUCKET(ctx, tmp_buck, head_ptr, *inserted_head);
+                }
+            }
+        }
+    }
+    return 0;
+}
+/*============================================================================
+ *============================================================================
+ * This is the end of the cgi filter code moved from mod_include.
+ *============================================================================
+ *============================================================================*/
+
+
 static void register_hook(apr_pool_t *p)
 {
     ap_hook_post_config(cgid_init, NULL, NULL, APR_HOOK_MIDDLE);