summaryrefslogtreecommitdiffstats
path: root/server/apreq_module_cgi.c
diff options
context:
space:
mode:
authorPhilip M. Gollucci <pgollucci@apache.org>2011-11-13 01:20:32 +0100
committerPhilip M. Gollucci <pgollucci@apache.org>2011-11-13 01:20:32 +0100
commit4472a86146a20a356383475792bae37870c7862a (patch)
tree629b76803ce960183dc145c9f23c9e0c816309d7 /server/apreq_module_cgi.c
parentServer directive display (-L): Include directives of DSOs. (diff)
downloadapache2-4472a86146a20a356383475792bae37870c7862a.tar.xz
apache2-4472a86146a20a356383475792bae37870c7862a.zip
As discussed at AC NA 2011
o relocate srclib/libapreq/library/*.c -> server/apreq_${f}.c o relocate srclib/libapreq/include/*.h -> include/*.h o remove apreq_version.[hc] related stuff since its nolonger its own lib o connect modules/apreq to the build under 'most' default comment out in httpd.conf o update make_exports.awk to handle APREQ marcos git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1201372 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'server/apreq_module_cgi.c')
-rw-r--r--server/apreq_module_cgi.c1013
1 files changed, 1013 insertions, 0 deletions
diff --git a/server/apreq_module_cgi.c b/server/apreq_module_cgi.c
new file mode 100644
index 0000000000..14fb39ba39
--- /dev/null
+++ b/server/apreq_module_cgi.c
@@ -0,0 +1,1013 @@
+/*
+** Licensed to the Apache Software Foundation (ASF) under one or more
+** contributor license agreements. See the NOTICE file distributed with
+** this work for additional information regarding copyright ownership.
+** The ASF licenses this file to You under the Apache License, Version 2.0
+** (the "License"); you may not use this file except in compliance with
+** the License. You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+#include <assert.h>
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+#include "apreq_module.h"
+#include "apreq_error.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_env.h"
+#include "apreq_util.h"
+
+#define USER_DATA_KEY "apreq"
+
+/* Parroting APLOG_* ... */
+
+#define CGILOG_EMERG 0 /* system is unusable */
+#define CGILOG_ALERT 1 /* action must be taken immediately */
+#define CGILOG_CRIT 2 /* critical conditions */
+#define CGILOG_ERR 3 /* error conditions */
+#define CGILOG_WARNING 4 /* warning conditions */
+#define CGILOG_NOTICE 5 /* normal but significant condition */
+#define CGILOG_INFO 6 /* informational */
+#define CGILOG_DEBUG 7 /* debug-level messages */
+
+#define CGILOG_LEVELMASK 7
+#define CGILOG_MARK __FILE__, __LINE__
+
+/** Interactive patch:
+ * TODO Don't use 65K buffer
+ * TODO Handle empty/non-existant parameters
+ * TODO Allow body elements to be files
+ * TODO When running body/get/cookies all at once, include previous cached
+ * values (and don't start at 0 in count)
+ * TODO What happens if user does apreq_param, but needs POST value - we'll
+ * never catch it now, as args param will match...
+ */
+
+struct cgi_handle {
+ struct apreq_handle_t handle;
+
+ apr_table_t *jar, *args, *body;
+ apr_status_t jar_status,
+ args_status,
+ body_status;
+
+ apreq_parser_t *parser;
+ apreq_hook_t *hook_queue;
+ apreq_hook_t *find_param;
+
+ const char *temp_dir;
+ apr_size_t brigade_limit;
+ apr_uint64_t read_limit;
+ apr_uint64_t bytes_read;
+
+ apr_bucket_brigade *in;
+ apr_bucket_brigade *tmpbb;
+
+ int interactive_mode;
+ const char *promptstr;
+ apr_file_t *sout, *sin;
+};
+
+#define CRLF "\015\012"
+static const char *nullstr = 0;
+#define DEFAULT_PROMPT "([$t] )$n(\\($l\\))([$d]): "
+#define MAX_PROMPT_NESTING_LEVELS 8
+#define MAX_BUFFER_SIZE 65536
+
+typedef struct {
+ const char *t_name;
+ int t_val;
+} TRANS;
+
+static const TRANS priorities[] = {
+ {"emerg", CGILOG_EMERG},
+ {"alert", CGILOG_ALERT},
+ {"crit", CGILOG_CRIT},
+ {"error", CGILOG_ERR},
+ {"warn", CGILOG_WARNING},
+ {"notice", CGILOG_NOTICE},
+ {"info", CGILOG_INFO},
+ {"debug", CGILOG_DEBUG},
+ {NULL, -1},
+};
+
+static char* chomp(char* str) {
+ apr_size_t p = strlen(str);
+ while (--p >= 0) {
+ switch ((char)(str[p])) {
+ case '\015':
+ case '\012':str[p]='\000';
+ break;
+ default:return str;
+ }
+ }
+ return str;
+}
+
+/** TODO: Support wide-characters */
+/* prompt takes a apreq_handle and 2 strings - name and type - and prompts a
+ user for input via stdin/stdout. used in interactive mode.
+
+ name must be defined. type can be null.
+
+ we take the promptstring defined in the handle and interpolate variables as
+ follows:
+
+ $n - name of the variable we're asking for (param 2 to prompt())
+ $t - type of the variable we're asking for - like cookie, get, post, etc
+ (param 3 to prompt())
+ parentheses - if a variable is surrounded by parentheses, and interpolates
+ as null, then nothing else in the parentheses will be displayed
+ Useful if you want a string to only show up if a given variable
+ is available
+
+ These are planned for forward-compatibility, but the underlying features
+ need some love... I left these in here just as feature reminders, rather
+ than completely removing them from the code - at least they provide sanity
+ testing of the default prompt & parentheses - issac
+
+ $l - label for the param - the end-user-developer can provide a textual
+ description of the param (name) being requested (currently unused in
+ lib)
+ $d - default value for the param (currently unused in lib)
+
+*/
+static char *prompt(apreq_handle_t *handle, const char *name,
+ const char *type) {
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const char *defval = nullstr;
+ const char *label = NULL;
+ const char *cprompt;
+ char buf[MAX_PROMPT_NESTING_LEVELS][MAX_BUFFER_SIZE];
+ /* Array of current arg for given p-level */
+ char *start, curarg[MAX_PROMPT_NESTING_LEVELS] = "";
+ /* Parenthesis level (for argument/text grouping) */
+ int plevel;
+
+ cprompt = req->promptstr - 1;
+ *buf[0] = plevel = 0;
+ start = buf[0];
+
+ while (*(++cprompt) != 0) {
+ switch (*cprompt) {
+ case '$': /* interpolate argument; curarg[plevel] => 1 */
+ cprompt++;
+ switch (*cprompt) {
+ case 't':
+ if (type != NULL) {
+ strcpy(start, type);
+ start += strlen(type);
+ curarg[plevel] = 1;
+ } else {
+ curarg[plevel] = curarg[plevel] | 0;
+ }
+ break;
+ case 'n':
+ /* Name can't be null :-) [If it can, we should
+ * immediately return NULL] */
+ strcpy(start, name);
+ start += strlen(name);
+ curarg[plevel] = 1;
+ break;
+ case 'l':
+ if (label != NULL) {
+ strcpy(start, label);
+ start += strlen(label);
+ curarg[plevel] = 1;
+ } else {
+ curarg[plevel] = curarg[plevel] | 0;
+ }
+ break;
+ case 'd':
+ /* TODO: Once null defaults are available,
+ * remove if and use nullstr if defval == NULL */
+ if (defval != NULL) {
+ strcpy(start, defval);
+ start += strlen(defval);
+ curarg[plevel] = 1;
+ } else {
+ curarg[plevel] = curarg[plevel] | 0;
+ }
+ break;
+ default:
+ /* Handle this? */
+ break;
+ }
+ break;
+
+ case '(':
+ if (plevel <= MAX_PROMPT_NESTING_LEVELS) {
+ plevel++;
+ curarg[plevel] = *buf[plevel] = 0;
+ start = buf[plevel];
+ }
+ /* else? */
+ break;
+
+ case ')':
+ if (plevel > 0) {
+ *start = 0; /* Null terminate current string */
+
+ /* Move pointer to end of string */
+ plevel--;
+ start = buf[plevel] + strlen(buf[plevel]);
+
+ /* If old curarg was set, concat buffer with level down */
+ if (curarg[plevel + 1]) {
+ strcpy(start, buf[plevel + 1]);
+ start += strlen(buf[plevel + 1]);
+ }
+
+ break;
+ }
+ case '\\': /* Check next character for escape sequence
+ * (just ignore it for now) */
+ (void)*cprompt++;
+ /* Fallthrough */
+
+ default:
+ *start++ = *cprompt;
+ }
+ }
+
+ *start = 0; /* Null terminate the string */
+
+ apr_file_printf(req->sout, "%s", buf[0]);
+ apr_file_gets(buf[0], MAX_BUFFER_SIZE, req->sin);
+ chomp(buf[0]);
+ if (strcmp(buf[0], "")) {
+/* if (strcmp(buf[0], nullstr)) */
+ return apr_pstrdup(handle->pool, buf[0]);
+/* return NULL; */
+ }
+
+ if (defval != nullstr)
+ return apr_pstrdup(handle->pool, defval);
+
+ return NULL;
+}
+
+static const char *cgi_header_in(apreq_handle_t *handle,
+ const char *name)
+{
+ apr_pool_t *p = handle->pool;
+ char *key = apr_pstrcat(p, "HTTP_", name, NULL);
+ char *k, *value = NULL;
+ for (k = key; *k; ++k) {
+ if (*k == '-')
+ *k = '_';
+ else
+ *k = apr_toupper(*k);
+ }
+
+ if (!strcmp(key, "HTTP_CONTENT_TYPE")
+ || !strcmp(key, "HTTP_CONTENT_LENGTH"))
+ {
+ key += 5; /* strlen("HTTP_") */
+ }
+
+ apr_env_get(&value, key, p);
+
+ return value;
+}
+
+
+
+
+static void cgi_log_error(const char *file, int line, int level,
+ apr_status_t status, apreq_handle_t *handle,
+ const char *fmt, ...)
+{
+ apr_pool_t *p = handle->pool;
+ char buf[256];
+ char *log_level_string, *ra;
+ const char *remote_addr;
+ unsigned log_level = CGILOG_WARNING;
+ char date[APR_CTIME_LEN];
+ va_list vp;
+#ifndef WIN32
+ apr_file_t *err;
+#endif
+
+ va_start(vp, fmt);
+
+ if (apr_env_get(&log_level_string, "LOG_LEVEL", p) == APR_SUCCESS)
+ log_level = (log_level_string[0] - '0');
+
+ level &= CGILOG_LEVELMASK;
+
+ if (level < (int)log_level) {
+
+ if (apr_env_get(&ra, "REMOTE_ADDR", p) == APR_SUCCESS)
+ remote_addr = ra;
+ else
+ remote_addr = "address unavailable";
+
+ apr_ctime(date, apr_time_now());
+
+#ifndef WIN32
+
+ apr_file_open_stderr(&err, p);
+ apr_file_printf(err, "[%s] [%s] [%s] %s(%d): %s: %s\n",
+ date, priorities[level].t_name, remote_addr, file, line,
+ apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp));
+ apr_file_flush(err);
+
+#else
+ fprintf(stderr, "[%s] [%s] [%s] %s(%d): %s: %s\n",
+ date, priorities[level].t_name, remote_addr, file, line,
+ apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp));
+#endif
+ }
+
+ va_end(vp);
+
+}
+
+
+APR_INLINE
+static const char *cgi_query_string(apreq_handle_t *handle)
+{
+ char *value = NULL, qs[] = "QUERY_STRING";
+ apr_env_get(&value, qs, handle->pool);
+ return value;
+}
+
+
+static void init_body(apreq_handle_t *handle)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const char *cl_header = cgi_header_in(handle, "Content-Length");
+ apr_bucket_alloc_t *ba = handle->bucket_alloc;
+ apr_pool_t *pool = handle->pool;
+ apr_file_t *file;
+ apr_bucket *eos, *pipe;
+
+ if (cl_header != NULL) {
+ char *dummy;
+ apr_int64_t content_length = apr_strtoi64(cl_header, &dummy, 0);
+
+ if (dummy == NULL || *dummy != 0) {
+ req->body_status = APREQ_ERROR_BADHEADER;
+ cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
+ "Invalid Content-Length header (%s)", cl_header);
+ return;
+ }
+ else if ((apr_uint64_t)content_length > req->read_limit) {
+ req->body_status = APREQ_ERROR_OVERLIMIT;
+ cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
+ "Content-Length header (%s) exceeds configured "
+ "max_body limit (%" APR_UINT64_T_FMT ")",
+ cl_header, req->read_limit);
+ return;
+ }
+ }
+
+ if (req->parser == NULL) {
+ const char *ct_header = cgi_header_in(handle, "Content-Type");
+
+ if (ct_header != NULL) {
+ apreq_parser_function_t pf = apreq_parser(ct_header);
+
+ if (pf != NULL) {
+ req->parser = apreq_parser_make(pool,
+ ba,
+ ct_header,
+ pf,
+ req->brigade_limit,
+ req->temp_dir,
+ req->hook_queue,
+ NULL);
+ }
+ else {
+ req->body_status = APREQ_ERROR_NOPARSER;
+ return;
+ }
+ }
+ else {
+ req->body_status = APREQ_ERROR_NOHEADER;
+ return;
+ }
+ }
+ else {
+ if (req->parser->brigade_limit > req->brigade_limit)
+ req->parser->brigade_limit = req->brigade_limit;
+ if (req->temp_dir != NULL)
+ req->parser->temp_dir = req->temp_dir;
+ if (req->hook_queue != NULL)
+ apreq_parser_add_hook(req->parser, req->hook_queue);
+ }
+
+ req->hook_queue = NULL;
+ req->in = apr_brigade_create(pool, ba);
+ req->tmpbb = apr_brigade_create(pool, ba);
+
+ apr_file_open_stdin(&file, pool); // error status?
+ pipe = apr_bucket_pipe_create(file, ba);
+ eos = apr_bucket_eos_create(ba);
+ APR_BRIGADE_INSERT_HEAD(req->in, pipe);
+ APR_BRIGADE_INSERT_TAIL(req->in, eos);
+
+ req->body_status = APR_INCOMPLETE;
+
+}
+
+static apr_status_t cgi_read(apreq_handle_t *handle,
+ apr_off_t bytes)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ apr_bucket *e;
+ apr_status_t s;
+
+ if (req->body_status == APR_EINIT)
+ init_body(handle);
+
+ if (req->body_status != APR_INCOMPLETE)
+ return req->body_status;
+
+
+ switch (s = apr_brigade_partition(req->in, bytes, &e)) {
+ apr_off_t len;
+
+ case APR_SUCCESS:
+
+ apreq_brigade_move(req->tmpbb, req->in, e);
+ req->bytes_read += bytes;
+
+ if (req->bytes_read > req->read_limit) {
+ req->body_status = APREQ_ERROR_OVERLIMIT;
+ cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status,
+ handle, "Bytes read (%" APR_UINT64_T_FMT
+ ") exceeds configured limit (%" APR_UINT64_T_FMT ")",
+ req->bytes_read, req->read_limit);
+ break;
+ }
+
+ req->body_status =
+ apreq_parser_run(req->parser, req->body, req->tmpbb);
+ apr_brigade_cleanup(req->tmpbb);
+ break;
+
+
+ case APR_INCOMPLETE:
+
+ apreq_brigade_move(req->tmpbb, req->in, e);
+ s = apr_brigade_length(req->tmpbb, 1, &len);
+
+ if (s != APR_SUCCESS) {
+ req->body_status = s;
+ break;
+ }
+ req->bytes_read += len;
+
+ if (req->bytes_read > req->read_limit) {
+ req->body_status = APREQ_ERROR_OVERLIMIT;
+ cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle,
+ "Bytes read (%" APR_UINT64_T_FMT
+ ") exceeds configured limit (%" APR_UINT64_T_FMT ")",
+ req->bytes_read, req->read_limit);
+
+ break;
+ }
+
+ req->body_status =
+ apreq_parser_run(req->parser, req->body, req->tmpbb);
+ apr_brigade_cleanup(req->tmpbb);
+ break;
+
+ default:
+ req->body_status = s;
+ }
+
+ return req->body_status;
+}
+
+
+
+static apr_status_t cgi_jar(apreq_handle_t *handle,
+ const apr_table_t **t)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->interactive_mode && req->jar_status != APR_SUCCESS) {
+ char buf[65536];
+ const char *name, *val;
+ apreq_cookie_t *p;
+ int i = 1;
+ apr_file_printf(req->sout, "[CGI] Requested all cookies\n");
+ while (1) {
+ apr_file_printf(req->sout, "[CGI] Please enter a name for cookie %d (or just hit ENTER to end): ",
+ i++);
+ apr_file_gets(buf, 65536, req->sin);
+ chomp(buf);
+ if (!strcmp(buf, "")) {
+ break;
+ }
+ name = apr_pstrdup(handle->pool, buf);
+ val = prompt(handle, name, "cookie");
+ if (val == NULL)
+ val = "";
+ p = apreq_cookie_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_cookie_tainted_on(p);
+ apreq_value_table_add(&p->v, req->jar);
+ val = p->v.data;
+ }
+ req->jar_status = APR_SUCCESS;
+ } /** Fallthrough */
+
+ if (req->jar_status == APR_EINIT) {
+ const char *cookies = cgi_header_in(handle, "Cookie");
+ if (cookies != NULL) {
+ req->jar_status =
+ apreq_parse_cookie_header(handle->pool, req->jar, cookies);
+ }
+ else
+ req->jar_status = APREQ_ERROR_NODATA;
+ }
+
+ *t = req->jar;
+ return req->jar_status;
+}
+
+static apr_status_t cgi_args(apreq_handle_t *handle,
+ const apr_table_t **t)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->interactive_mode && req->args_status != APR_SUCCESS) {
+ char buf[65536];
+ const char *name, *val;
+ apreq_param_t *p;
+ int i = 1;
+ apr_file_printf(req->sout, "[CGI] Requested all argument parameters\n");
+ while (1) {
+ apr_file_printf(req->sout, "[CGI] Please enter a name for parameter %d (or just hit ENTER to end): ",
+ i++);
+ apr_file_gets(buf, 65536, req->sin);
+ chomp(buf);
+ if (!strcmp(buf, "")) {
+ break;
+ }
+ name = apr_pstrdup(handle->pool, buf);
+ val = prompt(handle, name, "parameter");
+ if (val == NULL)
+ val = "";
+ p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_param_tainted_on(p);
+ apreq_value_table_add(&p->v, req->args);
+ val = p->v.data;
+ }
+ req->args_status = APR_SUCCESS;
+ } /** Fallthrough */
+
+ if (req->args_status == APR_EINIT) {
+ const char *qs = cgi_query_string(handle);
+ if (qs != NULL) {
+ req->args_status =
+ apreq_parse_query_string(handle->pool, req->args, qs);
+ }
+ else
+ req->args_status = APREQ_ERROR_NODATA;
+ }
+
+ *t = req->args;
+ return req->args_status;
+}
+
+
+
+
+static apreq_cookie_t *cgi_jar_get(apreq_handle_t *handle,
+ const char *name)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const apr_table_t *t;
+ const char *val = NULL;
+
+ if (req->jar_status == APR_EINIT && !req->interactive_mode)
+ cgi_jar(handle, &t);
+ else
+ t = req->jar;
+
+ val = apr_table_get(t, name);
+ if (val == NULL) {
+ if (!req->interactive_mode) {
+ return NULL;
+ } else {
+ apreq_cookie_t *p;
+ val = prompt(handle, name, "cookie");
+ if (val == NULL)
+ return NULL;
+ p = apreq_cookie_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_cookie_tainted_on(p);
+ apreq_value_table_add(&p->v, req->jar);
+ val = p->v.data;
+ }
+ }
+
+
+ return apreq_value_to_cookie(val);
+}
+
+static apreq_param_t *cgi_args_get(apreq_handle_t *handle,
+ const char *name)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const apr_table_t *t;
+ const char *val = NULL;
+
+ if (req->args_status == APR_EINIT && !req->interactive_mode)
+ cgi_args(handle, &t);
+ else
+ t = req->args;
+
+ val = apr_table_get(t, name);
+ if (val == NULL) {
+ if (!req->interactive_mode) {
+ return NULL;
+ } else {
+ apreq_param_t *p;
+ val = prompt(handle, name, "parameter");
+ if (val == NULL)
+ return NULL;
+ p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_param_tainted_on(p);
+ apreq_value_table_add(&p->v, req->args);
+ val = p->v.data;
+ }
+ }
+
+
+ return apreq_value_to_param(val);
+}
+
+
+
+static apr_status_t cgi_body(apreq_handle_t *handle,
+ const apr_table_t **t)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->interactive_mode && req->body_status != APR_SUCCESS) {
+ const char *name, *val;
+ apreq_param_t *p;
+ int i = 1;
+ apr_file_printf(req->sout, "[CGI] Requested all body parameters\n");
+ while (1) {
+ char buf[65536];
+ apr_file_printf(req->sout, "[CGI] Please enter a name for parameter %d (or just hit ENTER to end): ",
+ i++);
+ apr_file_gets(buf, 65536, req->sin);
+ chomp(buf);
+ if (!strcmp(buf, "")) {
+ break;
+ }
+ name = apr_pstrdup(handle->pool, buf);
+ val = prompt(handle, name, "parameter");
+ if (val == NULL)
+ val = "";
+ p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_param_tainted_on(p);
+ apreq_value_table_add(&p->v, req->body);
+ val = p->v.data;
+ }
+ req->body_status = APR_SUCCESS;
+ } /** Fallthrough */
+
+ switch (req->body_status) {
+
+ case APR_EINIT:
+ init_body(handle);
+ if (req->body_status != APR_INCOMPLETE)
+ break;
+
+ case APR_INCOMPLETE:
+ while (cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE)
+ == APR_INCOMPLETE)
+ ; /*loop*/
+ }
+
+ *t = req->body;
+ return req->body_status;
+}
+
+static apreq_param_t *cgi_body_get(apreq_handle_t *handle,
+ const char *name)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const char *val = NULL;
+ apreq_hook_t *h;
+ apreq_hook_find_param_ctx_t *hook_ctx;
+
+ if (req->interactive_mode) {
+ val = apr_table_get(req->body, name);
+ if (val == NULL) {
+ return NULL;
+ } else {
+ apreq_param_t *p;
+ val = prompt(handle, name, "parameter");
+ if (val == NULL)
+ return NULL;
+ p = apreq_param_make(handle->pool, name, strlen(name), val, strlen(val));
+ apreq_param_tainted_on(p);
+ apreq_value_table_add(&p->v, req->body);
+ val = p->v.data;
+ return apreq_value_to_param(val);
+ }
+ }
+
+
+ switch (req->body_status) {
+
+ case APR_SUCCESS:
+
+ val = apr_table_get(req->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+ return NULL;
+
+
+ case APR_EINIT:
+
+ init_body(handle);
+ if (req->body_status != APR_INCOMPLETE)
+ return NULL;
+ cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE);
+
+
+ case APR_INCOMPLETE:
+
+ val = apr_table_get(req->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+
+ /* Not seen yet, so we need to scan for
+ param while prefetching the body */
+
+ hook_ctx = apr_palloc(handle->pool, sizeof *hook_ctx);
+
+ if (req->find_param == NULL)
+ req->find_param = apreq_hook_make(handle->pool,
+ apreq_hook_find_param,
+ NULL, NULL);
+ h = req->find_param;
+ h->next = req->parser->hook;
+ req->parser->hook = h;
+ h->ctx = hook_ctx;
+ hook_ctx->name = name;
+ hook_ctx->param = NULL;
+ hook_ctx->prev = req->parser->hook;
+
+ do {
+ cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE);
+ if (hook_ctx->param != NULL)
+ return hook_ctx->param;
+ } while (req->body_status == APR_INCOMPLETE);
+
+ req->parser->hook = h->next;
+ return NULL;
+
+
+ default:
+
+ if (req->body == NULL)
+ return NULL;
+
+ val = apr_table_get(req->body, name);
+ if (val != NULL)
+ return apreq_value_to_param(val);
+ return NULL;
+ }
+
+ /* not reached */
+ return NULL;
+}
+
+static apr_status_t cgi_parser_get(apreq_handle_t *handle,
+ const apreq_parser_t **parser)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ *parser = req->parser;
+ return APR_SUCCESS;
+}
+
+static apr_status_t cgi_parser_set(apreq_handle_t *handle,
+ apreq_parser_t *parser)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->parser == NULL) {
+
+ if (req->hook_queue != NULL) {
+ apr_status_t s = apreq_parser_add_hook(parser, req->hook_queue);
+ if (s != APR_SUCCESS)
+ return s;
+ }
+ if (req->temp_dir != NULL) {
+ parser->temp_dir = req->temp_dir;
+ }
+ if (req->brigade_limit < parser->brigade_limit) {
+ parser->brigade_limit = req->brigade_limit;
+ }
+
+ req->hook_queue = NULL;
+ req->parser = parser;
+ return APR_SUCCESS;
+ }
+ else
+ return APREQ_ERROR_MISMATCH;
+}
+
+
+static apr_status_t cgi_hook_add(apreq_handle_t *handle,
+ apreq_hook_t *hook)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->parser != NULL) {
+ return apreq_parser_add_hook(req->parser, hook);
+ }
+ else if (req->hook_queue != NULL) {
+ apreq_hook_t *h = req->hook_queue;
+ while (h->next != NULL)
+ h = h->next;
+ h->next = hook;
+ }
+ else {
+ req->hook_queue = hook;
+ }
+ return APR_SUCCESS;
+
+}
+
+static apr_status_t cgi_brigade_limit_set(apreq_handle_t *handle,
+ apr_size_t bytes)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ apr_size_t *limit = (req->parser == NULL)
+ ? &req->brigade_limit
+ : &req->parser->brigade_limit;
+
+ if (*limit > bytes) {
+ *limit = bytes;
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_MISMATCH;
+}
+
+static apr_status_t cgi_brigade_limit_get(apreq_handle_t *handle,
+ apr_size_t *bytes)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ *bytes = (req->parser == NULL)
+ ? req->brigade_limit
+ : req->parser->brigade_limit;
+
+ return APR_SUCCESS;
+}
+
+static apr_status_t cgi_read_limit_set(apreq_handle_t *handle,
+ apr_uint64_t bytes)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+
+ if (req->read_limit > bytes && req->bytes_read < bytes) {
+ req->read_limit = bytes;
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_MISMATCH;
+}
+
+
+static apr_status_t cgi_read_limit_get(apreq_handle_t *handle,
+ apr_uint64_t *bytes)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ *bytes = req->read_limit;
+ return APR_SUCCESS;
+}
+
+
+static apr_status_t cgi_temp_dir_set(apreq_handle_t *handle,
+ const char *path)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ const char **temp_dir = (req->parser == NULL)
+ ? &req->temp_dir
+ : &req->parser->temp_dir;
+
+
+ if (*temp_dir == NULL && req->bytes_read == 0) {
+ if (path != NULL)
+ *temp_dir = apr_pstrdup(handle->pool, path);
+ return APR_SUCCESS;
+ }
+
+ return APREQ_ERROR_MISMATCH;
+}
+
+
+static apr_status_t cgi_temp_dir_get(apreq_handle_t *handle,
+ const char **path)
+{
+ struct cgi_handle *req = (struct cgi_handle *)handle;
+ *path = (req->parser == NULL)
+ ? req->temp_dir
+ : req->parser->temp_dir;
+ return APR_SUCCESS;
+}
+
+
+
+#ifdef APR_POOL_DEBUG
+static apr_status_t ba_cleanup(void *data)
+{
+ apr_bucket_alloc_t *ba = data;
+ apr_bucket_alloc_destroy(ba);
+ return APR_SUCCESS;
+}
+#endif
+
+/** Determine if we're interactive mode or not. Order is
+ QUERY_STRING ? NO : Interactive
+
+ I think we should just rely on GATEWAY_INTERFACE to set
+ non-interactive mode, and be interactive if it's not there
+
+ Behaviour change should really be:
+ Always check query_string before prompting user,
+ but rewrite body/cookies to get if interactive
+
+ Definately more work needed here...
+*/
+static int is_interactive_mode(apr_pool_t *pool) {
+ char *value = NULL, qs[] = "GATEWAY_INTERFACE";
+ apr_status_t rv;
+
+ rv = apr_env_get(&value, qs, pool);
+ if (rv != APR_SUCCESS)
+ if (rv == APR_ENOENT)
+ return 1;
+
+ /** handle else? (!SUCCESS && !ENOENT) */
+ return 0;
+}
+
+static APREQ_MODULE(cgi, 20090110);
+
+APREQ_DECLARE(apreq_handle_t *)apreq_handle_cgi(apr_pool_t *pool)
+{
+ apr_bucket_alloc_t *ba;
+ struct cgi_handle *req;
+ void *data;
+
+ apr_pool_userdata_get(&data, USER_DATA_KEY, pool);
+
+ if (data != NULL)
+ return data;
+
+ req = apr_pcalloc(pool, sizeof *req);
+ ba = apr_bucket_alloc_create(pool);
+
+ /* check pool's userdata first. */
+
+ req->handle.module = &cgi_module;
+ req->handle.pool = pool;
+ req->handle.bucket_alloc = ba;
+ req->read_limit = (apr_uint64_t) -1;
+ req->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT;
+
+ req->args = apr_table_make(pool, APREQ_DEFAULT_NELTS);
+ req->body = apr_table_make(pool, APREQ_DEFAULT_NELTS);
+ req->jar = apr_table_make(pool, APREQ_DEFAULT_NELTS);
+
+ req->args_status =
+ req->jar_status =
+ req->body_status = APR_EINIT;
+
+ if (is_interactive_mode(pool)) {
+ req->interactive_mode = 1;
+ apr_file_open_stdout(&(req->sout), pool);
+ apr_file_open_stdin(&(req->sin), pool);
+ req->promptstr=apr_pstrdup(pool, DEFAULT_PROMPT);
+ }
+
+ apr_pool_userdata_setn(&req->handle, USER_DATA_KEY, NULL, pool);
+
+#ifdef APR_POOL_DEBUG
+ apr_pool_cleanup_register(pool, ba, ba_cleanup, ba_cleanup);
+#endif
+
+ return &req->handle;
+}