summaryrefslogtreecommitdiff
path: root/srclib/libapreq/library/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'srclib/libapreq/library/parser.c')
-rw-r--r--srclib/libapreq/library/parser.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/srclib/libapreq/library/parser.c b/srclib/libapreq/library/parser.c
new file mode 100644
index 0000000000..69c0c4f035
--- /dev/null
+++ b/srclib/libapreq/library/parser.c
@@ -0,0 +1,356 @@
+/*
+** 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_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);
+ 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
+ && strcasecmp(ctx->name, param->v.name) == 0) {
+ ctx->param = param;
+ ctx->prev->next = hook->next;
+ }
+ return s;
+}