| /* |
| ** 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_error.h" |
| #include "apreq_parser.h" |
| #include "apreq_util.h" |
| #include "apr_strings.h" |
| #include "apr_xml.h" |
| #include "apr_hash.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); |
| |
| APREQ_DECLARE(apreq_parser_t *) apreq_parser_make(apr_pool_t *pool, |
| apr_bucket_alloc_t *ba, |
| const char *content_type, |
| apreq_parser_function_t pfn, |
| apr_size_t brigade_limit, |
| const char *temp_dir, |
| apreq_hook_t *hook, |
| void *ctx) |
| { |
| apreq_parser_t *p = apr_palloc(pool, sizeof *p); |
| p->content_type = content_type; |
| p->parser = pfn; |
| p->hook = hook; |
| p->pool = pool; |
| p->bucket_alloc = ba; |
| p->brigade_limit = brigade_limit; |
| p->temp_dir = temp_dir; |
| p->ctx = ctx; |
| return p; |
| } |
| |
| APREQ_DECLARE(apreq_hook_t *) apreq_hook_make(apr_pool_t *pool, |
| apreq_hook_function_t hook, |
| apreq_hook_t *next, |
| void *ctx) |
| { |
| apreq_hook_t *h = apr_palloc(pool, sizeof *h); |
| h->hook = hook; |
| h->next = next; |
| h->pool = pool; |
| h->ctx = ctx; |
| return h; |
| } |
| |
| |
| /*XXX this may need to check the parser's state before modifying the hook list */ |
| APREQ_DECLARE(apr_status_t) apreq_parser_add_hook(apreq_parser_t *p, |
| apreq_hook_t *h) |
| { |
| apreq_hook_t *last = h; |
| |
| while (last->next) |
| last = last->next; |
| |
| last->next = p->hook; |
| p->hook = h; |
| |
| return APR_SUCCESS; |
| } |
| |
| static int default_parsers_lock = 0; |
| static apr_hash_t *default_parsers = NULL; |
| static apr_pool_t *default_parser_pool = NULL; |
| |
| static apr_status_t apreq_parsers_cleanup(void *data) |
| { |
| default_parsers_lock = 0; |
| default_parsers = NULL; |
| default_parser_pool = NULL; |
| |
| return APR_SUCCESS; |
| } |
| |
| APREQ_DECLARE(apr_status_t) apreq_pre_initialize(apr_pool_t *pool) |
| { |
| apr_status_t status; |
| |
| if (default_parser_pool != NULL) |
| return APR_SUCCESS; |
| |
| if (default_parsers_lock) |
| return APREQ_ERROR_GENERAL; |
| |
| status = apr_pool_create(&default_parser_pool, pool); |
| if (status != APR_SUCCESS) |
| return status; |
| apr_pool_tag(default_parser_pool, "apreq_default_parser"); |
| |
| apr_pool_cleanup_register(default_parser_pool, NULL, |
| apreq_parsers_cleanup, |
| apr_pool_cleanup_null); |
| |
| default_parsers = apr_hash_make(default_parser_pool); |
| |
| apreq_register_parser("application/x-www-form-urlencoded", |
| apreq_parse_urlencoded); |
| apreq_register_parser("multipart/form-data", apreq_parse_multipart); |
| apreq_register_parser("multipart/related", apreq_parse_multipart); |
| |
| return APR_SUCCESS; |
| } |
| |
| APREQ_DECLARE(apr_status_t) apreq_post_initialize(apr_pool_t *pool) |
| { |
| (void)pool; |
| |
| if (default_parser_pool == NULL) |
| return APREQ_ERROR_GENERAL; |
| |
| default_parsers_lock = 1; |
| return APR_SUCCESS; |
| } |
| |
| APREQ_DECLARE(apr_status_t) apreq_initialize(apr_pool_t *pool) |
| { |
| apr_status_t s = apreq_pre_initialize(pool); |
| |
| if (s != APR_SUCCESS) |
| return s; |
| |
| return apreq_post_initialize(pool); |
| } |
| |
| |
| APREQ_DECLARE(apr_status_t) apreq_register_parser(const char *enctype, |
| apreq_parser_function_t pfn) |
| { |
| apreq_parser_function_t *f = NULL; |
| |
| if (default_parsers == NULL) |
| return APR_EINIT; |
| |
| if (enctype == NULL) |
| return APR_EINVAL; |
| |
| if (default_parsers_lock) |
| return APREQ_ERROR_GENERAL; |
| |
| if (pfn != NULL) { |
| f = apr_palloc(default_parser_pool, sizeof *f); |
| *f = pfn; |
| } |
| apr_hash_set(default_parsers, apr_pstrdup(default_parser_pool, enctype), |
| APR_HASH_KEY_STRING, f); |
| |
| return APR_SUCCESS; |
| } |
| |
| APREQ_DECLARE(apreq_parser_function_t)apreq_parser(const char *enctype) |
| { |
| apreq_parser_function_t *f; |
| apr_size_t tlen = 0; |
| |
| if (enctype == NULL || default_parsers_lock == 0) |
| return NULL; |
| |
| while (enctype[tlen] && enctype[tlen] != ';') |
| ++tlen; |
| |
| f = apr_hash_get(default_parsers, enctype, tlen); |
| |
| if (f != NULL) |
| return *f; |
| else |
| return NULL; |
| } |
| |
| APREQ_DECLARE_HOOK(apreq_hook_disable_uploads) |
| { |
| return (bb == NULL) ? APR_SUCCESS : APREQ_ERROR_GENERAL; |
| } |
| |
| APREQ_DECLARE_HOOK(apreq_hook_discard_brigade) |
| { |
| apr_status_t s = APR_SUCCESS; |
| if (hook->next) |
| s = apreq_hook_run(hook->next, param, bb); |
| if (bb != NULL) |
| apr_brigade_cleanup(bb); |
| return s; |
| } |
| |
| |
| /* generic parser */ |
| |
| struct gen_ctx { |
| apreq_param_t *param; |
| enum { |
| GEN_INCOMPLETE, |
| GEN_COMPLETE, |
| GEN_ERROR |
| } status; |
| }; |
| |
| APREQ_DECLARE_PARSER(apreq_parse_generic) |
| { |
| struct gen_ctx *ctx = parser->ctx; |
| apr_pool_t *pool = parser->pool; |
| apr_status_t s = APR_SUCCESS; |
| apr_bucket *e = APR_BRIGADE_LAST(bb); |
| unsigned saw_eos = 0; |
| |
| if (ctx == NULL) { |
| parser->ctx = ctx = apr_palloc(pool, sizeof *ctx); |
| ctx->status = GEN_INCOMPLETE; |
| ctx->param = apreq_param_make(pool, |
| "_dummy_", strlen("_dummy_"), "", 0); |
| if (ctx->param == NULL) |
| return APR_ENOMEM; |
| ctx->param->upload = apr_brigade_create(pool, parser->bucket_alloc); |
| ctx->param->info = apr_table_make(pool, APREQ_DEFAULT_NELTS); |
| } |
| |
| |
| PARSER_STATUS_CHECK(GEN); |
| |
| while (e != APR_BRIGADE_SENTINEL(bb)) { |
| if (APR_BUCKET_IS_EOS(e)) { |
| saw_eos = 1; |
| break; |
| } |
| e = APR_BUCKET_PREV(e); |
| } |
| |
| if (parser->hook != NULL) { |
| s = apreq_hook_run(parser->hook, ctx->param, bb); |
| if (s != APR_SUCCESS) { |
| ctx->status = GEN_ERROR; |
| return s; |
| } |
| } |
| |
| apreq_brigade_setaside(bb, pool); |
| s = apreq_brigade_concat(pool, parser->temp_dir, parser->brigade_limit, |
| ctx->param->upload, bb); |
| |
| if (s != APR_SUCCESS) { |
| ctx->status = GEN_ERROR; |
| return s; |
| } |
| |
| if (saw_eos) { |
| ctx->status = GEN_COMPLETE; |
| return APR_SUCCESS; |
| } |
| else |
| return APR_INCOMPLETE; |
| } |
| |
| |
| struct xml_ctx { |
| apr_xml_doc *doc; |
| apr_xml_parser *xml_parser; |
| enum { |
| XML_INCOMPLETE, |
| XML_COMPLETE, |
| XML_ERROR |
| } status; |
| }; |
| |
| |
| APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser) |
| { |
| apr_pool_t *pool = hook->pool; |
| struct xml_ctx *ctx = hook->ctx; |
| apr_status_t s = APR_SUCCESS; |
| apr_bucket *e; |
| |
| if (ctx == NULL) { |
| hook->ctx = ctx = apr_palloc(pool, sizeof *ctx); |
| ctx->doc = NULL; |
| ctx->xml_parser = apr_xml_parser_create(pool); |
| ctx->status = XML_INCOMPLETE; |
| } |
| |
| PARSER_STATUS_CHECK(XML); |
| |
| for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); |
| e = APR_BUCKET_NEXT(e)) |
| { |
| const char *data; |
| apr_size_t dlen; |
| |
| if (APR_BUCKET_IS_EOS(e)) { |
| s = apr_xml_parser_done(ctx->xml_parser, &ctx->doc); |
| if (s == APR_SUCCESS) { |
| ctx->status = XML_COMPLETE; |
| if (hook->next) |
| s = apreq_hook_run(hook->next, param, bb); |
| } |
| else { |
| ctx->status = XML_ERROR; |
| } |
| return s; |
| } |
| else if (APR_BUCKET_IS_METADATA(e)) { |
| continue; |
| } |
| |
| s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); |
| |
| if (s != APR_SUCCESS) { |
| ctx->status = XML_ERROR; |
| return s; |
| } |
| |
| s = apr_xml_parser_feed(ctx->xml_parser, data, dlen); |
| |
| if (s != APR_SUCCESS) { |
| ctx->status = XML_ERROR; |
| return s; |
| } |
| } |
| |
| if (hook->next) |
| return apreq_hook_run(hook->next, param, bb); |
| |
| return APR_SUCCESS; |
| } |
| |
| |
| APREQ_DECLARE_HOOK(apreq_hook_find_param) |
| { |
| apreq_hook_find_param_ctx_t *ctx = hook->ctx; |
| int is_final = (bb == NULL) || APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb)); |
| apr_status_t s = (hook->next == NULL) |
| ? APR_SUCCESS : apreq_hook_run(hook->next, param, bb); |
| |
| if (is_final && s == APR_SUCCESS && ctx->param == NULL |
| && strcasecmp(ctx->name, param->v.name) == 0) { |
| ctx->param = param; |
| ctx->prev->next = hook->next; |
| } |
| return s; |
| } |