blob: aa4f213d0b825541d17cb2a41b7245d703b87644 [file] [log] [blame]
/*
** Copyright 2003-2004 The Apache Software Foundation
**
** Licensed 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 "apreq.h"
#include "apreq_env.h"
#include "apr_strings.h"
#include "apr_lib.h"
#include "apr_env.h"
#include "apr_file_io.h"
static const apreq_env_t *apreq_env;
extern void apreq_parser_initialize(void);
APREQ_DECLARE(const apreq_env_t *) apreq_env_module(const apreq_env_t *mod)
{
apreq_parser_initialize();
if (mod != NULL) {
const apreq_env_t *old_mod = apreq_env;
apreq_env = mod;
return old_mod;
}
return apreq_env;
}
APREQ_DECLARE_NONSTD(void) apreq_log(const char *file, int line,
int level, apr_status_t status,
void *env, const char *fmt, ...)
{
va_list vp;
va_start(vp, fmt);
apreq_env->log(file,line,level,status,env,fmt,vp);
va_end(vp);
}
APREQ_DECLARE(apr_pool_t *) apreq_env_pool(void *env)
{
return apreq_env->pool(env);
}
APREQ_DECLARE(apr_bucket_alloc_t *) apreq_env_bucket_alloc(void *env)
{
return apreq_env->bucket_alloc(env);
}
APREQ_DECLARE(apreq_jar_t *) apreq_env_jar(void *env, apreq_jar_t *jar)
{
return apreq_env->jar(env,jar);
}
APREQ_DECLARE(apreq_request_t *) apreq_env_request(void *env,
apreq_request_t *req)
{
return apreq_env->request(env,req);
}
APREQ_DECLARE(const char *) apreq_env_query_string(void *env)
{
return apreq_env->query_string(env);
}
APREQ_DECLARE(const char *) apreq_env_header_in(void *env, const char *name)
{
return apreq_env->header_in(env, name);
}
APREQ_DECLARE(apr_status_t)apreq_env_header_out(void *env,
const char *name,
char *val)
{
return apreq_env->header_out(env,name,val);
}
APREQ_DECLARE(apr_status_t) apreq_env_read(void *env,
apr_read_type_e block,
apr_off_t bytes)
{
return apreq_env->read(env,block,bytes);
}
APREQ_DECLARE(const char *) apreq_env_temp_dir(void *env, const char *path)
{
if (path != NULL)
/* ensure path is a valid pointer during the entire request */
path = apr_pstrdup(apreq_env_pool(env),path);
return apreq_env->temp_dir(env,path);
}
APREQ_DECLARE(apr_off_t) apreq_env_max_body(void *env, apr_off_t bytes)
{
return apreq_env->max_body(env,bytes);
}
APREQ_DECLARE(apr_ssize_t) apreq_env_max_brigade(void *env, apr_ssize_t bytes)
{
return apreq_env->max_brigade(env,bytes);
}
#define dP apr_pool_t *p = (apr_pool_t *)env
static struct {
apreq_request_t *req;
apreq_jar_t *jar;
apr_status_t status;
const char *temp_dir;
apr_off_t max_body;
apr_ssize_t max_brigade;
apr_bucket_brigade *in;
apr_off_t bytes_read;
} ctx = {NULL, NULL, APR_SUCCESS, NULL, -1, APREQ_MAX_BRIGADE_LEN, NULL, 0};
#define CRLF "\015\012"
#define APREQ_ENV_STATUS(rc_run, k) do { \
apr_status_t rc = rc_run; \
if (rc != APR_SUCCESS) { \
apreq_log(APREQ_DEBUG APR_EGENERAL, p, \
"Lookup of %s failed: status=%d", k, rc); \
} \
} while (0)
/**
* @defgroup apreq_cgi Common Gateway Interface
* @ingroup apreq_env
* @brief CGI module included in the libapreq2 library.
*
* CGI is the default environment module included in libapreq2...
* XXX add more info here XXX
*
* @{
*/
#define APREQ_MODULE_NAME "CGI"
#define APREQ_MODULE_MAGIC_NUMBER 20050112
static apr_pool_t *cgi_pool(void *env)
{
return (apr_pool_t *)env;
}
static apr_status_t bucket_alloc_cleanup(void *data)
{
apr_bucket_alloc_t *ba = data;
apr_bucket_alloc_destroy(ba);
return APR_SUCCESS;
}
static apr_bucket_alloc_t *cgi_bucket_alloc(void *env)
{
dP;
apr_bucket_alloc_t *ba = apr_bucket_alloc_create(p);
apr_pool_cleanup_register(p, ba, bucket_alloc_cleanup,
bucket_alloc_cleanup);
return ba;
}
static const char *cgi_query_string(void *env)
{
dP;
char *value = NULL, qs[] = "QUERY_STRING";
APREQ_ENV_STATUS(apr_env_get(&value, qs, p), qs);
return value;
}
static const char *cgi_header_in(void *env,
const char *name)
{
dP;
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_") */
}
APREQ_ENV_STATUS(apr_env_get(&value, key, p), key);
return value;
}
static apr_status_t cgi_header_out(void *env, const char *name,
char *value)
{
dP;
apr_file_t *out;
int bytes;
apr_status_t s = apr_file_open_stdout(&out, p);
apreq_log(APREQ_DEBUG s, p, "Setting header: %s => %s", name, value);
bytes = apr_file_printf(out, "%s: %s" CRLF, name, value);
apr_file_flush(out);
return bytes > 0 ? APR_SUCCESS : APR_EGENERAL;
}
static apreq_jar_t *cgi_jar(void *env, apreq_jar_t *jar)
{
if (jar != NULL) {
apreq_jar_t *old_jar = ctx.jar;
ctx.jar = jar;
return old_jar;
}
return ctx.jar;
}
static apreq_request_t *cgi_request(void *env,
apreq_request_t *req)
{
apreq_parser_initialize();
if (req != NULL) {
apreq_request_t *old_req = ctx.req;
ctx.req = req;
return old_req;
}
return ctx.req;
}
typedef struct {
char *t_name;
int t_val;
} TRANS;
static const TRANS priorities[] = {
{"emerg", APREQ_LOG_EMERG},
{"alert", APREQ_LOG_ALERT},
{"crit", APREQ_LOG_CRIT},
{"error", APREQ_LOG_ERR},
{"warn", APREQ_LOG_WARNING},
{"notice", APREQ_LOG_NOTICE},
{"info", APREQ_LOG_INFO},
{"debug", APREQ_LOG_DEBUG},
{NULL, -1},
};
static void cgi_log(const char *file, int line, int level,
apr_status_t status, void *env, const char *fmt,
va_list vp)
{
dP;
char buf[256];
char *log_level_string, *remote_addr;
unsigned log_level = APREQ_LOG_WARNING;
char date[APR_CTIME_LEN];
#ifndef WIN32
apr_file_t *err;
#endif
if (apr_env_get(&log_level_string, "LOG_LEVEL", p) == APR_SUCCESS)
log_level = (log_level_string[0] - '0');
level &= APREQ_LOG_LEVELMASK;
if (level > log_level)
return;
if (apr_env_get(&remote_addr, "REMOTE_ADDR", p) != APR_SUCCESS)
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
}
static apr_status_t cgi_read(void *env,
apr_read_type_e block,
apr_off_t bytes)
{
dP;
apreq_request_t *req = apreq_request(env, NULL);
apr_bucket *e;
apr_status_t s;
if (ctx.in == NULL) {
apr_bucket_alloc_t *alloc = cgi_bucket_alloc(p);
apr_bucket *stdin_pipe, *eos = apr_bucket_eos_create(alloc);
apr_file_t *in;
apr_file_open_stdin(&in, p);
stdin_pipe = apr_bucket_pipe_create(in,alloc);
ctx.in = apr_brigade_create(p, alloc);
APR_BRIGADE_INSERT_HEAD(ctx.in, stdin_pipe);
APR_BRIGADE_INSERT_TAIL(ctx.in, eos);
ctx.status = APR_INCOMPLETE;
if (ctx.max_body >= 0) {
const char *cl = apreq_env_header_in(env, "Content-Length");
if (cl != NULL) {
char *dummy;
apr_int64_t content_length = apr_strtoi64(cl,&dummy,0);
if (dummy == NULL || *dummy != 0) {
apreq_log(APREQ_ERROR APR_EGENERAL, env,
"Invalid Content-Length header (%s)", cl);
ctx.status = APR_EGENERAL;
req->body_status = APR_EGENERAL;
}
else if (content_length > (apr_int64_t)ctx.max_body) {
apreq_log(APREQ_ERROR APR_EGENERAL, env,
"Content-Length header (%s) exceeds configured "
"max_body limit (%" APR_OFF_T_FMT ")",
cl, ctx.max_body);
ctx.status = APR_EGENERAL;
req->body_status = APR_EGENERAL;
}
}
}
}
if (ctx.status != APR_INCOMPLETE)
return ctx.status;
switch (s = apr_brigade_partition(ctx.in, bytes, &e)) {
apr_bucket_brigade *bb;
apr_off_t len;
case APR_SUCCESS:
bb = ctx.in;
ctx.in = apr_brigade_split(bb, e);
ctx.bytes_read += bytes;
if (ctx.max_body >= 0) {
if (ctx.bytes_read > ctx.max_body) {
apreq_log(APREQ_ERROR APR_EGENERAL, env,
"Bytes read (%" APR_OFF_T_FMT
") exceeds configured limit (%" APR_OFF_T_FMT ")",
ctx.bytes_read, ctx.max_body);
req->body_status = APR_EGENERAL;
return ctx.status = APR_EGENERAL;
}
}
ctx.status = apreq_parse_request(req, bb);
apr_brigade_cleanup(bb);
break;
case APR_INCOMPLETE:
bb = ctx.in;
ctx.in = apr_brigade_split(bb, e);
s = apr_brigade_length(bb,1,&len);
if (s != APR_SUCCESS)
return ctx.status = s;
ctx.bytes_read += len;
if (ctx.max_body >= 0) {
if (ctx.bytes_read > ctx.max_body) {
apreq_log(APREQ_ERROR APR_EGENERAL, env,
"Bytes read (%" APR_OFF_T_FMT
") exceeds configured limit (%" APR_OFF_T_FMT ")",
ctx.bytes_read, ctx.max_body);
req->body_status = APR_EGENERAL;
return ctx.status = APR_EGENERAL;
}
}
ctx.status = apreq_parse_request(req, bb);
apr_brigade_cleanup(bb);
break;
default:
ctx.status = s;
}
return ctx.status;
}
static const char *cgi_temp_dir(void *env, const char *path)
{
if (path != NULL) {
dP;
const char *rv = ctx.temp_dir;
ctx.temp_dir = apr_pstrdup(p, path);
return rv;
}
if (ctx.temp_dir == NULL) {
dP;
if (apr_temp_dir_get(&ctx.temp_dir, p) != APR_SUCCESS)
ctx.temp_dir = NULL;
}
return ctx.temp_dir;
}
static apr_off_t cgi_max_body(void *env, apr_off_t bytes)
{
if (bytes >= 0) {
apr_off_t rv = ctx.max_body;
ctx.max_body = bytes;
return rv;
}
return ctx.max_body;
}
static apr_ssize_t cgi_max_brigade(void *env, apr_ssize_t bytes)
{
if (bytes >= 0) {
apr_ssize_t rv = ctx.max_brigade;
ctx.max_brigade = bytes;
return rv;
}
return ctx.max_brigade;
}
static APREQ_ENV_MODULE(cgi, APREQ_MODULE_NAME,
APREQ_MODULE_MAGIC_NUMBER);
static const apreq_env_t *apreq_env = &cgi_module;
/** @} */