diff options
Diffstat (limited to 'srclib/libapreq/library/parser_urlencoded.c')
-rw-r--r-- | srclib/libapreq/library/parser_urlencoded.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/srclib/libapreq/library/parser_urlencoded.c b/srclib/libapreq/library/parser_urlencoded.c new file mode 100644 index 0000000000..e90d0dd382 --- /dev/null +++ b/srclib/libapreq/library/parser_urlencoded.c @@ -0,0 +1,275 @@ +/* +** 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. +*/ + +#include "apreq_parser.h" +#include "apreq_util.h" +#include "apreq_error.h" + + +#define PARSER_STATUS_CHECK(PREFIX) do { \ + if (ctx->status == PREFIX##_ERROR) \ + return APREQ_ERROR_GENERAL; \ + else if (ctx->status == PREFIX##_COMPLETE) \ + return APR_SUCCESS; \ + else if (bb == NULL) \ + return APR_INCOMPLETE; \ +} while (0); + + + +struct url_ctx { + apr_bucket_brigade *bb; + apr_size_t nlen; + apr_size_t vlen; + enum { + URL_NAME, + URL_VALUE, + URL_COMPLETE, + URL_ERROR + } status; +}; + + +/******************** application/x-www-form-urlencoded ********************/ + +static apr_status_t split_urlword(apreq_param_t **p, apr_pool_t *pool, + apr_bucket_brigade *bb, + apr_size_t nlen, + apr_size_t vlen) +{ + apreq_param_t *param; + apreq_value_t *v; + apr_bucket *e, *f; + apr_status_t s; + struct iovec vec[APREQ_DEFAULT_NELTS]; + apr_array_header_t arr; + apr_size_t mark; + apreq_charset_t charset; + + if (nlen == 0) + return APR_EBADARG; + + param = apreq_param_make(pool, NULL, nlen, NULL, vlen); + *(const apreq_value_t **)&v = ¶m->v; + + arr.pool = pool; + arr.elt_size = sizeof(struct iovec); + arr.nelts = 0; + arr.nalloc = APREQ_DEFAULT_NELTS; + arr.elts = (char *)vec; + + ++nlen, ++vlen; + e = APR_BRIGADE_FIRST(bb); + + while (!APR_BUCKET_IS_EOS(e)) { + struct iovec *iov = apr_array_push(&arr); + apr_size_t len; + s = apr_bucket_read(e, (const char **)&iov->iov_base, + &len, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + iov->iov_len = len; + nlen -= len; + + e = APR_BUCKET_NEXT(e); + + if (nlen == 0) { + iov->iov_len--; + break; + } + } + + mark = arr.nelts; + + while (!APR_BUCKET_IS_EOS(e)) { + struct iovec *iov = apr_array_push(&arr); + apr_size_t len; + s = apr_bucket_read(e, (const char **)&iov->iov_base, + &len, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + iov->iov_len = len; + vlen -= len; + + e = APR_BUCKET_NEXT(e); + + if (vlen == 0) { + iov->iov_len--; + break; + } + + } + + s = apreq_decodev(v->data, &vlen, + (struct iovec *)arr.elts + mark, arr.nelts - mark); + if (s != APR_SUCCESS) + return s; + + charset = apreq_charset_divine(v->data, vlen); + + v->name = v->data + vlen + 1; + v->dlen = vlen; + + s = apreq_decodev(v->name, &nlen, (struct iovec *)arr.elts, mark); + if (s != APR_SUCCESS) + return s; + + switch (apreq_charset_divine(v->name, nlen)) { + case APREQ_CHARSET_UTF8: + if (charset == APREQ_CHARSET_ASCII) + charset = APREQ_CHARSET_UTF8; + case APREQ_CHARSET_ASCII: + break; + + case APREQ_CHARSET_LATIN1: + if (charset != APREQ_CHARSET_CP1252) + charset = APREQ_CHARSET_LATIN1; + break; + case APREQ_CHARSET_CP1252: + charset = APREQ_CHARSET_CP1252; + } + + v->nlen = nlen; + + while ((f = APR_BRIGADE_FIRST(bb)) != e) + apr_bucket_delete(f); + + apreq_param_tainted_on(param); + apreq_param_charset_set(param, charset); + *p = param; + return APR_SUCCESS; +} + +APREQ_DECLARE_PARSER(apreq_parse_urlencoded) +{ + apr_pool_t *pool = parser->pool; + apr_bucket *e; + struct url_ctx *ctx; + + if (parser->ctx == NULL) { + ctx = apr_pcalloc(pool, sizeof *ctx); + ctx->bb = apr_brigade_create(pool, parser->bucket_alloc); + parser->ctx = ctx; + ctx->status = URL_NAME; + } + else + ctx = parser->ctx; + + PARSER_STATUS_CHECK(URL); + e = APR_BRIGADE_LAST(ctx->bb); + APR_BRIGADE_CONCAT(ctx->bb, bb); + + parse_url_brigade: + + for (e = APR_BUCKET_NEXT(e); + e != APR_BRIGADE_SENTINEL(ctx->bb); + e = APR_BUCKET_NEXT(e)) + { + apreq_param_t *param; + apr_size_t off = 0, dlen; + const char *data; + apr_status_t s; + + if (APR_BUCKET_IS_EOS(e)) { + if (ctx->status == URL_NAME) { + s = APR_SUCCESS; + } + else { + s = split_urlword(¶m, pool, ctx->bb, ctx->nlen, ctx->vlen); + if (parser->hook != NULL && s == APR_SUCCESS) + s = apreq_hook_run(parser->hook, param, NULL); + + if (s == APR_SUCCESS) { + apreq_value_table_add(¶m->v, t); + ctx->status = URL_COMPLETE; + } + else { + ctx->status = URL_ERROR; + } + } + + APR_BRIGADE_CONCAT(bb, ctx->bb); + return s; + } + + s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); + if ( s != APR_SUCCESS ) { + ctx->status = URL_ERROR; + return s; + } + + parse_url_bucket: + + switch (ctx->status) { + + case URL_NAME: + while (off < dlen) { + switch (data[off++]) { + case '=': + apr_bucket_split(e, off); + dlen -= off; + data += off; + off = 0; + e = APR_BUCKET_NEXT(e); + ctx->status = URL_VALUE; + goto parse_url_bucket; + default: + ++ctx->nlen; + } + } + break; + + case URL_VALUE: + while (off < dlen) { + + switch (data[off++]) { + case '&': + case ';': + apr_bucket_split(e, off); + s = split_urlword(¶m, pool, ctx->bb, + ctx->nlen, ctx->vlen); + if (parser->hook != NULL && s == APR_SUCCESS) + s = apreq_hook_run(parser->hook, param, NULL); + + if (s != APR_SUCCESS) { + ctx->status = URL_ERROR; + return s; + } + + apreq_value_table_add(¶m->v, t); + ctx->status = URL_NAME; + ctx->nlen = 0; + ctx->vlen = 0; + e = APR_BRIGADE_SENTINEL(ctx->bb); + goto parse_url_brigade; + + default: + ++ctx->vlen; + } + } + break; + default: + ; /* not reached */ + } + } + apreq_brigade_setaside(ctx->bb, pool); + return APR_INCOMPLETE; +} + + |