/* ** 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; }