| /* |
| ** 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); |
| if (param == NULL) |
| return APR_ENOMEM; |
| *(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; |
| } |
| |
| |