/* ** 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)); if (p == NULL) return APR_ENOMEM; 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)); if (p == NULL) return APR_ENOMEM; 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)); if (p == NULL) return NULL; 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)); if (p == NULL) return NULL; 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)); if (p == NULL) return APR_ENOMEM; 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)); if (p == NULL) return NULL; 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; }