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]
#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>
static apr_hash_t *include_hash;
+static APR_OPTIONAL_FN_TYPE(ap_register_include_handler) *ssi_pfn_register;
+
/* ------------------------ Environment function -------------------------- */
#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;
/*
* 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;
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;
* 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;
/* --------------------------- 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:
* (^/|(^|/)\.\.(/|$))
*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);
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)) {
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";
}
}
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)
*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;
*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. */
}
}
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;
*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;
}
}
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];
*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;
}
}
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;
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;
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));
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));
*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));
*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));
}
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,
*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");
*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);
*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);
*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;
}
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));
}
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;
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);
}
{
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);
+ }
}
/*
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);
}
#ifndef _MOD_INCLUDE_H
#define _MOD_INCLUDE_H 1
-
-
#define STARTING_SEQUENCE "<!--#"
#define ENDING_SEQUENCE "-->"
#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 */
#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
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
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
* 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
#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) {
*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);
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;
}
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;
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 =
#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;
#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
#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;
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' */
-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");
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");
}
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);
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;
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;
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);
#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);
+ }
}
}
"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.
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);