blob: 779115dd6e484f3c835cb821855aa9200595dd1a [file] [log] [blame]
/*
** 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;
}