|  | /* | 
|  | **  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. | 
|  | */ | 
|  |  | 
|  | #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" | 
|  |  | 
|  | #include "httpd.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-existent 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) | 
|  | { | 
|  | long p = (long)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+5; *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) { | 
|  | apr_off_t content_length; | 
|  |  | 
|  | if (!ap_parse_strict_length(&content_length, cl_header)) { | 
|  | 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; | 
|  | } | 
|  | 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); | 
|  | } | 
|  | 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 | 
|  |  | 
|  | Definitely 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; | 
|  | } |