summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES3
-rw-r--r--include/ap_mmn.h3
-rw-r--r--include/httpd.h27
-rw-r--r--include/mod_request.h50
-rw-r--r--modules/aaa/mod_auth_form.c10
-rw-r--r--modules/filters/mod_request.c216
-rw-r--r--server/util.c222
7 files changed, 254 insertions, 277 deletions
diff --git a/CHANGES b/CHANGES
index 6935ef16cb..1661601777 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index de64156afe..edaac77444 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -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" */
diff --git a/include/httpd.h b/include/httpd.h
index 36faa74578..b2357b5918 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -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
diff --git a/include/mod_request.h b/include/mod_request.h
index b65c4dcfef..ed28c05d20 100644
--- a/include/mod_request.h
+++ b/include/mod_request.h
@@ -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));
diff --git a/modules/aaa/mod_auth_form.c b/modules/aaa/mod_auth_form.c
index 05ac6c26cb..41e1f2fe36 100644
--- a/modules/aaa/mod_auth_form.c
+++ b/modules/aaa/mod_auth_form.c
@@ -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;
}
diff --git a/modules/filters/mod_request.c b/modules/filters/mod_request.c
index 3693db8d25..cebe539a19 100644
--- a/modules/filters/mod_request.c
+++ b/modules/filters/mod_request.c
@@ -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);
}
diff --git a/server/util.c b/server/util.c
index b61aa8eb80..c443c942a3 100644
--- a/server/util.c
+++ b/server/util.c
@@ -2147,7 +2147,7 @@ AP_DECLARE(int) ap_request_has_body(request_rec *r)
char *estr;
const char *cls;
int has_body;
-
+
has_body = (!r->header_only
&& (r->kept_body
|| apr_table_get(r->headers_in, "Transfer-Encoding")
@@ -2168,7 +2168,7 @@ AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
}
AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
-
+
for ( ; *src; src++, dest++)
{
if (!apr_isprint(*src))
@@ -2180,7 +2180,7 @@ AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
}
*dest = '\0';
return APR_SUCCESS;
-
+
}
AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
@@ -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;
+
+}