Expose "new" ap_parse_form_data() function instead of requiring

mod_request for any module that may want to parse form data...

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1072099 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jim Jagielski
2011-02-18 18:40:31 +00:00
parent aac783bdf5
commit ec88a92d83
7 changed files with 254 additions and 277 deletions

View File

@ -2,6 +2,9 @@
Changes with Apache 2.3.11
*) core: new util function: ap_parse_form_data(). Previously,
this capability was tucked away in mod_request. [Jim Jagielski]
*) core: new hook: ap_run_pre_read_request. [Jim Jagielski]
*) mod_cache: When a request other than GET or HEAD arrives, we must

View File

@ -300,7 +300,8 @@
* 20110117.1 (2.3.11-dev) Add ap_pstr2_alnum() and ap_str2_alnum()
* 20110203.0 (2.3.11-dev) Raise DYNAMIC_MODULE_LIMIT to 256
* 20110203.1 (2.3.11-dev) Add ap_state_query()
* 20110203.2 (2.3.11-dev) Add ap_run_pre_read_request() hook
* 20110203.2 (2.3.11-dev) Add ap_run_pre_read_request() hook and
* ap_parse_form_data() util
*/
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */

View File

@ -1853,6 +1853,33 @@ AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
*/
AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest);
/**
* Structure to store the contents of an HTTP form of the type
* application/x-www-form-urlencoded.
*
* Currently it contains the name as a char* of maximum length
* HUGE_STRING_LEN, and a value in the form of a bucket brigade
* of arbitrary length.
*/
typedef struct {
const char *name;
apr_bucket_brigade *value;
} ap_form_pair_t;
/**
* Read the body and parse any form found, which must be of the
* type application/x-www-form-urlencoded.
* @param r request containing POSTed form data
* @param f filter
* @param ptr returned array of ap_form_pair_t
* @param num max num of params or -1 for unlimited
* @param size max size allowed for parsed data
* @return OK or HTTP error
*/
AP_DECLARE(int) ap_parse_form_data(request_rec *r, struct ap_filter_t *f,
apr_array_header_t **ptr,
apr_size_t num, apr_size_t size);
/* Misc system hackery */
/**
* Given the name of an object in the file system determine if it is a directory

View File

@ -52,56 +52,6 @@ typedef struct {
int keep_body_set;
} request_dir_conf;
/**
* Structure to store the contents of an HTTP form of the type
* application/x-www-form-urlencoded.
*
* Currently it contains the name as a char* of maximum length
* HUGE_STRING_LEN, and a value in the form of a bucket brigade
* of arbitrary length.
*/
typedef struct {
const char *name;
apr_bucket_brigade *value;
} ap_form_pair_t;
/**
* Read the body and parse any form found, which must be of the
* type application/x-www-form-urlencoded.
*
* Name/value pairs are returned in an array, with the names as
* strings with a maximum length of HUGE_STRING_LEN, and the
* values as bucket brigades. This allows values to be arbitrarily
* large.
*
* All url-encoding is removed from both the names and the values
* on the fly. The names are interpreted as strings, while the
* values are interpreted as blocks of binary data, that may
* contain the 0 character.
*
* In order to ensure that resource limits are not exceeded, a
* maximum size must be provided. If the sum of the lengths of
* the names and the values exceed this size, this function
* will return HTTP_REQUEST_ENTITY_TOO_LARGE.
*
* An optional number of parameters can be provided, if the number
* of parameters provided exceeds this amount, this function will
* return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
* no limit is imposed, and the number of parameters is in turn
* constrained by the size parameter above.
*
* This function honours any kept_body configuration, and the
* original raw request body will be saved to the kept_body brigade
* if so configured, just as ap_discard_request_body does.
*
* NOTE: File upload is not yet supported, but can be without change
* to the function call.
*/
APR_DECLARE_OPTIONAL_FN(int, ap_parse_request_form, (request_rec * r, ap_filter_t * f,
apr_array_header_t ** ptr,
apr_size_t num, apr_size_t size));
APR_DECLARE_OPTIONAL_FN(void, ap_request_insert_filter, (request_rec * r));
APR_DECLARE_OPTIONAL_FN(void, ap_request_remove_filter, (request_rec * r));

View File

@ -46,9 +46,6 @@ static void (*ap_session_get_fn) (request_rec * r, session_rec * z,
const char *key, const char **value) = NULL;
static void (*ap_session_set_fn) (request_rec * r, session_rec * z,
const char *key, const char *value) = NULL;
static int (*ap_parse_request_form_fn) (request_rec * r, ap_filter_t *f,
apr_array_header_t ** ptr,
apr_size_t num, apr_size_t size) = NULL;
static void (*ap_request_insert_filter_fn) (request_rec * r) = NULL;
static void (*ap_request_remove_filter_fn) (request_rec * r) = NULL;
@ -187,11 +184,10 @@ static const char *add_authn_provider(cmd_parms * cmd, void *config,
}
}
if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
ap_parse_request_form_fn = APR_RETRIEVE_OPTIONAL_FN(ap_parse_request_form);
if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
ap_request_insert_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_insert_filter);
ap_request_remove_filter_fn = APR_RETRIEVE_OPTIONAL_FN(ap_request_remove_filter);
if (!ap_parse_request_form_fn || !ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
if (!ap_request_insert_filter_fn || !ap_request_remove_filter_fn) {
return "You must load mod_request to enable the mod_auth_form "
"functions";
}
@ -607,7 +603,7 @@ static int get_form_auth(request_rec * r,
return OK;
}
res = ap_parse_request_form_fn(r, NULL, &pairs, -1, conf->form_size);
res = ap_parse_form_data(r, NULL, &pairs, -1, conf->form_size);
if (res != OK) {
return res;
}

View File

@ -267,221 +267,6 @@ static apr_status_t kept_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
}
/* form parsing stuff */
typedef enum {
FORM_NORMAL,
FORM_AMP,
FORM_NAME,
FORM_VALUE,
FORM_PERCENTA,
FORM_PERCENTB,
FORM_ABORT
} ap_form_type_t;
/**
* Read the body and parse any form found, which must be of the
* type application/x-www-form-urlencoded.
*
* Name/value pairs are returned in an array, with the names as
* strings with a maximum length of HUGE_STRING_LEN, and the
* values as bucket brigades. This allows values to be arbitrarily
* large.
*
* All url-encoding is removed from both the names and the values
* on the fly. The names are interpreted as strings, while the
* values are interpreted as blocks of binary data, that may
* contain the 0 character.
*
* In order to ensure that resource limits are not exceeded, a
* maximum size must be provided. If the sum of the lengths of
* the names and the values exceed this size, this function
* will return HTTP_REQUEST_ENTITY_TOO_LARGE.
*
* An optional number of parameters can be provided, if the number
* of parameters provided exceeds this amount, this function will
* return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
* no limit is imposed, and the number of parameters is in turn
* constrained by the size parameter above.
*
* This function honours any kept_body configuration, and the
* original raw request body will be saved to the kept_body brigade
* if so configured, just as ap_discard_request_body does.
*
* NOTE: File upload is not yet supported, but can be without change
* to the function call.
*/
static int ap_parse_request_form(request_rec * r, ap_filter_t * f,
apr_array_header_t ** ptr,
apr_size_t num, apr_size_t usize)
{
apr_bucket_brigade *bb = NULL;
int seen_eos = 0;
char buffer[HUGE_STRING_LEN + 1];
const char *ct;
apr_size_t offset = 0;
apr_ssize_t size;
ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
ap_form_pair_t *pair = NULL;
apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
char hi = 0;
char low = 0;
*ptr = pairs;
/* sanity check - we only support forms for now */
ct = apr_table_get(r->headers_in, "Content-Type");
if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
return ap_discard_request_body(r);
}
if (usize > APR_SIZE_MAX >> 1)
size = APR_SIZE_MAX >> 1;
else
size = usize;
if (!f) {
f = r->input_filters;
}
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
do {
apr_bucket *bucket = NULL, *last = NULL;
int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, HUGE_STRING_LEN);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(bb);
return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
}
for (bucket = APR_BRIGADE_FIRST(bb);
bucket != APR_BRIGADE_SENTINEL(bb);
last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
const char *data;
apr_size_t len, slide;
if (last) {
apr_bucket_delete(last);
}
if (APR_BUCKET_IS_EOS(bucket)) {
seen_eos = 1;
break;
}
if (bucket->length == 0) {
continue;
}
rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(bb);
return HTTP_BAD_REQUEST;
}
slide = len;
while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
char c = *data++;
if ('+' == c) {
c = ' ';
}
else if ('&' == c) {
state = FORM_AMP;
}
if ('%' == c) {
percent = FORM_PERCENTA;
continue;
}
if (FORM_PERCENTA == percent) {
if (c >= 'a') {
hi = c - 'a' + 10;
}
else if (c >= 'A') {
hi = c - 'A' + 10;
}
else if (c >= '0') {
hi = c - '0';
}
hi = hi << 4;
percent = FORM_PERCENTB;
continue;
}
if (FORM_PERCENTB == percent) {
if (c >= 'a') {
low = c - 'a' + 10;
}
else if (c >= 'A') {
low = c - 'A' + 10;
}
else if (c >= '0') {
low = c - '0';
}
c = low | hi;
percent = FORM_NORMAL;
}
switch (state) {
case FORM_AMP:
if (pair) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
}
state = FORM_NAME;
pair = NULL;
offset = 0;
num--;
break;
case FORM_NAME:
if (offset < HUGE_STRING_LEN) {
if ('=' == c) {
buffer[offset] = 0;
offset = 0;
pair = (ap_form_pair_t *) apr_array_push(pairs);
pair->name = apr_pstrdup(r->pool, buffer);
pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
state = FORM_VALUE;
}
else {
buffer[offset++] = c;
size--;
}
}
else {
state = FORM_ABORT;
}
break;
case FORM_VALUE:
if (offset >= HUGE_STRING_LEN) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
offset = 0;
}
buffer[offset++] = c;
size--;
break;
default:
break;
}
}
}
apr_brigade_cleanup(bb);
} while (!seen_eos);
if (FORM_ABORT == state || size < 0 || num == 0) {
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
else if (FORM_VALUE == state && pair && offset > 0) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
}
return OK;
}
/**
* Check whether this filter is not already present.
*/
@ -596,7 +381,6 @@ static void register_hooks(apr_pool_t *p)
ap_register_input_filter(KEPT_BODY_FILTER, kept_body_filter,
kept_body_filter_init, AP_FTYPE_RESOURCE);
ap_hook_insert_filter(ap_request_insert_filter, NULL, NULL, APR_HOOK_LAST);
APR_REGISTER_OPTIONAL_FN(ap_parse_request_form);
APR_REGISTER_OPTIONAL_FN(ap_request_insert_filter);
APR_REGISTER_OPTIONAL_FN(ap_request_remove_filter);
}

View File

@ -2192,3 +2192,219 @@ AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
*dest = new;
return ap_str2_alnum(src, new);
}
/**
* Read the body and parse any form found, which must be of the
* type application/x-www-form-urlencoded.
*
* Name/value pairs are returned in an array, with the names as
* strings with a maximum length of HUGE_STRING_LEN, and the
* values as bucket brigades. This allows values to be arbitrarily
* large.
*
* All url-encoding is removed from both the names and the values
* on the fly. The names are interpreted as strings, while the
* values are interpreted as blocks of binary data, that may
* contain the 0 character.
*
* In order to ensure that resource limits are not exceeded, a
* maximum size must be provided. If the sum of the lengths of
* the names and the values exceed this size, this function
* will return HTTP_REQUEST_ENTITY_TOO_LARGE.
*
* An optional number of parameters can be provided, if the number
* of parameters provided exceeds this amount, this function will
* return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
* no limit is imposed, and the number of parameters is in turn
* constrained by the size parameter above.
*
* This function honours any kept_body configuration, and the
* original raw request body will be saved to the kept_body brigade
* if so configured, just as ap_discard_request_body does.
*
* NOTE: File upload is not yet supported, but can be without change
* to the function call.
*/
/* form parsing stuff */
typedef enum {
FORM_NORMAL,
FORM_AMP,
FORM_NAME,
FORM_VALUE,
FORM_PERCENTA,
FORM_PERCENTB,
FORM_ABORT
} ap_form_type_t;
AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
apr_array_header_t **ptr,
apr_size_t num, apr_size_t usize)
{
apr_bucket_brigade *bb = NULL;
int seen_eos = 0;
char buffer[HUGE_STRING_LEN + 1];
const char *ct;
apr_size_t offset = 0;
apr_ssize_t size;
ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
ap_form_pair_t *pair = NULL;
apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
char hi = 0;
char low = 0;
*ptr = pairs;
/* sanity check - we only support forms for now */
ct = apr_table_get(r->headers_in, "Content-Type");
if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
return ap_discard_request_body(r);
}
if (usize > APR_SIZE_MAX >> 1)
size = APR_SIZE_MAX >> 1;
else
size = usize;
if (!f) {
f = r->input_filters;
}
bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
do {
apr_bucket *bucket = NULL, *last = NULL;
int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
APR_BLOCK_READ, HUGE_STRING_LEN);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(bb);
return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
}
for (bucket = APR_BRIGADE_FIRST(bb);
bucket != APR_BRIGADE_SENTINEL(bb);
last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
const char *data;
apr_size_t len, slide;
if (last) {
apr_bucket_delete(last);
}
if (APR_BUCKET_IS_EOS(bucket)) {
seen_eos = 1;
break;
}
if (bucket->length == 0) {
continue;
}
rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
if (rv != APR_SUCCESS) {
apr_brigade_destroy(bb);
return HTTP_BAD_REQUEST;
}
slide = len;
while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
char c = *data++;
if ('+' == c) {
c = ' ';
}
else if ('&' == c) {
state = FORM_AMP;
}
if ('%' == c) {
percent = FORM_PERCENTA;
continue;
}
if (FORM_PERCENTA == percent) {
if (c >= 'a') {
hi = c - 'a' + 10;
}
else if (c >= 'A') {
hi = c - 'A' + 10;
}
else if (c >= '0') {
hi = c - '0';
}
hi = hi << 4;
percent = FORM_PERCENTB;
continue;
}
if (FORM_PERCENTB == percent) {
if (c >= 'a') {
low = c - 'a' + 10;
}
else if (c >= 'A') {
low = c - 'A' + 10;
}
else if (c >= '0') {
low = c - '0';
}
c = low | hi;
percent = FORM_NORMAL;
}
switch (state) {
case FORM_AMP:
if (pair) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
}
state = FORM_NAME;
pair = NULL;
offset = 0;
num--;
break;
case FORM_NAME:
if (offset < HUGE_STRING_LEN) {
if ('=' == c) {
buffer[offset] = 0;
offset = 0;
pair = (ap_form_pair_t *) apr_array_push(pairs);
pair->name = apr_pstrdup(r->pool, buffer);
pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
state = FORM_VALUE;
}
else {
buffer[offset++] = c;
size--;
}
}
else {
state = FORM_ABORT;
}
break;
case FORM_VALUE:
if (offset >= HUGE_STRING_LEN) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
offset = 0;
}
buffer[offset++] = c;
size--;
break;
default:
break;
}
}
}
apr_brigade_cleanup(bb);
} while (!seen_eos);
if (FORM_ABORT == state || size < 0 || num == 0) {
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
else if (FORM_VALUE == state && pair && offset > 0) {
const char *tmp = apr_pmemdup(r->pool, buffer, offset);
apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(pair->value, b);
}
return OK;
}