httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Paul J. Reder" <rede...@raleigh.ibm.com>
Subject [Patch]:Move SSI "exec" directive support from mod_include to mod_cgi(d).
Date Fri, 12 Jan 2001 20:16:17 GMT
The following patch moves the "exec cgi" and "exec cmd" logic from mod_include
to mod_cgi and mod_cgid. As much duplicate code as possible was removed in the
process.

We now have the first example of an external module registering as an SSI
directive handler with the mod_include code.

This patch is based from the CVS repository as of 1/11/2001 around noon.

The only thing I'm not sure of is what the preferred way to include the
mod_include.h file in mod_cgi(d).c.

-- 
Paul J. Reder
-----------------------------------------------------------
"The strength of the Constitution lies entirely in the determination of each
citizen to defend it.  Only if every single citizen feels duty bound to do
his share in this defense are the constitutional rights secure."
-- Albert Einstein


Index: modules/filters/mod_include.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/filters/mod_include.c,v
retrieving revision 1.85
diff -u -r1.85 mod_include.c
--- modules/filters/mod_include.c	2001/01/09 05:12:40	1.85
+++ modules/filters/mod_include.c	2001/01/12 19:46:46
@@ -505,7 +505,7 @@
 
 #define SKIP_TAG_WHITESPACE(ptr) while ((*ptr != '\0') && (apr_isspace (*ptr))) ptr++
 
-static void get_tag_and_value(include_ctx_t *ctx, char **tag,
+void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
                               char **tag_val, int dodecode)
 {
     char *c = ctx->curr_tag_pos;
@@ -583,8 +583,8 @@
 /*
  * Do variable substitution on strings
  */
-static void parse_string(request_rec *r, const char *in, char *out,
-			size_t length, int leave_name)
+void ap_ssi_parse_string(request_rec *r, const char *in, char *out,
+                         size_t length, int leave_name)
 {
     char ch;
     char *next = out;
@@ -610,7 +610,6 @@
             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;
@@ -646,17 +645,10 @@
 		 * 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;
@@ -694,69 +686,6 @@
 
 /* --------------------------- Action handlers ---------------------------- */
 
-static int include_cgi(char *s, request_rec *r, ap_filter_t *next,
-                       ap_bucket *head_ptr, ap_bucket **inserted_head)
-{
-    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
-    int rr_status;
-    ap_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 = ap_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
-        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
-        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = ap_bucket_create_immortal("\">", sizeof("\">"));
-        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
-        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-        tmp2_buck = ap_bucket_create_immortal("</A>", sizeof("</A>"));
-        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
-
-        if (*inserted_head == NULL) {
-            *inserted_head = tmp_buck;
-        }
-    }
-
-    ap_destroy_sub_req(rr);
-    ap_chdir_file(r->filename);
-
-    return 0;
-}
-
 /* ensure that path is relative, and does not contain ".." elements
  * ensentially ensure that it does not match the regex:
  * (^/|(^|/)\.\.(/|$))
@@ -808,7 +737,7 @@
     *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);
@@ -821,7 +750,7 @@
                 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)) {
@@ -894,10 +823,9 @@
     		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";
                     }
                 }
@@ -926,215 +854,6 @@
     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, ap_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 {
-            ap_bucket_brigade *bcgi;
-            ap_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 = ap_brigade_create(r->pool);
-            b = ap_bucket_create_pipe(file);
-            AP_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, ap_bucket_brigade **bb, request_rec *r,
-                       ap_filter_t *f, ap_bucket *head_ptr, ap_bucket **inserted_head)
-{
-    char *tag     = NULL;
-    char *tag_val = NULL;
-    char *file = r->filename;
-    ap_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 */
-                    ap_chdir_file(r->filename);
-                }
-                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);
-                    }
-                    ap_chdir_file(r->filename);
-                }
-                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, ap_bucket_brigade **bb, request_rec *r,
                        ap_filter_t *f, ap_bucket *head_ptr, ap_bucket **inserted_head)
@@ -1151,7 +870,7 @@
     *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;
@@ -1219,7 +938,7 @@
     *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. */
@@ -1229,20 +948,20 @@
                 }
             }
             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;
@@ -1379,7 +1098,7 @@
     *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;
@@ -1389,7 +1108,7 @@
                 }
             }
             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];
 
@@ -1441,7 +1160,7 @@
     *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;
@@ -1451,7 +1170,7 @@
                 }
             }
             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;
 
@@ -1993,7 +1712,7 @@
                     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;
@@ -2018,7 +1737,7 @@
             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));
@@ -2033,7 +1752,7 @@
             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));
@@ -2082,11 +1801,11 @@
                 *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));
@@ -2153,11 +1872,11 @@
                 *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));
@@ -2319,7 +2038,7 @@
     }
     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,
@@ -2387,7 +2106,7 @@
     *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");
                 
@@ -2455,7 +2174,7 @@
 
     *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);
@@ -2488,7 +2207,7 @@
 
     *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);
@@ -2519,7 +2238,7 @@
     *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;
             }
@@ -2537,7 +2256,7 @@
                     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));
             }
@@ -2560,7 +2279,7 @@
     ap_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;
@@ -3045,7 +2764,6 @@
     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);
Index: modules/filters/mod_include.h
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/filters/mod_include.h,v
retrieving revision 1.6
diff -u -r1.6 mod_include.h
--- modules/filters/mod_include.h	2000/12/20 16:43:37	1.6
+++ modules/filters/mod_include.h	2001/01/12 19:46:46
@@ -190,15 +190,21 @@
     }                                                             \
 }
 
-#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt)               \
+#define SPLIT_AND_PASS_PRETAG_BUCKETS(brgd, cntxt, next)         \
 if ((AP_BRIGADE_EMPTY(cntxt->ssi_tag_brigade)) &&                \
     (cntxt->head_start_bucket != NULL)) {                        \
     ap_bucket_brigade *tag_plus;                                 \
                                                                  \
     tag_plus = ap_brigade_split(brgd, cntxt->head_start_bucket); \
-    ap_pass_brigade(f->next, brgd);                              \
+    ap_pass_brigade(next, brgd);                                 \
     brgd = tag_plus;                                             \
 }
+
+void ap_ssi_get_tag_and_value(include_ctx_t *ctx, char **tag,
+                              char **tag_val, int dodecode);
+
+void ap_ssi_parse_string(request_rec *r, const char *in, char *out,
+                         size_t length, int leave_name);
 
 typedef int (*handler)(include_ctx_t *ctx, ap_bucket_brigade **bb, 
                        request_rec *r, ap_filter_t *f, ap_bucket *head_ptr, 
Index: modules/generators/mod_cgi.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/generators/mod_cgi.c,v
retrieving revision 1.77
diff -u -r1.77 mod_cgi.c
--- modules/generators/mod_cgi.c	2001/01/08 23:54:52	1.77
+++ modules/generators/mod_cgi.c	2001/01/12 19:46:47
@@ -88,6 +88,7 @@
 #include "util_script.h"
 #include "ap_mpm.h"
 #include "http_conf_globals.h"
+#include "../filters/mod_include.h"
 #ifdef HAVE_STRING_H
 #include <string.h>
 #endif
@@ -97,6 +98,19 @@
 
 module AP_MODULE_DECLARE_DATA cgi_module;
 
+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;
+    ap_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
@@ -310,21 +324,52 @@
     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
 
 
@@ -344,7 +389,13 @@
 	    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
@@ -357,10 +408,10 @@
      * 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
@@ -372,12 +423,17 @@
 #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) {
@@ -391,20 +447,19 @@
             *script_in = procnew->out;
             if (!script_in)
                 return APR_EBADF;
-            apr_set_pipe_timeout(*script_in, 
-                                 (int)(r->server->timeout * APR_USEC_PER_SEC));
+            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));
+            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);
@@ -513,6 +568,7 @@
     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;
@@ -597,9 +653,18 @@
     }
     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;
@@ -720,9 +785,202 @@
     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,
+                       ap_bucket *head_ptr, ap_bucket **inserted_head)
+{
+    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
+    int rr_status;
+    ap_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 = ap_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
+        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_immortal("\">", sizeof("\">"));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_immortal("</A>", sizeof("</A>"));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+
+        if (*inserted_head == NULL) {
+            *inserted_head = tmp_buck;
+        }
+    }
+
+    ap_destroy_sub_req(rr);
+    ap_chdir_file(r->filename);
+
+    return 0;
+}
+
+
+static int include_cmd(include_ctx_t *ctx, ap_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;
+    ap_bucket_brigade *bcgi;
+    ap_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 = ap_brigade_create(r->pool);
+    b = ap_bucket_create_pipe(script_in);
+    AP_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, ap_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, ap_bucket *head_ptr, ap_bucket **inserted_head)
+{
+    char *tag     = NULL;
+    char *tag_val = NULL;
+    char *file = r->filename;
+    ap_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) {
+                ap_ssi_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")) {
+                    ap_ssi_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 */
+                    ap_chdir_file(r->filename);
+                }
+                else if (!strcmp(tag, "cgi")) {
+                    ap_ssi_parse_string(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);
+                    }
+                    ap_chdir_file(r->filename);
+                }
+                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)
+{
+    /* Required by mod_include filter. This is how mod_cgi registers
+     *   with mod_include to provide processing of the exec directive.
+     */
+    ap_register_include_handler("exec", handle_exec);
+}
+
 static void register_hooks(void)
 {
     ap_hook_handler(cgi_handler, NULL, NULL, AP_HOOK_MIDDLE);
+    ap_hook_post_config(cgi_post_config, NULL, NULL, AP_HOOK_REALLY_FIRST);
 }
 
 module AP_MODULE_DECLARE_DATA cgi_module =
Index: modules/generators/mod_cgid.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/generators/mod_cgid.c,v
retrieving revision 1.59
diff -u -r1.59 mod_cgid.c
--- modules/generators/mod_cgid.c	2001/01/08 23:54:53	1.59
+++ modules/generators/mod_cgid.c	2001/01/12 19:46:48
@@ -91,6 +91,7 @@
 #include "http_conf_globals.h" 
 #include "ap_mpm.h"
 #include "unixd.h"
+#include "../filters/mod_include.h"
 #include <sys/stat.h>
 #ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
@@ -107,6 +108,8 @@
 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, ap_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, ap_bucket *head_ptr, ap_bucket **inserted_head);
 
 static apr_pool_t *pcgi; 
 
@@ -130,6 +133,9 @@
 
 #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
@@ -224,7 +230,7 @@
 #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; 
@@ -234,6 +240,7 @@
 
     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' */
@@ -304,9 +311,9 @@
 
 
 
-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; 
 
@@ -317,28 +324,38 @@
         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"); 
-        }     
+    }     
 
     for (i = 0; env[i]; i++) { 
         data = apr_pstrcat(r->pool, data, env[i], "\n", NULL); 
     } 
     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 0
 #ifdef RLIMIT_CPU 
     if (conf->limit_cpu) { 
@@ -381,7 +398,7 @@
 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;
@@ -439,6 +456,10 @@
         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;
@@ -462,21 +483,29 @@
         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);
@@ -532,6 +561,11 @@
 #if APR_HAS_OTHER_CHILD
         apr_register_other_child(procnew, cgid_maint, NULL, NULL, p);
 #endif
+
+        /* Required by mod_include filter. This is how mod_cgid registers
+         *   with mod_include to provide processing of the exec directive.
+         */
+        ap_register_include_handler("exec", handle_exec);
     }
 } 
 
@@ -814,7 +848,7 @@
                                    "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.
@@ -930,6 +964,257 @@
 
     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,
+                       ap_bucket *head_ptr, ap_bucket **inserted_head)
+{
+    request_rec *rr = ap_sub_req_lookup_uri(s, r, next);
+    int rr_status;
+    ap_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 = ap_bucket_create_immortal("<A HREF=\"", sizeof("<A HREF=\""));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp_buck);
+        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_immortal("\">", sizeof("\">"));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_heap(location, len_loc, 1, &h_wrt);
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+        tmp2_buck = ap_bucket_create_immortal("</A>", sizeof("</A>"));
+        AP_BUCKET_INSERT_BEFORE(head_ptr, tmp2_buck);
+
+        if (*inserted_head == NULL) {
+            *inserted_head = tmp_buck;
+        }
+    }
+
+    ap_destroy_sub_req(rr);
+    ap_chdir_file(r->filename);
+
+    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, ap_bucket_brigade **bb, char *command,
+                       request_rec *r, ap_filter_t *f)
+{
+    char **env; 
+    const char *location; 
+    int sd;
+    int retval; 
+    ap_bucket_brigade *bcgi;
+    ap_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 = ap_brigade_create(r->pool);
+        b    = ap_bucket_create_pipe(tempsock);
+        AP_BRIGADE_INSERT_TAIL(bcgi, b);
+        ap_pass_brigade(f->next, bcgi);
+    } 
+
+    return 0;
+}
+
+static int handle_exec(include_ctx_t *ctx, ap_bucket_brigade **bb, request_rec *r,
+                       ap_filter_t *f, ap_bucket *head_ptr, ap_bucket **inserted_head)
+{
+    char *tag     = NULL;
+    char *tag_val = NULL;
+    char *file = r->filename;
+    ap_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) {
+                ap_ssi_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")) {
+                    ap_ssi_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 */
+                    ap_chdir_file(r->filename);
+                }
+                else if (!strcmp(tag, "cgi")) {
+                    ap_ssi_parse_string(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);
+                    }
+                    ap_chdir_file(r->filename);
+                }
+                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(void)
 {

Mime
View raw message