diff options
38 files changed, 9839 insertions, 0 deletions
diff --git a/modules/apreq/Makefile.in b/modules/apreq/Makefile.in new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/apreq/Makefile.in diff --git a/modules/apreq/apreq_private_apache2.h b/modules/apreq/apreq_private_apache2.h new file mode 100644 index 0000000000..173060dc48 --- /dev/null +++ b/modules/apreq/apreq_private_apache2.h @@ -0,0 +1,56 @@ +extern module AP_MODULE_DECLARE_DATA apreq_module; + +struct dir_config { + const char *temp_dir; + apr_uint64_t read_limit; + apr_size_t brigade_limit; +}; + +/* The "warehouse", stored in r->request_config */ +struct apache2_handle { + apreq_handle_t handle; + request_rec *r; + apr_table_t *jar, *args; + apr_status_t jar_status, args_status; + ap_filter_t *f; +}; + +/* Tracks the apreq filter state */ +struct filter_ctx { + apr_bucket_brigade *bb; /* input brigade that's passed to the parser */ + apr_bucket_brigade *bbtmp; /* temporary copy of bb, destined for the spool */ + apr_bucket_brigade *spool; /* copied prefetch data for downstream filters */ + apreq_parser_t *parser; + apreq_hook_t *hook_queue; + apreq_hook_t *find_param; + apr_table_t *body; + apr_status_t body_status; + apr_status_t filter_error; + apr_uint64_t bytes_read; /* Total bytes read into this filter. */ + apr_uint64_t read_limit; /* Max bytes the filter may show to parser */ + apr_size_t brigade_limit; + const char *temp_dir; +}; + +apr_status_t apreq_filter_prefetch(ap_filter_t *f, apr_off_t readbytes); +apr_status_t apreq_filter(ap_filter_t *f, + apr_bucket_brigade *bb, + ap_input_mode_t mode, + apr_read_type_e block, + apr_off_t readbytes); + +void apreq_filter_make_context(ap_filter_t *f); +void apreq_filter_init_context(ap_filter_t *f); + +APR_INLINE +static void apreq_filter_relocate(ap_filter_t *f) +{ + request_rec *r = f->r; + + if (f != r->input_filters) { + ap_filter_t *top = r->input_filters; + ap_remove_input_filter(f); + r->input_filters = f; + f->next = top; + } +} diff --git a/modules/apreq/config.m4 b/modules/apreq/config.m4 new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/apreq/config.m4 diff --git a/modules/apreq/filter.c b/modules/apreq/filter.c new file mode 100644 index 0000000000..6c75a51b48 --- /dev/null +++ b/modules/apreq/filter.c @@ -0,0 +1,540 @@ +/* +** 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 "assert.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "util_filter.h" +#include "apr_tables.h" +#include "apr_buckets.h" +#include "http_request.h" +#include "apr_strings.h" + +#include "apreq_module_apache2.h" +#include "apreq_private_apache2.h" +#include "apreq_error.h" +#include "apreq_util.h" +#include "apreq_version.h" + +static void *apreq_create_dir_config(apr_pool_t *p, char *d) +{ + /* d == OR_ALL */ + struct dir_config *dc = apr_palloc(p, sizeof *dc); + dc->temp_dir = NULL; + dc->read_limit = APREQ_DEFAULT_READ_LIMIT; + dc->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT; + return dc; +} + +static void *apreq_merge_dir_config(apr_pool_t *p, void *a_, void *b_) +{ + struct dir_config *a = a_, *b = b_, *c = apr_palloc(p, sizeof *c); + + c->temp_dir = (b->temp_dir != NULL) /* overrides ok */ + ? b->temp_dir : a->temp_dir; + + c->brigade_limit = (b->brigade_limit == (apr_size_t)-1) /* overrides ok */ + ? a->brigade_limit : b->brigade_limit; + + c->read_limit = (b->read_limit < a->read_limit) /* why min? */ + ? b->read_limit : a->read_limit; + + return c; +} + +static const char *apreq_set_temp_dir(cmd_parms *cmd, void *data, + const char *arg) +{ + struct dir_config *conf = data; + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + + if (err != NULL) + return err; + + conf->temp_dir = arg; + return NULL; +} + +static const char *apreq_set_read_limit(cmd_parms *cmd, void *data, + const char *arg) +{ + struct dir_config *conf = data; + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + + if (err != NULL) + return err; + + conf->read_limit = apreq_atoi64f(arg); + return NULL; +} + +static const char *apreq_set_brigade_limit(cmd_parms *cmd, void *data, + const char *arg) +{ + struct dir_config *conf = data; + const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT); + + if (err != NULL) + return err; + + conf->brigade_limit = apreq_atoi64f(arg); + return NULL; +} + + +static const command_rec apreq_cmds[] = +{ + AP_INIT_TAKE1("APREQ2_TempDir", apreq_set_temp_dir, NULL, OR_ALL, + "Default location of temporary directory"), + AP_INIT_TAKE1("APREQ2_ReadLimit", apreq_set_read_limit, NULL, OR_ALL, + "Maximum amount of data that will be fed into a parser."), + AP_INIT_TAKE1("APREQ2_BrigadeLimit", apreq_set_brigade_limit, NULL, OR_ALL, + "Maximum in-memory bytes a brigade may use."), + { NULL } +}; + + +void apreq_filter_init_context(ap_filter_t *f) +{ + request_rec *r = f->r; + struct filter_ctx *ctx = f->ctx; + apr_bucket_alloc_t *ba = r->connection->bucket_alloc; + const char *cl_header; + + if (r->method_number == M_GET) { + /* Don't parse GET (this protects against subrequest body parsing). */ + ctx->body_status = APREQ_ERROR_NODATA; + return; + } + + cl_header = apr_table_get(r->headers_in, "Content-Length"); + + if (cl_header != NULL) { + char *dummy; + apr_uint64_t content_length = apr_strtoi64(cl_header,&dummy,0); + + if (dummy == NULL || *dummy != 0) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, + "Invalid Content-Length header (%s)", cl_header); + ctx->body_status = APREQ_ERROR_BADHEADER; + return; + } + else if (content_length > ctx->read_limit) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, + "Content-Length header (%s) exceeds configured " + "max_body limit (%" APR_UINT64_T_FMT ")", + cl_header, ctx->read_limit); + ctx->body_status = APREQ_ERROR_OVERLIMIT; + return; + } + } + + if (ctx->parser == NULL) { + const char *ct_header = apr_table_get(r->headers_in, "Content-Type"); + + if (ct_header != NULL) { + apreq_parser_function_t pf = apreq_parser(ct_header); + + if (pf != NULL) { + ctx->parser = apreq_parser_make(r->pool, ba, ct_header, pf, + ctx->brigade_limit, + ctx->temp_dir, + ctx->hook_queue, + NULL); + } + else { + ctx->body_status = APREQ_ERROR_NOPARSER; + return; + } + } + else { + ctx->body_status = APREQ_ERROR_NOHEADER; + return; + } + } + else { + if (ctx->parser->brigade_limit > ctx->brigade_limit) + ctx->parser->brigade_limit = ctx->brigade_limit; + if (ctx->temp_dir != NULL) + ctx->parser->temp_dir = ctx->temp_dir; + if (ctx->hook_queue != NULL) + apreq_parser_add_hook(ctx->parser, ctx->hook_queue); + } + + ctx->hook_queue = NULL; + ctx->bb = apr_brigade_create(r->pool, ba); + ctx->bbtmp = apr_brigade_create(r->pool, ba); + ctx->spool = apr_brigade_create(r->pool, ba); + ctx->body = apr_table_make(r->pool, APREQ_DEFAULT_NELTS); + ctx->body_status = APR_INCOMPLETE; +} + + +/* + * Situations to contend with: + * + * 1) Often the filter will be added by the content handler itself, + * so the apreq_filter_init hook will not be run. + * 2) If an auth handler uses apreq, the apreq_filter will ensure + * it's part of the protocol filters. apreq_filter_init does NOT need + * to notify the protocol filter that it must not continue parsing, + * the apreq filter can perform this check itself. apreq_filter_init + * just needs to ensure cfg->f does not point at it. + * 3) If req->proto_input_filters and req->input_filters are apreq + * filters, and req->input_filters->next == req->proto_input_filters, + * it is safe for apreq_filter to "steal" the proto filter's context + * and subsequently drop it from the chain. + */ + + +/* Examines the input_filter chain and moves the apreq filter(s) around + * before the filter chain is stacked by ap_get_brigade. + */ + + +static apr_status_t apreq_filter_init(ap_filter_t *f) +{ + request_rec *r = f->r; + struct filter_ctx *ctx = f->ctx; + struct apache2_handle *handle = + (struct apache2_handle *)apreq_handle_apache2(r); + + /* Don't parse GET (this protects against subrequest body parsing). */ + if (f->r->method_number == M_GET) + return APR_SUCCESS; + + if (ctx == NULL || ctx->body_status == APR_EINIT) { + if (f == r->input_filters) { + handle->f = f; + } + else if (r->input_filters->frec->filter_func.in_func == apreq_filter) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, + "removing intermediate apreq filter"); + if (handle->f == f) + handle->f = r->input_filters; + ap_remove_input_filter(f); + } + else { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, + "relocating intermediate apreq filter"); + apreq_filter_relocate(f); + handle->f = f; + } + return APR_SUCCESS; + } + + /* else this is a protocol filter which may still be active. + * if it is, we must deregister it now. + */ + if (handle->f == f) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, + "disabling stale protocol filter"); + if (ctx->body_status == APR_INCOMPLETE) + ctx->body_status = APREQ_ERROR_INTERRUPT; + handle->f = NULL; + } + return APR_SUCCESS; +} + + + +apr_status_t apreq_filter_prefetch(ap_filter_t *f, apr_off_t readbytes) +{ + struct filter_ctx *ctx = f->ctx; + request_rec *r = f->r; + apr_status_t rv; + apr_off_t len; + + if (ctx->body_status == APR_EINIT) + apreq_filter_init_context(f); + + if (ctx->body_status != APR_INCOMPLETE || readbytes == 0) + return ctx->body_status; + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, + "prefetching %" APR_OFF_T_FMT " bytes", readbytes); + + rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, + APR_BLOCK_READ, readbytes); + + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "ap_get_brigade failed during prefetch"); + ctx->filter_error = rv; + return ctx->body_status = APREQ_ERROR_GENERAL; + } + + apreq_brigade_setaside(ctx->bb, r->pool); + apreq_brigade_copy(ctx->bbtmp, ctx->bb); + + rv = apreq_brigade_concat(r->pool, ctx->temp_dir, ctx->brigade_limit, + ctx->spool, ctx->bbtmp); + if (rv != APR_SUCCESS && rv != APR_EOF) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, + "apreq_brigade_concat failed; TempDir problem?"); + ctx->filter_error = APR_EGENERAL; + return ctx->body_status = rv; + } + + /* Adding "f" to the protocol filter chain ensures the + * spooled data is preserved across internal redirects. + */ + + if (f != r->proto_input_filters) { + ap_filter_t *in; + for (in = r->input_filters; in != r->proto_input_filters; + in = in->next) + { + if (f == in) { + r->proto_input_filters = f; + break; + } + } + } + + apr_brigade_length(ctx->bb, 1, &len); + ctx->bytes_read += len; + + if (ctx->bytes_read > ctx->read_limit) { + ctx->body_status = APREQ_ERROR_OVERLIMIT; + ap_log_rerror(APLOG_MARK, APLOG_ERR, ctx->body_status, r, + "Bytes read (%" APR_UINT64_T_FMT + ") exceeds configured read limit (%" APR_UINT64_T_FMT ")", + ctx->bytes_read, ctx->read_limit); + return ctx->body_status; + } + + ctx->body_status = apreq_parser_run(ctx->parser, ctx->body, ctx->bb); + apr_brigade_cleanup(ctx->bb); + + return ctx->body_status; +} + + + +apr_status_t apreq_filter(ap_filter_t *f, + apr_bucket_brigade *bb, + ap_input_mode_t mode, + apr_read_type_e block, + apr_off_t readbytes) +{ + request_rec *r = f->r; + struct filter_ctx *ctx; + apr_status_t rv; + apr_off_t len; + + switch (mode) { + case AP_MODE_READBYTES: + /* only the modes above are supported */ + break; + + case AP_MODE_EXHAUSTIVE: /* not worth supporting at this level */ + case AP_MODE_GETLINE: /* chunked trailers are b0rked in ap_http_filter */ + return ap_get_brigade(f->next, bb, mode, block, readbytes); + + default: + return APR_ENOTIMPL; + } + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + if (ctx->body_status == APR_EINIT) + apreq_filter_init_context(f); + + if (ctx->spool && !APR_BRIGADE_EMPTY(ctx->spool)) { + apr_bucket *e; + rv = apr_brigade_partition(ctx->spool, readbytes, &e); + if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) + return rv; + + if (APR_BUCKET_IS_EOS(e)) + e = APR_BUCKET_NEXT(e); + + apreq_brigade_move(bb, ctx->spool, e); + return APR_SUCCESS; + } + else if (ctx->body_status != APR_INCOMPLETE) { + if (ctx->filter_error) + return ctx->filter_error; + + rv = ap_get_brigade(f->next, bb, mode, block, readbytes); + ap_remove_input_filter(f); + return rv; + } + + + rv = ap_get_brigade(f->next, bb, mode, block, readbytes); + if (rv != APR_SUCCESS) + return rv; + + apreq_brigade_copy(ctx->bb, bb); + apr_brigade_length(bb, 1, &len); + ctx->bytes_read += len; + + if (ctx->bytes_read > ctx->read_limit) { + ctx->body_status = APREQ_ERROR_OVERLIMIT; + ap_log_rerror(APLOG_MARK, APLOG_ERR, ctx->body_status, r, + "Bytes read (%" APR_UINT64_T_FMT + ") exceeds configured max_body limit (%" + APR_UINT64_T_FMT ")", + ctx->bytes_read, ctx->read_limit); + } + else { + ctx->body_status = apreq_parser_run(ctx->parser, ctx->body, ctx->bb); + apr_brigade_cleanup(ctx->bb); + } + return APR_SUCCESS; +} + + +static int apreq_pre_init(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *base_server) +{ + apr_status_t status; + + status = apreq_pre_initialize(p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, base_server, + "Failed to pre-initialize libapreq2"); + return HTTP_INTERNAL_SERVER_ERROR; + } + return OK; +} + +static int apreq_post_init(apr_pool_t *p, apr_pool_t *plog, + apr_pool_t *ptemp, server_rec *base_server) +{ + apr_status_t status; + + ap_add_version_component(p, apr_psprintf(p, + "mod_apreq2-%d/%s", + APREQ_APACHE2_MMN, + apreq_version_string())); + + status = apreq_post_initialize(p); + if (status != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_STARTUP|APLOG_ERR, status, base_server, + "Failed to post-initialize libapreq2"); + return HTTP_INTERNAL_SERVER_ERROR; + } + return OK; +} + +static void register_hooks (apr_pool_t *p) +{ + /* APR_HOOK_FIRST because we want other modules to be able to + * register parsers in their post_config hook via APR_HOOK_MIDDLE. + */ + ap_hook_post_config(apreq_pre_init, NULL, NULL, APR_HOOK_FIRST); + + /* APR_HOOK_LAST because we need to lock the default_parsers hash + * (to prevent further modifications) before the server forks. + */ + ap_hook_post_config(apreq_post_init, NULL, NULL, APR_HOOK_LAST); + + ap_register_input_filter(APREQ_FILTER_NAME, apreq_filter, apreq_filter_init, + AP_FTYPE_PROTOCOL-1); +} + + + +/** @} */ + + +module AP_MODULE_DECLARE_DATA apreq_module = { +#line __LINE__ "mod_apreq2.c" + STANDARD20_MODULE_STUFF, + apreq_create_dir_config, + apreq_merge_dir_config, + NULL, + NULL, + apreq_cmds, + register_hooks, +}; + + +void apreq_filter_make_context(ap_filter_t *f) +{ + request_rec *r; + struct filter_ctx *ctx; + struct dir_config *d; + + r = f->r; + d = ap_get_module_config(r->per_dir_config, &apreq_module); + + if (f == r->input_filters + && r->proto_input_filters == f->next + && f->next->frec->filter_func.in_func == apreq_filter + && f->r->method_number != M_GET) + { + + ctx = f->next->ctx; + + switch (ctx->body_status) { + + case APREQ_ERROR_INTERRUPT: + ctx->body_status = APR_INCOMPLETE; + /* fall thru */ + + case APR_SUCCESS: + + if (d != NULL) { + ctx->temp_dir = d->temp_dir; + ctx->read_limit = d->read_limit; + ctx->brigade_limit = d->brigade_limit; + + if (ctx->parser != NULL) { + ctx->parser->temp_dir = d->temp_dir; + ctx->parser->brigade_limit = d->brigade_limit; + } + + } + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, + "stealing filter context"); + f->ctx = ctx; + r->proto_input_filters = f; + ap_remove_input_filter(f->next); + + return; + + default: + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, ctx->body_status, r, + "cannot steal context: bad filter status"); + } + } + + ctx = apr_pcalloc(r->pool, sizeof *ctx); + ctx->body_status = APR_EINIT; + + if (d == NULL) { + ctx->read_limit = (apr_uint64_t)-1; + ctx->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT; + } else { + ctx->temp_dir = d->temp_dir; + ctx->read_limit = d->read_limit; + ctx->brigade_limit = d->brigade_limit; + } + + f->ctx = ctx; +} + diff --git a/modules/apreq/handle.c b/modules/apreq/handle.c new file mode 100644 index 0000000000..859710c9ea --- /dev/null +++ b/modules/apreq/handle.c @@ -0,0 +1,439 @@ +/* +** 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 "assert.h" + +#include "httpd.h" +#include "http_config.h" +#include "http_log.h" +#include "util_filter.h" +#include "apr_tables.h" +#include "apr_buckets.h" +#include "http_request.h" +#include "apr_strings.h" + +#include "apreq_module_apache2.h" +#include "apreq_private_apache2.h" +#include "apreq_error.h" + + +APR_INLINE +static ap_filter_t *get_apreq_filter(apreq_handle_t *handle) +{ + struct apache2_handle *req = (struct apache2_handle *)handle; + + if (req->f == NULL) { + req->f = ap_add_input_filter(APREQ_FILTER_NAME, NULL, + req->r, + req->r->connection); + /* ap_add_input_filter does not guarantee cfg->f == r->input_filters, + * so we reposition the new filter there as necessary. + */ + apreq_filter_relocate(req->f); + } + + return req->f; +} + + +static apr_status_t apache2_jar(apreq_handle_t *handle, const apr_table_t **t) +{ + struct apache2_handle *req = (struct apache2_handle*)handle; + request_rec *r = req->r; + + if (req->jar_status == APR_EINIT) { + const char *cookies = apr_table_get(r->headers_in, "Cookie"); + if (cookies != NULL) { + req->jar = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS); + req->jar_status = + apreq_parse_cookie_header(handle->pool, req->jar, cookies); + } + else + req->jar_status = APREQ_ERROR_NODATA; + } + + *t = req->jar; + return req->jar_status; +} + +static apr_status_t apache2_args(apreq_handle_t *handle, const apr_table_t **t) +{ + struct apache2_handle *req = (struct apache2_handle*)handle; + request_rec *r = req->r; + + if (req->args_status == APR_EINIT) { + if (r->args != NULL) { + req->args = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS); + req->args_status = + apreq_parse_query_string(handle->pool, req->args, r->args); + } + else + req->args_status = APREQ_ERROR_NODATA; + } + + *t = req->args; + return req->args_status; +} + + + + +static apreq_cookie_t *apache2_jar_get(apreq_handle_t *handle, const char *name) +{ + struct apache2_handle *req = (struct apache2_handle *)handle; + const apr_table_t *t; + const char *val; + + if (req->jar_status == APR_EINIT) + apache2_jar(handle, &t); + else + t = req->jar; + + if (t == NULL) + return NULL; + + val = apr_table_get(t, name); + if (val == NULL) + return NULL; + + return apreq_value_to_cookie(val); +} + +static apreq_param_t *apache2_args_get(apreq_handle_t *handle, const char *name) +{ + struct apache2_handle *req = (struct apache2_handle *)handle; + const apr_table_t *t; + const char *val; + + if (req->args_status == APR_EINIT) + apache2_args(handle, &t); + else + t = req->args; + + if (t == NULL) + return NULL; + + val = apr_table_get(t, name); + if (val == NULL) + return NULL; + + return apreq_value_to_param(val); +} + + +static apr_status_t apache2_body(apreq_handle_t *handle, const apr_table_t **t) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + switch (ctx->body_status) { + + case APR_EINIT: + apreq_filter_init_context(f); + if (ctx->body_status != APR_INCOMPLETE) + break; + + case APR_INCOMPLETE: + while (apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE) == APR_INCOMPLETE) + ; /*loop*/ + } + + *t = ctx->body; + return ctx->body_status; +} + +static apreq_param_t *apache2_body_get(apreq_handle_t *handle, const char *name) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + const char *val; + apreq_hook_t *h; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + switch (ctx->body_status) { + + case APR_SUCCESS: + + val = apr_table_get(ctx->body, name); + if (val != NULL) + return apreq_value_to_param(val); + return NULL; + + + case APR_EINIT: + + apreq_filter_init_context(f); + if (ctx->body_status != APR_INCOMPLETE) + return NULL; + apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE); + + + case APR_INCOMPLETE: + + val = apr_table_get(ctx->body, name); + if (val != NULL) + return apreq_value_to_param(val); + + /* Not seen yet, so we need to scan for + param while prefetching the body */ + + if (ctx->find_param == NULL) + ctx->find_param = apreq_hook_make(handle->pool, + apreq_hook_find_param, + NULL, NULL); + h = ctx->find_param; + h->next = ctx->parser->hook; + ctx->parser->hook = h; + h->ctx = (void *)name; + + do { + apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE); + if (h->ctx != name) { + ctx->parser->hook = h->next; + return h->ctx; + } + } while (ctx->body_status == APR_INCOMPLETE); + + ctx->parser->hook = h->next; + return NULL; + + + default: + + if (ctx->body == NULL) + return NULL; + + val = apr_table_get(ctx->body, name); + if (val != NULL) + return apreq_value_to_param(val); + return NULL; + + } + + /* not reached */ + return NULL; +} + +static +apr_status_t apache2_parser_get(apreq_handle_t *handle, + const apreq_parser_t **parser) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx = f->ctx; + + if (ctx == NULL) { + *parser = NULL; + return APR_EINIT; + } + *parser = ctx->parser; + return APR_SUCCESS; +} + +static +apr_status_t apache2_parser_set(apreq_handle_t *handle, + apreq_parser_t *parser) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + if (ctx->parser == NULL) { + ctx->parser = parser; + return APR_SUCCESS; + } + else + return APREQ_ERROR_NOTEMPTY; +} + + + +static +apr_status_t apache2_hook_add(apreq_handle_t *handle, + apreq_hook_t *hook) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + if (ctx->parser != NULL) { + return apreq_parser_add_hook(ctx->parser, hook); + } + else if (ctx->hook_queue != NULL) { + apreq_hook_t *h = ctx->hook_queue; + while (h->next != NULL) + h = h->next; + h->next = hook; + } + else { + ctx->hook_queue = hook; + } + return APR_SUCCESS; + +} + +static +apr_status_t apache2_brigade_limit_set(apreq_handle_t *handle, + apr_size_t bytes) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + if (ctx->body_status == APR_EINIT || ctx->brigade_limit > bytes) { + ctx->brigade_limit = bytes; + return APR_SUCCESS; + } + + return APREQ_ERROR_MISMATCH; +} + +static +apr_status_t apache2_brigade_limit_get(apreq_handle_t *handle, + apr_size_t *bytes) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + *bytes = ctx->brigade_limit; + return APR_SUCCESS; +} + +static +apr_status_t apache2_read_limit_set(apreq_handle_t *handle, + apr_uint64_t bytes) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + + if (ctx->read_limit > bytes && ctx->bytes_read < bytes) { + ctx->read_limit = bytes; + return APR_SUCCESS; + } + + return APREQ_ERROR_MISMATCH; +} + +static +apr_status_t apache2_read_limit_get(apreq_handle_t *handle, + apr_uint64_t *bytes) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + *bytes = ctx->read_limit; + return APR_SUCCESS; +} + +static +apr_status_t apache2_temp_dir_set(apreq_handle_t *handle, + const char *path) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + // init vs incomplete state? + if (ctx->temp_dir == NULL && ctx->bytes_read == 0) { + if (path != NULL) + ctx->temp_dir = apr_pstrdup(handle->pool, path); + return APR_SUCCESS; + } + + return APREQ_ERROR_NOTEMPTY; +} + +static +apr_status_t apache2_temp_dir_get(apreq_handle_t *handle, + const char **path) +{ + ap_filter_t *f = get_apreq_filter(handle); + struct filter_ctx *ctx; + + if (f->ctx == NULL) + apreq_filter_make_context(f); + + ctx = f->ctx; + *path = ctx->parser ? ctx->parser->temp_dir : ctx->temp_dir; + return APR_SUCCESS; +} + +static APREQ_MODULE(apache2, APREQ_APACHE2_MMN); + +APREQ_DECLARE(apreq_handle_t *) apreq_handle_apache2(request_rec *r) +{ + struct apache2_handle *req = + ap_get_module_config(r->request_config, &apreq_module); + + if (req != NULL) { + get_apreq_filter(&req->handle); + return &req->handle; + } + + req = apr_palloc(r->pool, sizeof *req); + ap_set_module_config(r->request_config, &apreq_module, req); + + req->handle.module = &apache2_module; + req->handle.pool = r->pool; + req->handle.bucket_alloc = r->connection->bucket_alloc; + req->r = r; + + req->args_status = req->jar_status = APR_EINIT; + req->args = req->jar = NULL; + + req->f = NULL; + + get_apreq_filter(&req->handle); + return &req->handle; + +} diff --git a/modules/apreq/mod_apreq2.h b/modules/apreq/mod_apreq2.h new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/modules/apreq/mod_apreq2.h diff --git a/srclib/libapreq/Makefile.in b/srclib/libapreq/Makefile.in new file mode 100644 index 0000000000..b86e9f3680 --- /dev/null +++ b/srclib/libapreq/Makefile.in @@ -0,0 +1,94 @@ +# +# Top-level Makefile for APREQ +# +CPP = @CPP@ + +# gets substituted into some targets +APREQ_MAJOR_VERSION=@APREQ_MAJOR_VERSION@ +APREQ_MINOR_VERSION=@APREQ_MINOR_VERSION@ +APREQ_PATCH_VERSION=@APREQ_PATCH_VERSION@ +APREQ_DOTTED_VERSION=@APREQ_DOTTED_VERSION@ +APREQ_LIBTOOL_VERSION=@APREQ_LIBTOOL_VERSION@ + +srcdir = @srcdir@ +VPATH = @srcdir@ + +INCLUDES = @APREQ_INCLUDES@ @APREQ_PRIV_INCLUDES@ @APR_INCLUDES@ +APREQ_LDFLAGS = @APREQ_LDFLAGS@ +APREQ_LIBS = @APREQ_LIBS@ + +TARGET_LIB = lib@APREQ_LIBNAME@.la +INSTALL_SUBDIRS = +EXTRA_SOURCE_DIRS = +APRUTIL_PCFILE = apr-util-$(APRUTIL_MAJOR_VERSION).pc +APREQ_CONFIG = apreq2-config +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ + +TARGETS = $(TARGET_LIB) apreq.exp + +# bring in rules.mk for standard functionality +@INCLUDE_RULES@ +@INCLUDE_OUTPUTS@ + +LINK = $(LIBTOOL) $(LTFLAGS) --mode=link $(LT_LDFLAGS) $(COMPILE) -version-info $(APREQ_LIBTOOL_VERSION) $(ALL_LDFLAGS) -o $@ + +CLEAN_SUBDIRS = test + +CLEAN_TARGETS = exports.c export_vars.c apreq.exp .make.dirs apreq2-config.out +DISTCLEAN_TARGETS = config.cache config.log config.status libtool \ + export_vars.sh $(APREQ_CONFIG) build/rules.mk \ + apr-util.pc build/pkg/pkginfo +EXTRACLEAN_TARGETS = configure aclocal.m4 \ + exports.c build-outputs.mk \ + build/apr_common.m4 build/find_apr.m4 build/install.sh \ + build/config.guess build/config.sub + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +includedir=@includedir@ +top_srcdir=@abs_srcdir@ +top_blddir=@abs_builddir@ + +# Create apu-config script suitable for the install tree +apreq2-config.out: $(APREQ_CONFIG) + sed 's,^\(location=\).*$$,\1installed,' < $(APREQ_CONFIG) > $@ + +install: $(TARGET_LIB) apreq2-config.out + $(APR_MKDIR) $(DESTDIR)$(includedir) $(DESTDIR)$(libdir)/pkgconfig \ + $(DESTDIR)$(libdir) $(DESTDIR)$(bindir) + for f in $(top_srcdir)/include/*.h $(top_blddir)/include/*.h; do \ + $(INSTALL_DATA) $${f} $(DESTDIR)$(includedir); \ + done + echo $(INSTALL_DATA) apr-util.pc $(DESTDIR)$(libdir)/pkgconfig/$(APRUTIL_PCFILE) + list='$(INSTALL_SUBDIRS)'; for i in $$list; do \ + ( cd $$i ; $(MAKE) DESTDIR=$(DESTDIR) install ); \ + done + $(LIBTOOL) --mode=install $(INSTALL) -m 755 $(TARGET_LIB) $(DESTDIR)$(libdir) + $(INSTALL_DATA) apreq.exp $(DESTDIR)$(libdir) + $(INSTALL) -m 755 apreq2-config.out $(DESTDIR)$(bindir)/$(APREQ_CONFIG) + +$(TARGET_LIB): $(OBJECTS) + $(LINK) @lib_target@ $(ALL_LIBS) $(APREQ_LDFLAGS) $(APREQ_LIBS) + +exports.c: $(HEADERS) + $(APR_MKEXPORT) $(HEADERS) > $@ + +export_vars.c: $(HEADERS) + $(APR_MKVAREXPORT) $(HEADERS) > $@ + +apreq.exp: exports.c export_vars.c + @echo "#! lib@APREQ_LIBNAME@.so" > $@ + @echo "* This file was AUTOGENERATED at build time." >> $@ + @echo "* Please do not edit by hand." >> $@ + $(CPP) $(ALL_CPPFLAGS) $(ALL_INCLUDES) exports.c | grep "ap_hack_" | sed -e 's/^.*[)]\(.*\);$$/\1/' >> $@ + $(CPP) $(ALL_CPPFLAGS) $(ALL_INCLUDES) export_vars.c | sed -e 's/^\#[^!]*//' | sed -e '/^$$/d' >> $@ + +dox: + doxygen $(top_srcdir)/docs/doxygen.conf + +test: check +check: $(TARGET_LIB) + cd test && $(MAKE) check diff --git a/srclib/libapreq/STATUS b/srclib/libapreq/STATUS new file mode 100644 index 0000000000..5f934989ee --- /dev/null +++ b/srclib/libapreq/STATUS @@ -0,0 +1,35 @@ +/** @page apreq_status STATUS + + 2.08 under development. + +Contributors looking for a mission: + + - just do an egrep on "TODO" or "XXX" and see what's there + + +CURRENT RELEASE NOTES: + + +RELEASE SHOWSTOPPERS: + + + +CURRENT VOTES: + + +TODO: + + apreq2-config doesn't install correctly. + mod_apreq2 doesn't even build. + + +OPEN ISSUES: + + +BUGS: + + +WISH LIST: + + +*/ diff --git a/srclib/libapreq/apreq2-config.in b/srclib/libapreq/apreq2-config.in new file mode 100644 index 0000000000..bfc8e7d22c --- /dev/null +++ b/srclib/libapreq/apreq2-config.in @@ -0,0 +1,218 @@ +#!/bin/sh + +## +## Copyright 2003-2006 The Apache Software Foundation +## +## Licensed 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. +## + +# apreq2-config- based on apu-config: +# APR-util script designed to allow easy command line access to APR-util +# configuration parameters. + +prefix="@prefix@" +exec_prefix="@exec_prefix@" +bindir="@bindir@" +libdir="@libdir@" +includedir="@includedir@" + +LIBS="@APREQ_LIBS@" +LDFLAGS="@APREQ_LDFLAGS@" +INCLUDES="@APREQ_INCLUDES@" + +APREQ_LIBNAME="@APREQ_LIBNAME@" + +APREQ_SOURCE_DIR="@abs_srcdir@" +APREQ_BUILD_DIR="@abs_builddir@" + +show_usage() +{ + cat << EOF +Usage: apreq2-config [OPTION] + +Known values for OPTION are: + --prefix[=DIR] change prefix to DIR + --bindir print location where binaries are installed + --includes print include information + --includedir print location where headers are installed + --ldflags print linker flags + --libs print library information + --srcdir print apreq2 source directory + --link-ld print link switch(es) for linking to libapreq2 + --link-libtool print the libtool inputs for linking to libapreq2 + --la-file print the path to the library's .la file, if available + --library-version print the API version as a dotted triple + --libtool-version print the ABI version in a libtool-compatible format + --package-version print the version number of the distribution + --help print this help + +When linking with libtool, an application should do something like: + APREQ_LIBS="\`apreq2-config --link-libtool --libs\`" +or when linking directly: + APREQ_LIBS="\`apreq2-config --link-ld --ldflags --libs\`" + +An application should use the results of --includes, and --ldflags in +their build process. +EOF +} + +if test $# -eq 0; then + show_usage + exit 1 +fi + +thisdir="`dirname $0`" +thisdir="`cd $thisdir && pwd`" +if test -d $bindir; then + tmpbindir="`cd $bindir && pwd`" +else + tmpbindir="" +fi +# If we have the realpath program, use it to resolve symlinks. +# Otherwise, being in a symlinked dir may result in incorrect output. +if test -x "`which realpath 2>/dev/null`"; then + thisdir="`realpath $thisdir`" + if test -d "$APREQ_SOURCE_DIR"; then + APREQ_SOURCE_DIR="`realpath $APREQ_SOURCE_DIR`" + fi + if test -n "$tmpbindir"; then + tmpbindir="`realpath $tmpbindir`" + fi +fi +if test "$tmpbindir" = "$thisdir"; then + location=installed +elif test "$APREQ_SOURCE_DIR" = "$thisdir"; then + location=source +else + location=build +fi + + +if test "$location" = "installed"; then + LA_FILE="$libdir/lib${APREQ_LIBNAME}.la" +else + LA_FILE="$thisdir/lib${APREQ_LIBNAME}.la" +fi + +flags="" + +while test $# -gt 0; do + # Normalize the prefix. + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case "$1" in + # It is possible for the user to override our prefix. + --prefix=*) + prefix=$optarg + ;; + --prefix) + echo $prefix + exit 0 + ;; + --bindir) + echo $bindir + exit 0 + ;; + --libs) + flags="$flags $LIBS" + ;; + --includedir) + if test "$location" = "installed"; then + flags="$includedir" + elif test "$location" = "source"; then + flags="$APREQ_SOURCE_DIR/include" + else + # this is for VPATH builds + flags="$thisdir/include $APREQ_SOURCE_DIR/include" + fi + echo $flags + exit 0 + ;; + --includes) + if test "$location" = "installed"; then + flags="$flags -I$includedir $INCLUDES" + elif test "$location" = "source"; then + flags="$flags -I$APREQ_SOURCE_DIR/include $INCLUDES" + else + # this is for VPATH builds + flags="$flags -I$thisdir/include -I$APREQ_SOURCE_DIR/include $INCLUDES" + fi + ;; + --ldflags) + flags="$flags $LDFLAGS" + ;; + --srcdir) + echo $APREQ_SOURCE_DIR + exit 0 + ;; + --library-version) + echo @APREQ_DOTTED_VERSION@ + exit 0 + ;; + --libtool-version) + echo @APREQ_LIBTOOL_VERSION@ + exit 0 + ;; + --package-version) + echo "@VERSION@" + exit 0 + ;; + --link-ld) + if test "$location" = "installed"; then + ### avoid using -L if libdir is a "standard" location like /usr/lib + flags="$flags -L$libdir -l$APREQ_LIBNAME" + else + flags="$flags -L$thisdir/library/.libs -l$APREQ_LIBNAME" + fi + ;; + --link-libtool) + # If the LA_FILE exists where we think it should be, use it. If we're + # installed and the LA_FILE does not exist, assume to use -L/-l + # (the LA_FILE may not have been installed). If we're building ourselves, + # we'll assume that at some point the .la file be created. + if test -f "$LA_FILE"; then + flags="$flags $LA_FILE" + elif test "$location" = "installed"; then + ### avoid using -L if libdir is a "standard" location like /usr/lib + flags="$flags -L$libdir -l$APREQ_LIBNAME $apreq_libs" + else + flags="$flags $LA_FILE" + fi + ;; + --la-file) + if test -f "$LA_FILE"; then + flags="$flags $LA_FILE" + fi + ;; + --help) + show_usage + exit 0 + ;; + *) + show_usage + exit 1 + ;; + esac + + # Next please. + shift +done + +if test -n "$flags"; then + echo "$flags" +fi + +exit 0 diff --git a/srclib/libapreq/build.conf b/srclib/libapreq/build.conf new file mode 100644 index 0000000000..5f0603cfe8 --- /dev/null +++ b/srclib/libapreq/build.conf @@ -0,0 +1,19 @@ +# +# Configuration file for APREQ. Used by APR/build/gen-build.py +# + +[options] + +# the platform-independent .c files +paths = + library/*.c +# we have no platform-specific subdirs +platform_dirs = + +# the public headers +headers = include/*.h + +# gen_uri_delim.c + +# we have a recursive makefile for the test files (for now) +# test/*.c diff --git a/srclib/libapreq/build/apreq-conf.m4 b/srclib/libapreq/build/apreq-conf.m4 new file mode 100644 index 0000000000..a2ec6758a2 --- /dev/null +++ b/srclib/libapreq/build/apreq-conf.m4 @@ -0,0 +1,72 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Copyright 2000-2005 The Apache Software Foundation or its licensors, as +dnl applicable. +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + + +dnl +dnl custom autoconf rules for APRUTIL +dnl + +dnl +dnl APU_FIND_APR: figure out where APR is located +dnl +AC_DEFUN([APREQ_FIND_APR], [ + + dnl use the find_apr.m4 script to locate APR. sets apr_found and apr_config + APR_FIND_APR(,,,[1]) + if test "$apr_found" = "no"; then + AC_MSG_ERROR(APR could not be located. Please use the --with-apr option.) + fi + + APR_BUILD_DIR="`$apr_config --installbuilddir`" + + dnl make APR_BUILD_DIR an absolute directory (we'll need it in the + dnl sub-projects in some cases) + APR_BUILD_DIR="`cd $APR_BUILD_DIR && pwd`" + + APR_INCLUDES="`$apr_config --includes`" + APR_LIBS="`$apr_config --link-libtool --libs`" + APR_SO_EXT="`$apr_config --apr-so-ext`" + APR_LIB_TARGET="`$apr_config --apr-lib-target`" + + AC_SUBST(APR_INCLUDES) + AC_SUBST(APR_LIBS) + AC_SUBST(APR_BUILD_DIR) +]) + + +AC_DEFUN([APREQ_FIND_APU], [ + + dnl use the find_apr.m4 script to locate APR. sets apr_found and apu_config + APR_FIND_APU(,,,[1]) + if test "$apu_found" = "no"; then + AC_MSG_ERROR(APR could not be located. Please use the --with-apr-util option.) + fi + + APU_BUILD_DIR="`$apu_config --installbuilddir`" + + dnl make APR_BUILD_DIR an absolute directory (we'll need it in the + dnl sub-projects in some cases) + APU_BUILD_DIR="`cd $APR_BUILD_DIR && pwd`" + + APU_INCLUDES="`$apu_config --includes`" + APU_LIBS="`$apu_config --link-libtool --libs`" + + AC_SUBST(APU_INCLUDES) + AC_SUBST(APU_LIBS) + AC_SUBST(APU_BUILD_DIR) +]) + + diff --git a/srclib/libapreq/build/apreq-hints.m4 b/srclib/libapreq/build/apreq-hints.m4 new file mode 100644 index 0000000000..33a5d22d3b --- /dev/null +++ b/srclib/libapreq/build/apreq-hints.m4 @@ -0,0 +1,61 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Copyright 2003-2005 The Apache Software Foundation or its licensors, as +dnl applicable. +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + +dnl ----------------------------------------------------------------- +dnl apu-hints.m4: apr-util's autoconf macros for platform-specific hints +dnl +dnl We preload various configure settings depending +dnl on previously obtained platform knowledge. +dnl We allow all settings to be overridden from +dnl the command-line. + +dnl +dnl APU_PRELOAD +dnl +dnl Preload various build parameters based on outside knowledge. +dnl +AC_DEFUN([APREQ_PRELOAD], [ +if test "x$apreq_preload_done" != "xyes" ; then + apreq_preload_done="yes" + + echo "Applying apr-util hints file rules for $host" + + case "$host" in + *-dec-osf*) + APR_SETIFNULL(apreq_crypt_threadsafe, [1]) + ;; + *-hp-hpux11.*) + APR_SETIFNULL(apreq_crypt_threadsafe, [1]) + ;; + *-ibm-aix4*|*-ibm-aix5.1*) + APR_SETIFNULL(apreq_iconv_inbuf_const, [1]) + ;; + *-ibm-os390) + APR_SETIFNULL(apreq_crypt_threadsafe, [1]) + ;; + *-solaris2*) + APR_SETIFNULL(apreq_iconv_inbuf_const, [1]) + APR_SETIFNULL(apreq_crypt_threadsafe, [1]) + ;; + *-sco3.2v5*) + APR_SETIFNULL(apreq_db_xtra_libs, [-lsocket]) + ;; + esac + +fi +]) + + diff --git a/srclib/libapreq/build/find_apreq.m4 b/srclib/libapreq/build/find_apreq.m4 new file mode 100644 index 0000000000..ed69397ece --- /dev/null +++ b/srclib/libapreq/build/find_apreq.m4 @@ -0,0 +1,176 @@ +dnl -------------------------------------------------------- -*- autoconf -*- +dnl Copyright 2002-2006 The Apache Software Foundation or its licensors, as +dnl applicable. +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. + +dnl +dnl find_apu.m4 : locate the APR-util (APU) include files and libraries +dnl +dnl This macro file can be used by applications to find and use the APU +dnl library. It provides a standardized mechanism for using APU. It supports +dnl embedding APU into the application source, or locating an installed +dnl copy of APU. +dnl +dnl APR_FIND_APU(srcdir, builddir, implicit-install-check, acceptable-majors) +dnl +dnl where srcdir is the location of the bundled APU source directory, or +dnl empty if source is not bundled. +dnl +dnl where builddir is the location where the bundled APU will be built, +dnl or empty if the build will occur in the srcdir. +dnl +dnl where implicit-install-check set to 1 indicates if there is no +dnl --with-apr-util option specified, we will look for installed copies. +dnl +dnl where acceptable-majors is a space separated list of acceptable major +dnl version numbers. Often only a single major version will be acceptable. +dnl If multiple versions are specified, and --with-apr-util=PREFIX or the +dnl implicit installed search are used, then the first (leftmost) version +dnl in the list that is found will be used. Currently defaults to [0 1]. +dnl +dnl Sets the following variables on exit: +dnl +dnl apu_found : "yes", "no", "reconfig" +dnl +dnl apu_config : If the apu-config tool exists, this refers to it. If +dnl apu_found is "reconfig", then the bundled directory +dnl should be reconfigured *before* using apu_config. +dnl +dnl Note: this macro file assumes that apr-config has been installed; it +dnl is normally considered a required part of an APR installation. +dnl +dnl Note: At this time, we cannot find *both* a source dir and a build dir. +dnl If both are available, the build directory should be passed to +dnl the --with-apr-util switch. +dnl +dnl Note: the installation layout is presumed to follow the standard +dnl PREFIX/lib and PREFIX/include pattern. If the APU config file +dnl is available (and can be found), then non-standard layouts are +dnl possible, since it will be described in the config file. +dnl +dnl If a bundled source directory is available and needs to be (re)configured, +dnl then apu_found is set to "reconfig". The caller should reconfigure the +dnl (passed-in) source directory, placing the result in the build directory, +dnl as appropriate. +dnl +dnl If apu_found is "yes" or "reconfig", then the caller should use the +dnl value of apu_config to fetch any necessary build/link information. +dnl + +AC_DEFUN([APR_FIND_APREQ], [ + apreq_found="no" + + if test "$target_os" = "os2-emx"; then + # Scripts don't pass test -x on OS/2 + TEST_X="test -f" + else + TEST_X="test -x" + fi + + ifelse([$4], [], + [ + ifdef(AC_WARNING,([$0: missing argument 4 (acceptable-majors): Defaulting to APREQ 0.x then APREQ 1.x])) + acceptable_majors="0 1" + ], [acceptable_majors="$4"]) + + apreq_temp_acceptable_apreq_config="" + for apreq_temp_major in $acceptable_majors + do + case $apreq_temp_major in + 0) + apreq_temp_acceptable_apreq_config="$apreq_temp_acceptable_apreq_config apreq-config" + ;; + *) + apreq_temp_acceptable_apreq_config="$apreq_temp_acceptable_apreq_config apreq$apreq_temp_major-config" + ;; + esac + done + + AC_MSG_CHECKING(for APREQ) + AC_ARG_WITH(apreq, + [ --with-apreq=PATH prefix for installed APREQ, path to APREQ build tree, + or the full path to apreq-config], + [ + if test "$withval" = "no" || test "$withval" = "yes"; then + AC_MSG_ERROR([--with-apreq requires a directory or file to be provided]) + fi + + for apreq_temp_apreq_config_file in $apreq_temp_acceptable_apreq_config + do + for lookdir in "$withval/bin" "$withval" + do + if $TEST_X "$lookdir/$apreq_temp_apreq_config_file"; then + apreq_found="yes" + apreq_config="$lookdir/$apreq_temp_apreq_config_file" + break 2 + fi + done + done + + if test "$apreq_found" != "yes" && $TEST_X "$withval" && $withval --help > /dev/null 2>&1 ; then + apreq_found="yes" + apreq_config="$withval" + fi + + dnl if --with-apreq is used, it is a fatal error for its argument + dnl to be invalid + if test "$apreq_found" != "yes"; then + AC_MSG_ERROR([the --with-apreq parameter is incorrect. It must specify an install prefix, a build directory, or an apreq-config file.]) + fi + ],[ + if test -n "$3" && test "$3" = "1"; then + for apreq_temp_apreq_config_file in $apreq_temp_acceptable_apreq_config + do + if $apreq_temp_apreq_config_file --help > /dev/null 2>&1 ; then + apreq_found="yes" + apreq_config="$apreq_temp_apreq_config_file" + break + else + dnl look in some standard places (apparently not in builtin/default) + for lookdir in /usr /usr/local /usr/local/apr /opt/apr /usr/local/apache2 ; do + if $TEST_X "$lookdir/bin/$apreq_temp_apreq_config_file"; then + apreq_found="yes" + apreq_config="$lookdir/bin/$apreq_temp_apreq_config_file" + break 2 + fi + done + fi + done + fi + dnl if we have not found anything yet and have bundled source, use that + if test "$apreq_found" = "no" && test -d "$1"; then + apreq_temp_abs_srcdir="`cd $1 && pwd`" + apreq_found="reconfig" + apreq_bundled_major="`sed -n '/#define.*APREQ_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p' \"$1/include/apreq_version.h\"`" + case $apreq_bundled_major in + "") + AC_MSG_ERROR([failed to find major version of bundled APREQ]) + ;; + 0) + apreq_temp_apreq_config_file="apreq-config" + ;; + *) + apreq_temp_apreq_config_file="apreq$apreq_bundled_major-config" + ;; + esac + if test -n "$2"; then + apreq_config="$2/$apreq_temp_apreq_config_file" + else + apreq_config="$1/$apreq_temp_apreq_config_file" + fi + fi + ]) + + AC_MSG_RESULT($apreq_found) +]) diff --git a/srclib/libapreq/build/get-version.sh b/srclib/libapreq/build/get-version.sh new file mode 100755 index 0000000000..d0d9d5bff8 --- /dev/null +++ b/srclib/libapreq/build/get-version.sh @@ -0,0 +1,39 @@ +#!/bin/sh +# +# extract version numbers from a header file +# +# USAGE: get-version.sh CMD VERSION_HEADER PREFIX +# where CMD is one of: all, major, libtool +# where PREFIX is the prefix to {MAJOR|MINOR|PATCH}_VERSION defines +# +# get-version.sh all returns a dotted version number +# get-version.sh major returns just the major version number +# get-version.sh minor returns just the minor version number +# get-version.sh patch returns just the match version number +# + +if test $# != 3; then + echo "USAGE: $0 CMD INCLUDEDIR PREFIX" + echo " where CMD is one of: all, major, minor, patch" + exit 1 +fi + +major_sed="/#define.*$3_MAJOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +minor_sed="/#define.*$3_MINOR_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +patch_sed="/#define.*$3_PATCH_VERSION/s/^[^0-9]*\([0-9]*\).*$/\1/p" +major="`sed -n $major_sed $2`" +minor="`sed -n $minor_sed $2`" +patch="`sed -n $patch_sed $2`" + +if test "$1" = "all"; then + echo ${major}.${minor}.${patch} +elif test "$1" = "major"; then + echo ${major} +elif test "$1" = "minor"; then + echo ${minor} +elif test "$1" = "patch"; then + echo ${patch} +else + echo "ERROR: unknown version CMD ($1)" + exit 1 +fi diff --git a/srclib/libapreq/buildconf b/srclib/libapreq/buildconf new file mode 100755 index 0000000000..0c2f5df1ad --- /dev/null +++ b/srclib/libapreq/buildconf @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright 1999-2005 The Apache Software Foundation or its licensors, as +# applicable. +# +# Licensed 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. +# +# + +# Default place to look for apr source. Can be overridden with +# --with-apr=[directory] +apr_src_dir=../apr +apu_src_dir=../apr-util + +while test $# -gt 0 +do + # Normalize + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case "$1" in + --with-apr=*) + apr_src_dir=$optarg + ;; + --with-apr-util=*) + apu_src_dir=$optarg + ;; + esac + + shift +done + +if [ -f "$apr_src_dir/build/apr_common.m4" ]; then + echo "" + echo "Looking for apr source in $apr_src_dir" +else + echo "" + echo "Problem finding apr source in $apr_src_dir." + echo "Use:" + echo " --with-apr=[directory]" + exit 1 +fi + +if [ -f "$apu_src_dir/build/find_apu.m4" ]; then + echo "" + echo "Looking for apr-util source in $apu_src_dir" +else + echo "" + echo "Problem finding apr-util source in $apu_src_dir." + echo "Use:" + echo " --with-apr-util=[directory]" + exit 1 +fi + +set -e + +# Remove some files, then copy them from apr source tree +rm -f build/apr_common.m4 build/find_apr.m4 build/install.sh \ + build/config.guess build/config.sub +cp $apr_src_dir/build/apr_common.m4 $apr_src_dir/build/find_apr.m4 \ + $apr_src_dir/build/install.sh $apr_src_dir/build/config.guess \ + $apr_src_dir/build/config.sub build + +rm -f build/find_apu.m4 +cp $apu_src_dir/build/find_apu.m4 build + + +# Remove aclocal.m4 as it'll break some builds... +rm -rf aclocal.m4 autom4te*.cache + +# +# Generate the autoconf header (include/apreq_config.h) and ./configure +# +echo "Creating include/apreq_config.h ..." +${AUTOHEADER:-autoheader} + +echo "Creating configure ..." +### do some work to toss config.cache? +if ${AUTOCONF:-autoconf}; then + : +else + echo "autoconf failed" + exit 1 +fi + +# +# Generate build-outputs.mk for the build systme +# +echo "Generating 'make' outputs ..." +$apr_src_dir/build/gen-build.py make + +# Remove autoconf cache again +rm -rf autom4te*.cache + diff --git a/srclib/libapreq/config.layout b/srclib/libapreq/config.layout new file mode 100644 index 0000000000..80a4c8b5f3 --- /dev/null +++ b/srclib/libapreq/config.layout @@ -0,0 +1,232 @@ +## +## config.layout -- Pre-defined Installation Path Layouts +## +## Hints: +## - layouts can be loaded with configure's --enable-layout=ID option +## - when no --enable-layout option is given, the default layout is `apr' +## - a trailing plus character (`+') on paths is replaced with a +## `/<target>' suffix where <target> is currently hardcoded to 'apr'. +## (This may become a configurable parameter at some point.) +## + +# Classical APREQ path layout designed for parallel installs. +<Layout apreq> + prefix: /usr/local/apr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/bin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/modules + mandir: ${prefix}/man + sysconfdir: ${prefix}/conf + datadir: ${prefix} + installbuilddir: ${datadir}/build + includedir: ${prefix}/include/apreq2 + localstatedir: ${prefix} + libsuffix: -${APREQ_MAJOR_VERSION} +</Layout> + +# Classical single-installation APR path layout. +<Layout classic> + prefix: /usr/local/apr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/bin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/modules + mandir: ${prefix}/man + sysconfdir: ${prefix}/conf + datadir: ${prefix} + installbuilddir: ${datadir}/build + includedir: ${prefix}/include + localstatedir: ${prefix} +</Layout> + +# GNU standards conforming path layout. +# See FSF's GNU project `make-stds' document for details. +<Layout GNU> + prefix: /usr/local + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec + mandir: ${prefix}/man + sysconfdir: ${prefix}/etc+ + datadir: ${prefix}/share+ + installbuilddir: ${datadir}/build + includedir: ${prefix}/include+ + localstatedir: ${prefix}/var+ + runtimedir: ${localstatedir}/run +</Layout> + +# Mac OS X Server (Rhapsody) +<Layout Mac OS X Server> + prefix: /Local/Library/WebServer + exec_prefix: /usr + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: /System/Library/apr/Modules + mandir: ${exec_prefix}/share/man + sysconfdir: ${prefix}/Configuration + datadir: ${prefix} + installbuilddir: /System/Library/apr/Build + includedir: /System/Library/Frameworks/apr.framework/Versions/2.0/Headers + localstatedir: /var + runtimedir: ${prefix}/Logs +</Layout> + +# Darwin/Mac OS Layout +<Layout Darwin> + prefix: /usr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec+ + mandir: ${prefix}/share/man + datadir: /Library/WebServer + sysconfdir: /etc+ + installbuilddir: ${prefix}/share/httpd/build + includedir: ${prefix}/include+ + localstatedir: /var + runtimedir: ${localstatedir}/run +</Layout> + +# Red Hat Linux 7.x layout +<Layout RedHat> + prefix: /usr + exec_prefix: ${prefix} + bindir: ${prefix}/bin + sbindir: ${prefix}/sbin + libdir: ${prefix}/lib + libexecdir: ${prefix}/lib/apr + mandir: ${prefix}/man + sysconfdir: /etc/httpd/conf + datadir: /var/www + installbuilddir: ${datadir}/build + includedir: ${prefix}/include/apr + localstatedir: /var + runtimedir: ${localstatedir}/run +</Layout> + +# According to the /opt filesystem conventions +<Layout opt> + prefix: /opt/apr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec + mandir: ${prefix}/man + sysconfdir: /etc${prefix} + datadir: ${prefix}/share + installbuilddir: ${datadir}/build + includedir: ${prefix}/include + localstatedir: /var${prefix} + runtimedir: ${localstatedir}/run +</Layout> + +# BeOS layout... +<Layout beos> + prefix: /boot/home/apr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/bin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec + mandir: ${prefix}/man + sysconfdir: ${prefix}/conf + datadir: ${prefix} + installbuilddir: ${datadir}/build + includedir: ${prefix}/include + localstatedir: ${prefix} + runtimedir: ${localstatedir}/logs +</Layout> + +# SuSE 6.x layout +<Layout SuSE> + prefix: /usr + exec_prefix: ${prefix} + bindir: ${prefix}/bin + sbindir: ${prefix}/sbin + libdir: ${prefix}/lib + libexecdir: ${prefix}/lib/apr + mandir: ${prefix}/share/man + sysconfdir: /etc/httpd + datadir: /usr/local/httpd + installbuilddir: ${datadir}/build + includedir: ${prefix}/include/apr + localstatedir: /var/lib/httpd + runtimedir: /var/run +</Layout> + +# BSD/OS layout +<Layout BSDI> + prefix: /var/www + exec_prefix: /usr/contrib + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/bin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec/apr + mandir: ${exec_prefix}/man + sysconfdir: ${prefix}/conf + datadir: ${prefix} + installbuilddir: ${datadir}/build + includedir: ${exec_prefix}/include/apr + localstatedir: /var + runtimedir: ${localstatedir}/run +</Layout> + +# Solaris 8 Layout +<Layout Solaris> + prefix: /usr/apr + exec_prefix: ${prefix} + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/bin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec + mandir: ${exec_prefix}/man + sysconfdir: /etc/apr + datadir: /var/apr + installbuilddir: ${datadir}/build + includedir: ${exec_prefix}/include + localstatedir: ${prefix} + runtimedir: /var/run +</Layout> + +# OpenBSD Layout +<Layout OpenBSD> + prefix: /var/www + exec_prefix: /usr + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/lib/apr/modules + mandir: ${exec_prefix}/share/man + sysconfdir: ${prefix}/conf + datadir: ${prefix} + installbuilddir: ${prefix}/build + includedir: ${exec_prefix}/lib/apr/include + localstatedir: ${prefix} + runtimedir: ${prefix}/logs +</Layout> + +# Debian layout +<Layout Debian> + prefix: + exec_prefix: ${prefix}/usr + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/lib/apr/modules + mandir: ${exec_prefix}/share/man + datadir: ${exec_prefix}/share/apr + includedir: ${exec_prefix}/include/apr-${APRUTIL_MAJOR_VERSION} + localstatedir: ${prefix}/var/run + runtimedir: ${prefix}/var/run + infodir: ${exec_prefix}/share/info + libsuffix: -${APRUTIL_MAJOR_VERSION} + installbuilddir: ${prefix}/usr/share/apache2/build +</Layout> diff --git a/srclib/libapreq/configure.in b/srclib/libapreq/configure.in new file mode 100644 index 0000000000..7d31878520 --- /dev/null +++ b/srclib/libapreq/configure.in @@ -0,0 +1,173 @@ +dnl +dnl Process this file with autoconf to produce a configure script +dnl + +AC_PREREQ(2.50) +AC_INIT(export_vars.sh.in) + +VERSION=2.08 +AC_SUBST(VERSION) + +AC_CONFIG_HEADER(include/apreq_config.h) +AC_CONFIG_AUX_DIR(build) + +sinclude(build/apreq-conf.m4) +sinclude(build/apreq-hints.m4) +sinclude(build/apr_common.m4) +sinclude(build/find_apr.m4) +sinclude(build/find_apu.m4) + +dnl Generate ./config.nice for reproducing runs of configure +dnl +APR_CONFIG_NICE(config.nice) + +dnl # Some initial steps for configuration. We setup the default directory +dnl # and which files are to be configured. + +dnl Absolute source/build directory +abs_srcdir=`(cd $srcdir && pwd)` +abs_builddir=`pwd` + +if test "$abs_builddir" != "$abs_srcdir"; then + USE_VPATH=1 + APREQ_CONFIG_LOCATION=build +else + APREQ_CONFIG_LOCATION=source +fi + +AC_SUBST(APREQ_CONFIG_LOCATION) + +AC_CANONICAL_SYSTEM + +AC_PROG_INSTALL + +dnl +dnl compute the top directory of the build +dnl note: this is needed for LIBTOOL and exporting the bundled Expat +dnl +top_builddir="$abs_builddir" +AC_SUBST(top_builddir) +AC_SUBST(abs_srcdir) +AC_SUBST(abs_builddir) + +dnl Initialize mkdir -p functionality. +APR_MKDIR_P_CHECK($abs_srcdir/build/mkdir.sh) + + +dnl Enable the layout handling code, then reparse the prefix-style +dnl arguments due to autoconf being a PITA. +APR_ENABLE_LAYOUT(apreq) +APR_PARSE_ARGUMENTS + +dnl load os-specific hints for apr-util +APREQ_PRELOAD + +dnl +dnl set up the compilation flags and stuff +dnl + +APREQ_INCLUDES="" +APREQ_PRIV_INCLUDES="-I$top_builddir/include -I$top_builddir/include/private" +if test -n "$USE_VPATH"; then + APREQ_PRIV_INCLUDES="$APRUTIL_PRIV_INCLUDES -I$abs_srcdir/include/private -I$abs_srcdir/include" +fi + +dnl +dnl Find the APR includes directory and (possibly) the source (base) dir. +dnl +APREQ_FIND_APR + +dnl +dnl even though we use apr_rules.mk for building apr-util, we need +dnl to grab CC and CPP ahead of time so that apr-util config tests +dnl use the same compiler as APR; we need the same compiler options +dnl and feature test macros as well +dnl +APR_SETIFNULL(CC, `$apr_config --cc`) +APR_SETIFNULL(CPP, `$apr_config --cpp`) +APR_ADDTO(CFLAGS, `$apr_config --cflags`) +APR_ADDTO(CPPFLAGS, `$apr_config --cppflags`) + +AC_SUBST(CPP) + +APREQ_FIND_APU + +dnl get our version information +get_version="$abs_srcdir/build/get-version.sh" +version_hdr="$abs_srcdir/include/apreq_version.h" +APREQ_MAJOR_VERSION="`$get_version major $version_hdr APREQ`" +APREQ_MINOR_VERSION="`$get_version minor $version_hdr APREQ`" +APREQ_PATCH_VERSION="`$get_version patch $version_hdr APREQ`" +APREQ_DOTTED_VERSION="`$get_version all $version_hdr APREQ`" + +APR_MAJOR_VERSION="`$apr_config --version | cut -f1 -d.`" +APREQ_LIBTOOL_VERSION="`expr $APREQ_MAJOR_VERSION + $APREQ_MINOR_VERSION + $APR_MAJOR_VERSION`:$APREQ_PATCH_VERSION:$APREQ_MINOR_VERSION" + +AC_SUBST(APREQ_DOTTED_VERSION) +AC_SUBST(APREQ_LIBTOOL_VERSION) +AC_SUBST(APREQ_MAJOR_VERSION) +AC_SUBST(APREQ_MINOR_VERSION) +AC_SUBST(APREQ_PATCH_VERSION) + +echo "APREQ Version: ${APREQ_DOTTED_VERSION}" + + + +so_ext=$APR_SO_EXT +lib_target=$APR_LIB_TARGET +AC_SUBST(so_ext) +AC_SUBST(lib_target) + +APREQ_LIBNAME="apreq2" +AC_SUBST(APREQ_LIBNAME) + +dnl +dnl Prep all the flags and stuff for compilation and export to other builds +dnl +APR_ADDTO(APREQ_LIBS, [$APR_LIBS]) +APR_ADDTO(APREQ_LIBS, [$APU_LIBS]) + +AC_SUBST(APREQ_EXPORT_LIBS) +AC_SUBST(APREQ_PRIV_INCLUDES) +AC_SUBST(APREQ_INCLUDES) +AC_SUBST(APREQ_LDFLAGS) +AC_SUBST(APREQ_LIBS) +AC_SUBST(LDFLAGS) + +dnl copy apr's rules.mk into our build directory. +if test ! -d ./build; then + $mkdir_p build +fi +cp $APR_BUILD_DIR/apr_rules.mk $abs_builddir/build/rules.mk + +dnl +dnl BSD/OS (BSDi) needs to use a different include syntax in the Makefiles +dnl +case "$host_alias" in +*bsdi* | BSD/OS) + # Check whether they've installed GNU make + if make --version > /dev/null 2>&1; then + INCLUDE_RULES="include $abs_builddir/build/rules.mk" + INCLUDE_OUTPUTS="include $abs_srcdir/build-outputs.mk" + else + INCLUDE_RULES=".include \"$abs_builddir/build/rules.mk\"" + INCLUDE_OUTPUTS=".include \"$abs_srcdir/build-outputs.mk\"" + fi + ;; +*) + INCLUDE_RULES="include $abs_builddir/build/rules.mk" + INCLUDE_OUTPUTS="include $abs_srcdir/build-outputs.mk" + ;; +esac +AC_SUBST(INCLUDE_RULES) +AC_SUBST(INCLUDE_OUTPUTS) + +for d in include include/private; do + test -d $top_builddir/$d || mkdir $top_builddir/$d +done + +AC_CONFIG_FILES([Makefile export_vars.sh apreq2-config]) + +AC_CONFIG_COMMANDS([default], [chmod +x apreq2-config]) + +AC_OUTPUT diff --git a/srclib/libapreq/export_vars.sh.in b/srclib/libapreq/export_vars.sh.in new file mode 100644 index 0000000000..08a6296334 --- /dev/null +++ b/srclib/libapreq/export_vars.sh.in @@ -0,0 +1,13 @@ +# +# export_vars.sh +# +# This shell script is used to export vars to the application using the +# APRUTIL library. This script should be "sourced" to ensure the variable +# values are set within the calling script's context. For example: +# +# $ . path/to/apr-util/export_vars.sh +# + +APREQ_EXPORT_INCLUDES="@APREQ_INCLUDES@" +APREQ_EXPORT_LIBS="@APREQ_EXPORT_LIBS@" +APREQ_LDFLAGS="@APREQ_LDFLAGS@" diff --git a/srclib/libapreq/include/apreq.h b/srclib/libapreq/include/apreq.h new file mode 100644 index 0000000000..aa7f0ec111 --- /dev/null +++ b/srclib/libapreq/include/apreq.h @@ -0,0 +1,295 @@ +/* +** 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. +*/ + +#ifndef APREQ_H +#define APREQ_H + +#ifdef APREQ_DEBUG +#include <assert.h> +#endif + +#include "apr_tables.h" +#include <stddef.h> + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @file apreq.h + * @brief Main header file... + * @ingroup libapreq2 + * + * Define the generic APREQ_ macros and common data structures. + */ + +#ifndef WIN32 +/** + * The public APREQ functions are declared with APREQ_DECLARE(), so they may + * use the most appropriate calling convention. Public APR functions with + * variable arguments must use APR_DECLARE_NONSTD(). + * + * @remark Both the declaration and implementations must use the same macro. + */ +/** APREQ_DECLARE(rettype) apeq_func(args) + */ +#define APREQ_DECLARE(d) APR_DECLARE(d) +/** + * The public APEQ functions using variable arguments are declared with + * APEQ_DECLARE_NONSTD(), as they must follow the C language calling convention. + * @see APEQ_DECLARE @see APEQ_DECLARE_DATA + * @remark Both the declaration and implementations must use the same macro. + * @example + */ +/** APEQ_DECLARE_NONSTD(rettype) apr_func(args, ...); + */ +#define APREQ_DECLARE_NONSTD(d) APR_DECLARE_NONSTD(d) +/** + * The public APREQ variables are declared with APREQ_DECLARE_DATA. + * This assures the appropriate indirection is invoked at compile time. + * @see APREQ_DECLARE @see APREQ_DECLARE_NONSTD + * @remark Note that the declaration and implementations use different forms, + * but both must include the macro. + */ +/** extern APREQ_DECLARE_DATA type apr_variable;\n + * APREQ_DECLARE_DATA type apr_variable = value; + */ +#define APREQ_DECLARE_DATA +#elif defined (APREQ_DECLARE_STATIC) +#define APREQ_DECLARE(type) type __stdcall +#define APREQ_DECLARE_NONSTD(type) type +#define APREQ_DECLARE_DATA +#elif defined (APREQ_DECLARE_EXPORT) +#define APREQ_DECLARE(type) __declspec(dllexport) type __stdcall +#define APREQ_DECLARE_NONSTD(type) __declspec(dllexport) type +#define APREQ_DECLARE_DATA __declspec(dllexport) +#else +#define APREQ_DECLARE(type) __declspec(dllimport) type __stdcall +#define APREQ_DECLARE_NONSTD(type) __declspec(dllimport) type +#define APREQ_DECLARE_DATA __declspec(dllimport) +#endif + +/** + * Read chucks of data in 64k blocks from the request + */ + +#define APREQ_DEFAULT_READ_BLOCK_SIZE (64 * 1024) + +/** + * Maximum number of bytes mod_apreq2 will send off to libapreq2 for parsing. + * mod_apreq2 will log this event and subsequently remove itself + * from the filter chain. + * @see ap_set_read_limit + */ +#define APREQ_DEFAULT_READ_LIMIT (64 * 1024 * 1024) +/** + * Maximum number of bytes mod_apreq2 will let accumulate within the + * heap-buckets in a brigade. Excess data will be spooled to an + * appended file bucket + * @see ap_set_brigade_read_limit + */ +#define APREQ_DEFAULT_BRIGADE_LIMIT (256 * 1024) + +/** + * Number of elements in the initial apr_table + * @see apr_table_make + */ +#define APREQ_DEFAULT_NELTS 8 + + + +/** + * Check to see if specified bit f is off in bitfiled name + */ +#define APREQ_FLAGS_OFF(f, name) ((f) &= ~(name##_MASK << name##_BIT)) +/** + * Check to see if specified bit f is on in bitfiled name + */ +#define APREQ_FLAGS_ON(f, name) ((f) |= (name##_MASK << name##_BIT)) +/** + * Get specified bit f in bitfiled name + */ +#define APREQ_FLAGS_GET(f, name) (((f) >> name##_BIT) & name##_MASK) +/** + * Set specified bit f in bitfiled name to value + * Note the below BIT/Mask defines are used sans the + * _BIT, _MASK because of the this define's \#\#_MASK, \#\#_BIT usage. + * Each come in a pair + */ +#define APREQ_FLAGS_SET(f, name, value) \ + ((f) = (((f) & ~(name##_MASK << name##_BIT)) \ + | ((name##_MASK & (value)) << name##_BIT))) + +/** + * Charset Bit + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_CHARSET_BIT 0 + +/** + * Charset Mask + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_CHARSET_MASK 255 + +/** + * Tainted Bit + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_TAINTED_BIT 8 +/** + * Tainted Mask + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_TAINTED_MASK 1 + +/** + * Cookier Version Bit + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ + +#define APREQ_COOKIE_VERSION_BIT 11 +/** + * Cookie Version Mask + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_COOKIE_VERSION_MASK 3 + +/** + * Cookie's Secure Bit + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_COOKIE_SECURE_BIT 13 +/** + * Cookie's Secure Mask + * @see APREQ_FLAGS_OFF @see APREQ_FLAGS_ON + * @see APREQ_FLAGS_GET @see APREQ_FLAGS_SET + */ +#define APREQ_COOKIE_SECURE_MASK 1 + +/** Character encodings. */ +typedef enum { + APREQ_CHARSET_ASCII =0, + APREQ_CHARSET_LATIN1 =1, /* ISO-8859-1 */ + APREQ_CHARSET_CP1252 =2, /* Windows-1252 */ + APREQ_CHARSET_UTF8 =8 +} apreq_charset_t; + + +/** @enum apreq_join_t Join type */ +typedef enum { + APREQ_JOIN_AS_IS, /**< Join the strings without modification */ + APREQ_JOIN_ENCODE, /**< Url-encode the strings before joining them */ + APREQ_JOIN_DECODE, /**< Url-decode the strings before joining them */ + APREQ_JOIN_QUOTE /**< Quote the strings, backslashing existing quote marks. */ +} apreq_join_t; + +/** @enum apreq_match_t Match type */ +typedef enum { + APREQ_MATCH_FULL, /**< Full match only. */ + APREQ_MATCH_PARTIAL /**< Partial matches are ok. */ +} apreq_match_t; + +/** @enum apreq_expires_t Expiration date format */ +typedef enum { + APREQ_EXPIRES_HTTP, /**< Use date formatting consistent with RFC 2616 */ + APREQ_EXPIRES_NSCOOKIE /**< Use format consistent with Netscape's Cookie Spec */ +} apreq_expires_t; + + +/** @brief libapreq's pre-extensible string type */ +typedef struct apreq_value_t { + char *name; /**< value name */ + apr_size_t nlen; /**< length of name */ + apr_size_t dlen; /**< length of data */ + char data[1]; /**< value data */ +} apreq_value_t; + +/** + * Adds the specified apreq_value_t to the apr_table_t. + * + * @param v value to add + * @param t add v to this table + * + * @return void + * + * @ see apr_table_t @see apr_value_t + */ + +static APR_INLINE +void apreq_value_table_add(const apreq_value_t *v, apr_table_t *t) { + apr_table_addn(t, v->name, v->data); +} + +/** + * @param T type + * @param A attribute + * @param P + * + * XXX + */ +#define apreq_attr_to_type(T,A,P) ( (T*) ((char*)(P)-offsetof(T,A)) ) + +/** + * Initialize libapreq2. Applications (except apache modules using + * mod_apreq) should call this exactly once before they use any + * libapreq2 modules. If you want to modify the list of default parsers + * with apreq_register_parser(), please use apreq_pre_initialize() + * and apreq_post_initialize() instead. + * + * @param pool a base pool persisting while libapreq2 is used + * @remarks after you detroy the pool, you have to call this function again + * with a new pool if you still plan to use libapreq2 + */ +APREQ_DECLARE(apr_status_t) apreq_initialize(apr_pool_t *pool); + + +/** + * Pre-initialize libapreq2. Applications (except apache modules using + * mod_apreq2) should call this exactly once before they register custom + * parsers with libapreq2. mod_apreq2 does this automatically during the + * post-config phase, so modules that need call apreq_register_parser should + * create a post-config hook using APR_HOOK_MIDDLE. + * + * @param pool a base pool persisting while libapreq2 is used + * @remarks after you detroyed the pool, you have to call this function again + * with a new pool if you still plan to use libapreq2 + */ +APREQ_DECLARE(apr_status_t) apreq_pre_initialize(apr_pool_t *pool); + +/** + * Post-initialize libapreq2. Applications (except apache modules using + * mod_apreq2) should this exactly once before they use any + * libapreq2 modules for parsing. + * + * @param pool the same pool that was used in apreq_pre_initialize(). + */ +APREQ_DECLARE(apr_status_t) apreq_post_initialize(apr_pool_t *pool); + + +#ifdef __cplusplus + } +#endif + +#endif /* APREQ_H */ diff --git a/srclib/libapreq/include/apreq_cookie.h b/srclib/libapreq/include/apreq_cookie.h new file mode 100644 index 0000000000..b1d5dda05a --- /dev/null +++ b/srclib/libapreq/include/apreq_cookie.h @@ -0,0 +1,216 @@ +/* +** 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. +*/ + +#ifndef APREQ_COOKIE_H +#define APREQ_COOKIE_H + +#include "apreq.h" +#include "apr_time.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file apreq_cookie.h + * @brief Cookies and Jars. + * @ingroup libapreq2 + * + * apreq_cookie.h describes a common server-side API for request (incoming) + * and response (outgoing) cookies. It aims towards compliance with the + * standard cookie specifications listed below. + * + * @see http://wp.netscape.com/newsref/std/cookie_spec.html + * @see http://www.ietf.org/rfc/rfc2109.txt + * @see http://www.ietf.org/rfc/rfc2964.txt + * @see http://www.ietf.org/rfc/rfc2965.txt + * + */ + +/** @todo convert this macro to an apreq_module_t method. + * + * Maximum length of a single Set-Cookie(2) header. + */ +#define APREQ_COOKIE_MAX_LENGTH 4096 + +/** @brief Cookie type, supporting both Netscape and RFC cookie specifications. + */ + +typedef struct apreq_cookie_t { + + char *path; /**< Restricts url path */ + char *domain; /**< Restricts server domain */ + char *port; /**< Restricts server port */ + char *comment; /**< RFC cookies may send a comment */ + char *commentURL; /**< RFC cookies may place an URL here */ + apr_time_t max_age; /**< total duration of cookie: -1 == session */ + unsigned flags; /**< charsets, taint marks, app-specific bits */ + const apreq_value_t v; /**< "raw" cookie value */ + +} apreq_cookie_t; + + +/** Upgrades a jar's table values to apreq_cookie_t structs. */ +static APR_INLINE +apreq_cookie_t *apreq_value_to_cookie(const char *val) +{ + union { const char *in; char *out; } deconst; + + deconst.in = val; + return apreq_attr_to_type(apreq_cookie_t, v, + apreq_attr_to_type(apreq_value_t, data, deconst.out)); +} + +/**@return 1 if this is an RFC cookie, 0 if its a Netscape cookie. */ +static APR_INLINE +unsigned apreq_cookie_version(const apreq_cookie_t *c) { + return APREQ_FLAGS_GET(c->flags, APREQ_COOKIE_VERSION); +} + +/** Sets the cookie's protocol version. */ +static APR_INLINE +void apreq_cookie_version_set(apreq_cookie_t *c, unsigned v) { + APREQ_FLAGS_SET(c->flags, APREQ_COOKIE_VERSION, v); +} + +/** @return 1 if the secure flag is set, 0 otherwise. */ +static APR_INLINE +unsigned apreq_cookie_is_secure(const apreq_cookie_t *c) { + return APREQ_FLAGS_GET(c->flags, APREQ_COOKIE_SECURE); +} + +/** Sets the cookie's secure flag, meaning it only + * comes back over an SSL-encrypted connction. + */ +static APR_INLINE +void apreq_cookie_secure_on(apreq_cookie_t *c) { + APREQ_FLAGS_ON(c->flags, APREQ_COOKIE_SECURE); +} + +/** Turns off the cookie's secure flag. */ +static APR_INLINE +void apreq_cookie_secure_off(apreq_cookie_t *c) { + APREQ_FLAGS_OFF(c->flags, APREQ_COOKIE_SECURE); +} + + +/** @return 1 if the taint flag is set, 0 otherwise. */ +static APR_INLINE +unsigned apreq_cookie_is_tainted(const apreq_cookie_t *c) { + return APREQ_FLAGS_GET(c->flags, APREQ_TAINTED); +} + +/** Sets the cookie's tainted flag. */ +static APR_INLINE +void apreq_cookie_tainted_on(apreq_cookie_t *c) { + APREQ_FLAGS_ON(c->flags, APREQ_TAINTED); +} + +/** Turns off the cookie's tainted flag. */ +static APR_INLINE +void apreq_cookie_tainted_off(apreq_cookie_t *c) { + APREQ_FLAGS_OFF(c->flags, APREQ_TAINTED); +} + +/** + * Parse a cookie header and store the cookies in an apr_table_t. + * + * @param pool pool which allocates the cookies + * @param jar table where parsed cookies are stored + * @param header the header value + * + * @return APR_SUCCESS. + * @return ::APREQ_ERROR_BADSEQ if an unparseable character sequence appears. + * @return ::APREQ_ERROR_MISMATCH if an rfc-cookie attribute appears in a + * netscape cookie header. + * @return ::APR_ENOTIMPL if an unrecognized rfc-cookie attribute appears. + * @return ::APREQ_ERROR_NOTOKEN if a required token was not present. + * @return ::APREQ_ERROR_BADCHAR if an unexpected token was present. + */ +APREQ_DECLARE(apr_status_t) apreq_parse_cookie_header(apr_pool_t *pool, + apr_table_t *jar, + const char *header); + +/** + * Returns a new cookie, made from the argument list. + * + * @param pool Pool which allocates the cookie. + * @param name The cookie's name. + * @param nlen Length of name. + * @param value The cookie's value. + * @param vlen Length of value. + * + * @return the new cookie + */ +APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(apr_pool_t *pool, + const char *name, + const apr_size_t nlen, + const char *value, + const apr_size_t vlen); + +/** + * Returns a string that represents the cookie as it would appear + * in a valid "Set-Cookie*" header. + * + * @param c cookie. + * @param p pool which allocates the returned string. + * + * @return header string. + */ +APREQ_DECLARE(char*) apreq_cookie_as_string(const apreq_cookie_t *c, + apr_pool_t *p); + + +/** + * Same functionality as apreq_cookie_as_string. Stores the string + * representation in buf, using up to len bytes in buf as storage. + * The return value has the same semantics as that of apr_snprintf, + * including the special behavior for a "len = 0" argument. + * + * @param c cookie. + * @param buf storage location for the result. + * @param len size of buf's storage area. + * + * @return size of resulting header string. + */ +APREQ_DECLARE(int) apreq_cookie_serialize(const apreq_cookie_t *c, + char *buf, apr_size_t len); + +/** + * Set the Cookie's expiration date. + * + * @param c The cookie. + * @param time_str If NULL, the Cookie's expiration date is unset, + * making it a session cookie. This means no "expires" or "max-age" + * attribute will appear in the cookie's serialized form. If time_str + * is not NULL, the expiration date will be reset to the offset (from now) + * represented by time_str. The time_str should be in a format that + * apreq_atoi64t() can understand, namely /[+-]?\\d+\\s*[YMDhms]/. + * + * @remarks Now time_str may also be a fixed date; see apr_date_parse_rfc() + * for admissible formats. + */ +APREQ_DECLARE(void) apreq_cookie_expires(apreq_cookie_t *c, + const char *time_str); + +#ifdef __cplusplus + } +#endif + +#endif /*APREQ_COOKIE_H*/ + + diff --git a/srclib/libapreq/include/apreq_error.h b/srclib/libapreq/include/apreq_error.h new file mode 100644 index 0000000000..b2de1fd2be --- /dev/null +++ b/srclib/libapreq/include/apreq_error.h @@ -0,0 +1,97 @@ +/* +** 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. +*/ + +#ifndef APREQ_ERROR_H +#define APREQ_ERROR_H + +#include "apr_errno.h" +#include "apreq.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * apreq's wrapper around apr_strerror(); + * recognizes APREQ_ERROR_* status codes. + */ +APREQ_DECLARE(char *) +apreq_strerror(apr_status_t s, char *buf, apr_size_t bufsize); + +/** + * @file apreq_error.h + * @brief Error status codes. + * @ingroup libapreq2 + * + * Define the APREQ_ error codes. + */ + +#ifndef APR_EBADARG +/** + * Bad Arguments return value + * @see APR_BADARG + */ +#define APR_EBADARG APR_BADARG /* XXX: don't use APR_BADARG */ +#endif + +/** Internal apreq error. */ +#define APREQ_ERROR_GENERAL APR_OS_START_USERERR +/** Attempted to perform unsafe action with tainted data. */ +#define APREQ_ERROR_TAINTED (APREQ_ERROR_GENERAL + 1) +/** Parsing interrupted. */ +#define APREQ_ERROR_INTERRUPT (APREQ_ERROR_GENERAL + 2) + +/** Invalid input data. */ +#define APREQ_ERROR_BADDATA (APREQ_ERROR_GENERAL + 10) +/** Invalid character. */ +#define APREQ_ERROR_BADCHAR (APREQ_ERROR_BADDATA + 1) +/** Invalid byte sequence. */ +#define APREQ_ERROR_BADSEQ (APREQ_ERROR_BADDATA + 2) +/** Invalid attribute. */ +#define APREQ_ERROR_BADATTR (APREQ_ERROR_BADDATA + 3) +/** Invalid header. */ +#define APREQ_ERROR_BADHEADER (APREQ_ERROR_BADDATA + 4) +/** Invalid utf8 encoding. */ +#define APREQ_ERROR_BADUTF8 (APREQ_ERROR_BADDATA + 5) + +/** Missing input data. */ +#define APREQ_ERROR_NODATA (APREQ_ERROR_GENERAL + 20) +/** Missing required token. */ +#define APREQ_ERROR_NOTOKEN (APREQ_ERROR_NODATA + 1) +/** Missing attribute. */ +#define APREQ_ERROR_NOATTR (APREQ_ERROR_NODATA + 2) +/** Missing header. */ +#define APREQ_ERROR_NOHEADER (APREQ_ERROR_NODATA + 3) +/** Missing parser. */ +#define APREQ_ERROR_NOPARSER (APREQ_ERROR_NODATA + 4) + + +/** Conflicting information. */ +#define APREQ_ERROR_MISMATCH (APREQ_ERROR_GENERAL + 30) +/** Exceeds configured maximum limit. */ +#define APREQ_ERROR_OVERLIMIT (APREQ_ERROR_MISMATCH + 1) +/** Below configured minimum limit. */ +#define APREQ_ERROR_UNDERLIMIT (APREQ_ERROR_MISMATCH + 2) +/** Setting already configured. */ +#define APREQ_ERROR_NOTEMPTY (APREQ_ERROR_MISMATCH + 3) + + +#ifdef __cplusplus + } +#endif + +#endif /* APREQ_ERROR_H */ diff --git a/srclib/libapreq/include/apreq_module.h b/srclib/libapreq/include/apreq_module.h new file mode 100644 index 0000000000..1bb2345e04 --- /dev/null +++ b/srclib/libapreq/include/apreq_module.h @@ -0,0 +1,457 @@ +/* +** 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. +*/ + +#ifndef APREQ_MODULE_H +#define APREQ_MODULE_H + +#include "apreq_cookie.h" +#include "apreq_parser.h" +#include "apreq_error.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * @file apreq_module.h + * @brief Module API + * @ingroup libapreq2 + */ + + +/** + * An apreq handle associated with a module. The structure + * may have variable size, because the module may append its own data + * structures after it. + */ +typedef struct apreq_handle_t { + /** the apreq module which implements this handle */ + const struct apreq_module_t *module; + /** the pool which defines the lifetime of the parsed data */ + apr_pool_t *pool; + /** the allocator, which persists at least as long as the pool */ + apr_bucket_alloc_t *bucket_alloc; + +} apreq_handle_t; + +/** + * @brief Vtable describing the necessary environment functions. + */ + + +typedef struct apreq_module_t { + /** name of this apreq module */ + const char *name; + /** magic number identifying the module and version */ + apr_uint32_t magic_number; + + /** get a table with all cookies */ + apr_status_t (*jar)(apreq_handle_t *, const apr_table_t **); + /** get a table with all query string parameters */ + apr_status_t (*args)(apreq_handle_t *, const apr_table_t **); + /** get a table with all body parameters */ + apr_status_t (*body)(apreq_handle_t *, const apr_table_t **); + + /** get a cookie by its name */ + apreq_cookie_t *(*jar_get)(apreq_handle_t *, const char *); + /** get a query string parameter by its name */ + apreq_param_t *(*args_get)(apreq_handle_t *, const char *); + /** get a body parameter by its name */ + apreq_param_t *(*body_get)(apreq_handle_t *, const char *); + + /** gets the parser associated with the request body */ + apr_status_t (*parser_get)(apreq_handle_t *, const apreq_parser_t **); + /** manually set a parser for the request body */ + apr_status_t (*parser_set)(apreq_handle_t *, apreq_parser_t *); + /** add a hook function */ + apr_status_t (*hook_add)(apreq_handle_t *, apreq_hook_t *); + + /** determine the maximum in-memory bytes a brigade may use */ + apr_status_t (*brigade_limit_get)(apreq_handle_t *, apr_size_t *); + /** set the maximum in-memory bytes a brigade may use */ + apr_status_t (*brigade_limit_set)(apreq_handle_t *, apr_size_t); + + /** determine the maximum amount of data that will be fed into a parser */ + apr_status_t (*read_limit_get)(apreq_handle_t *, apr_uint64_t *); + /** set the maximum amount of data that will be fed into a parser */ + apr_status_t (*read_limit_set)(apreq_handle_t *, apr_uint64_t); + + /** determine the directory used by the parser for temporary files */ + apr_status_t (*temp_dir_get)(apreq_handle_t *, const char **); + /** set the directory used by the parser for temporary files */ + apr_status_t (*temp_dir_set)(apreq_handle_t *, const char *); + +} apreq_module_t; + + +/** + * Defines the module-specific status codes which + * are commonly considered to be non-fatal. + * + * @param s status code returned by an apreq_module_t method. + * + * @return 1 if s is fatal, 0 otherwise. + */ +static APR_INLINE +unsigned apreq_module_status_is_error(apr_status_t s) { + switch (s) { + case APR_SUCCESS: + case APR_INCOMPLETE: + case APR_EINIT: + case APREQ_ERROR_NODATA: + case APREQ_ERROR_NOPARSER: + case APREQ_ERROR_NOHEADER: + return 0; + default: + return 1; + } +} + + +/** + * Expose the parsed "cookie" header associated to this handle. + * + * @param req The request handle + * @param t The resulting table, which will either be NULL or a + * valid table object on return. + * + * @return APR_SUCCESS or a module-specific error status code. + */ +static APR_INLINE +apr_status_t apreq_jar(apreq_handle_t *req, const apr_table_t **t) +{ + return req->module->jar(req,t); +} + +/** + * Expose the parsed "query string" associated to this handle. + * + * @param req The request handle + * @param t The resulting table, which will either be NULL or a + * valid table object on return. + * + * @return APR_SUCCESS or a module-specific error status code. + */ +static APR_INLINE +apr_status_t apreq_args(apreq_handle_t *req, const apr_table_t **t) +{ + return req->module->args(req,t); +} + +/** + * Expose the parsed "request body" associated to this handle. + * + * @param req The request handle + * @param t The resulting table, which will either be NULL or a + * valid table object on return. + * + * @return APR_SUCCESS or a module-specific error status code. + */ +static APR_INLINE +apr_status_t apreq_body(apreq_handle_t *req, const apr_table_t **t) +{ + return req->module->body(req, t); +} + + +/** + * Fetch the first cookie with the given name. + * + * @param req The request handle + * @param name Case-insensitive cookie name. + * + * @return First matching cookie, or NULL if none match. + */ +static APR_INLINE +apreq_cookie_t *apreq_jar_get(apreq_handle_t *req, const char *name) +{ + return req->module->jar_get(req, name); +} + +/** + * Fetch the first query string param with the given name. + * + * @param req The request handle + * @param name Case-insensitive param name. + * + * @return First matching param, or NULL if none match. + */ +static APR_INLINE +apreq_param_t *apreq_args_get(apreq_handle_t *req, const char *name) +{ + return req->module->args_get(req, name); +} + +/** + * Fetch the first body param with the given name. + * + * @param req The request handle + * @param name Case-insensitive cookie name. + * + * @return First matching param, or NULL if none match. + */ +static APR_INLINE +apreq_param_t *apreq_body_get(apreq_handle_t *req, const char *name) +{ + return req->module->body_get(req, name); +} + +/** + * Fetch the active body parser. + * + * @param req The request handle + * @param parser Points to the active parser on return. + * + * @return APR_SUCCESS or module-specific error. + * + */ +static APR_INLINE +apr_status_t apreq_parser_get(apreq_handle_t *req, + const apreq_parser_t **parser) +{ + return req->module->parser_get(req, parser); +} + + +/** + * Set the body parser for this request. + * + * @param req The request handle + * @param parser New parser to use. + * + * @return APR_SUCCESS or module-specific error. + */ +static APR_INLINE +apr_status_t apreq_parser_set(apreq_handle_t *req, + apreq_parser_t *parser) +{ + return req->module->parser_set(req, parser); +} + +/** + * Add a parser hook for this request. + * + * @param req The request handle + * @param hook Hook to add. + * + * @return APR_SUCCESS or module-specific error. + */ +static APR_INLINE +apr_status_t apreq_hook_add(apreq_handle_t *req, apreq_hook_t *hook) +{ + return req->module->hook_add(req, hook); +} + + +/** + * Set the active brigade limit. + * + * @param req The handle. + * @param bytes New limit to use. + * + * @return APR_SUCCESS or module-specific error. + * + */ +static APR_INLINE +apr_status_t apreq_brigade_limit_set(apreq_handle_t *req, + apr_size_t bytes) +{ + return req->module->brigade_limit_set(req, bytes); +} + +/** + * Get the active brigade limit. + * + * @param req The handle. + * @param bytes Pointer to resulting (current) limit. + * + * @return APR_SUCCESS or a module-specific error, + * which may leave bytes undefined. + */ +static APR_INLINE +apr_status_t apreq_brigade_limit_get(apreq_handle_t *req, + apr_size_t *bytes) +{ + return req->module->brigade_limit_get(req, bytes); +} + +/** + * Set the active read limit. + * + * @param req The handle. + * @param bytes New limit to use. + * + * @return APR_SUCCESS or a module-specific error. + * + */ +static APR_INLINE +apr_status_t apreq_read_limit_set(apreq_handle_t *req, + apr_uint64_t bytes) +{ + return req->module->read_limit_set(req, bytes); +} + +/** + * Get the active read limit. + * + * @param req The request handle. + * @param bytes Pointer to resulting (current) limit. + * + * @return APR_SUCCESS or a module-specific error, + * which may leave bytes undefined. + */ +static APR_INLINE +apr_status_t apreq_read_limit_get(apreq_handle_t *req, + apr_uint64_t *bytes) +{ + return req->module->read_limit_get(req, bytes); +} + +/** + * Set the active temp directory. + * + * @param req The handle. + * @param path New path to use; may be NULL. + * + * @return APR_SUCCESS or a module-specific error . + */ +static APR_INLINE +apr_status_t apreq_temp_dir_set(apreq_handle_t *req, const char *path) +{ + return req->module->temp_dir_set(req, path); +} + +/** + * Get the active temp directory. + * + * @param req The handle. + * @param path Resulting path to temp dir. + * + * @return APR_SUCCESS implies path is valid, but may also be NULL. + * Any other return value is module-specific, and may leave + * path undefined. + */ +static APR_INLINE +apr_status_t apreq_temp_dir_get(apreq_handle_t *req, const char **path) +{ + return req->module->temp_dir_get(req, path); +} + + + +/** + * Convenience macro for defining a module by mapping + * a function prefix to an associated apreq_module_t structure. + * + * @param pre Prefix to define new environment. All attributes of + * the apreq_env_module_t struct are defined with this as their + * prefix. The generated struct is named by appending "_module" to + * the prefix. + * @param mmn Magic number (i.e. version number) of this environment. + */ +#define APREQ_MODULE(pre, mmn) const apreq_module_t \ + pre##_module = { #pre, mmn, \ + pre##_jar, pre##_args, pre##_body, \ + pre##_jar_get, pre##_args_get, pre##_body_get, \ + pre##_parser_get, pre##_parser_set, pre##_hook_add, \ + pre##_brigade_limit_get, pre##_brigade_limit_set, \ + pre##_read_limit_get, pre##_read_limit_set, \ + pre##_temp_dir_get, pre##_temp_dir_set, \ + } + + +/** + * Create an apreq handle which is suitable for a CGI program. It + * reads input from stdin and writes output to stdout. + * + * @param pool Pool associated to this handle. + * + * @return New handle; can only be NULL if the pool allocation failed. + * + * @remarks The handle gets cached in the pool's userdata, so subsequent + * calls will retrieve the original cached handle. + */ +APREQ_DECLARE(apreq_handle_t*) apreq_handle_cgi(apr_pool_t *pool); + +/** + * Create a custom apreq handle which knows only some static + * values. Useful if you want to test the parser code or if you have + * got data from a custom source (neither Apache 2 nor CGI). + * + * @param pool allocates the parse data, + * @param query_string parsed into args table + * @param cookie value of the request "Cookie" header + * @param parser parses the request body + * @param read_limit maximum bytes to read from the body + * @param in brigade containing the request body + * + * @return new handle; can only be NULL if the pool allocation failed. + */ +APREQ_DECLARE(apreq_handle_t*) apreq_handle_custom(apr_pool_t *pool, + const char *query_string, + const char *cookie, + apreq_parser_t *parser, + apr_uint64_t read_limit, + apr_bucket_brigade *in); + +/** + * Find the first query string parameter or body parameter with the + * specified name. The match is case-insensitive. + * + * @param req request handle. + * @param key desired parameter name + * + * @return The first matching parameter (with args searched first) or NULL. + */ +APREQ_DECLARE(apreq_param_t *)apreq_param(apreq_handle_t *req, const char *key); + +/** + * Find the first cookie with the specified name. + * The match is case-insensitive. + * + * @param req request handle. + * @param name desired cookie name + * + * @return The first matching parameter (with args searched first) or NULL. + */ +#define apreq_cookie(req, name) apreq_jar_get(req, name) + +/** + * Returns a table containing key-value pairs for the full request + * (args + body). + * + * @param req request handle + * @param p allocates the returned table. + * + * @return table representing all available params; is never NULL. + */ +APREQ_DECLARE(apr_table_t *) apreq_params(apreq_handle_t *req, apr_pool_t *p); + + +/** + * Returns a table containing all request cookies. + * + * @param req the apreq request handle + * @param p Allocates the returned table. + */ +APREQ_DECLARE(apr_table_t *)apreq_cookies(apreq_handle_t *req, apr_pool_t *p); + +#ifdef __cplusplus + } +#endif + +#endif /* APREQ_MODULE_H */ diff --git a/srclib/libapreq/include/apreq_param.h b/srclib/libapreq/include/apreq_param.h new file mode 100644 index 0000000000..832cfc2f38 --- /dev/null +++ b/srclib/libapreq/include/apreq_param.h @@ -0,0 +1,209 @@ +/* +** 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. +*/ + +#ifndef APREQ_PARAM_H +#define APREQ_PARAM_H + +#include "apreq.h" +#include "apr_buckets.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @file apreq_param.h + * @brief Request parsing and parameter API + * @ingroup libapreq2 + */ + + +/** Common data structure for params and file uploads */ +typedef struct apreq_param_t { + apr_table_t *info; /**< header table associated with the param */ + apr_bucket_brigade *upload; /**< brigade used to spool upload files */ + unsigned flags; /**< charsets, taint marks, app-specific bits */ + const apreq_value_t v; /**< underlying name/value info */ +} apreq_param_t; + + +/** @return 1 if the taint flag is set, 0 otherwise. */ +static APR_INLINE +unsigned apreq_param_is_tainted(const apreq_param_t *p) { + return APREQ_FLAGS_GET(p->flags, APREQ_TAINTED); +} + +/** Sets the tainted flag. */ +static APR_INLINE +void apreq_param_tainted_on(apreq_param_t *p) { + APREQ_FLAGS_ON(p->flags, APREQ_TAINTED); +} + +/** Turns off the taint flag. */ +static APR_INLINE +void apreq_param_tainted_off(apreq_param_t *p) { + APREQ_FLAGS_OFF(p->flags, APREQ_TAINTED); +} + +/** Sets the character encoding for this parameter. */ +static APR_INLINE +apreq_charset_t apreq_param_charset_set(apreq_param_t *p, apreq_charset_t c) { + apreq_charset_t old = (apreq_charset_t) + APREQ_FLAGS_GET(p->flags, APREQ_CHARSET); + APREQ_FLAGS_SET(p->flags, APREQ_CHARSET, c); + return old; +} + +/** Gets the character encoding for this parameter. */ +static APR_INLINE +apreq_charset_t apreq_param_charset_get(apreq_param_t *p) { + return (apreq_charset_t)APREQ_FLAGS_GET(p->flags, APREQ_CHARSET); +} + + +/** Upgrades args and body table values to apreq_param_t structs. */ +static APR_INLINE +apreq_param_t *apreq_value_to_param(const char *val) +{ + union { const char *in; char *out; } deconst; + + deconst.in = val; + return apreq_attr_to_type(apreq_param_t, v, + apreq_attr_to_type(apreq_value_t, data, deconst.out)); +} + + + +/** creates a param from name/value information */ +APREQ_DECLARE(apreq_param_t *) apreq_param_make(apr_pool_t *p, + const char *name, + const apr_size_t nlen, + const char *val, + const apr_size_t vlen); + +/** + * Url-decodes a name=value pair into a param. + * + * @param param points to the decoded parameter on success + * @param pool Pool from which the param is allocated. + * @param word Start of the name=value pair. + * @param nlen Length of urlencoded name. + * @param vlen Length of urlencoded value. + * + * @return APR_SUCCESS on success. + * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input. + * + * @remarks Unless vlen == 0, this function assumes there is + * exactly one character ('=') which separates the pair. + * + */ +APREQ_DECLARE(apr_status_t) apreq_param_decode(apreq_param_t **param, + apr_pool_t *pool, + const char *word, + apr_size_t nlen, + apr_size_t vlen); + +/** + * Url-encodes the param into a name-value pair. + * @param pool Pool which allocates the returned string. + * @param param Param to encode. + * @return name-value pair representing the param. + */ +APREQ_DECLARE(char *) apreq_param_encode(apr_pool_t *pool, + const apreq_param_t *param); + +/** + * Parse a url-encoded string into a param table. + * @param pool pool used to allocate the param data. + * @param t table to which the params are added. + * @param qs Query string to url-decode. + * @return APR_SUCCESS if successful, error otherwise. + * @remark This function uses [&;] as the set of tokens + * to delineate words, and will treat a word w/o '=' + * as a name-value pair with value-length = 0. + * + */ +APREQ_DECLARE(apr_status_t) apreq_parse_query_string(apr_pool_t *pool, + apr_table_t *t, + const char *qs); + + +/** + * Returns an array of parameters (apreq_param_t *) matching the given key. + * The key is case-insensitive. + * @param p Allocates the returned array. + * @param t the parameter table returned by apreq_args(), apreq_body() + * or apreq_params() + * @param key Null-terminated search key, case insensitive. + * key==NULL fetches all parameters. + * @return an array of apreq_param_t* (pointers) + * @remark Also parses the request if necessary. + */ +APREQ_DECLARE(apr_array_header_t *) apreq_params_as_array(apr_pool_t *p, + const apr_table_t *t, + const char *key); + +/** + * Returns a ", " -joined string containing all parameters + * for the requested key, an empty string if none are found. + * The key is case-insensitive. + * + * @param p Allocates the return string. + * @param t the parameter table returned by apreq_args(), apreq_body() + * or apreq_params() + * @param key Null-terminated parameter name, case insensitive. + * key==NULL fetches all values. + * @param mode Join type- see apreq_join(). + * @return the joined string or NULL on error + * @remark Also parses the request if necessary. + */ +APREQ_DECLARE(const char *) apreq_params_as_string(apr_pool_t *p, + const apr_table_t *t, + const char *key, + apreq_join_t mode); + +/** + * Returns a table of all params in req->body with non-NULL upload brigades. + * @param body parameter table returned by apreq_body() or apreq_params() + * @param pool Pool which allocates the table struct. + * @return Upload table. + * @remark Will parse the request if necessary. + */ +APREQ_DECLARE(const apr_table_t *) apreq_uploads(const apr_table_t *body, + apr_pool_t *pool); + +/** + * Returns the first param in req->body which has both param->v.name + * matching key (case insensitive) and param->upload != NULL. + * @param body parameter table returned by apreq_body() or apreq_params() + * @param name Parameter name. key == NULL returns first upload. + * @return Corresponding upload, NULL if none found. + * @remark Will parse the request as necessary. + */ +APREQ_DECLARE(const apreq_param_t *) apreq_upload(const apr_table_t *body, + const char *name); + + +#ifdef __cplusplus +} +#endif + +#endif /* APREQ_PARAM_H */ + + + diff --git a/srclib/libapreq/include/apreq_parser.h b/srclib/libapreq/include/apreq_parser.h new file mode 100644 index 0000000000..e0f38b96f7 --- /dev/null +++ b/srclib/libapreq/include/apreq_parser.h @@ -0,0 +1,287 @@ +/* +** 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. +*/ + +#ifndef APREQ_PARSERS_H +#define APREQ_PARSERS_H +/* These structs are defined below */ + +#include "apreq_param.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @file apreq_parser.h + * @brief Request body parser API + * @ingroup libapreq2 + */ + +/** + * A hook is called by the parser whenever data arrives in a file + * upload parameter of the request body. You may associate any number + * of hooks with a parser instance with apreq_parser_add_hook(). + */ +typedef struct apreq_hook_t apreq_hook_t; + +/** + * A request body parser instance. + */ +typedef struct apreq_parser_t apreq_parser_t; + +/** Parser arguments. */ +#define APREQ_PARSER_ARGS apreq_parser_t *parser, \ + apr_table_t *t, \ + apr_bucket_brigade *bb + +/** Hook arguments */ +#define APREQ_HOOK_ARGS apreq_hook_t *hook, \ + apreq_param_t *param, \ + apr_bucket_brigade *bb + +/** + * The callback function implementing a request body parser. + */ +typedef apr_status_t (*apreq_parser_function_t)(APREQ_PARSER_ARGS); + +/** + * The callback function of a hook. See apreq_hook_t. + */ +typedef apr_status_t (*apreq_hook_function_t)(APREQ_HOOK_ARGS); + +/** + * Declares a API parser. + */ +#define APREQ_DECLARE_PARSER(f) APREQ_DECLARE_NONSTD(apr_status_t) \ + (f) (APREQ_PARSER_ARGS) + +/** + * Declares an API hook. + */ +#define APREQ_DECLARE_HOOK(f) APREQ_DECLARE_NONSTD(apr_status_t) \ + (f) (APREQ_HOOK_ARGS) + +/** + * A hook is called by the parser whenever data arrives in a file + * upload parameter of the request body. You may associate any number + * of hooks with a parser instance with apreq_parser_add_hook(). + */ +struct apreq_hook_t { + apreq_hook_function_t hook; /**< the hook function */ + apreq_hook_t *next; /**< next item in the linked list */ + apr_pool_t *pool; /**< pool which allocated this hook */ + void *ctx; /**< a user defined pointer passed to the hook function */ +}; + +/** + * A request body parser instance. + */ +struct apreq_parser_t { + /** the function which parses chunks of body data */ + apreq_parser_function_t parser; + /** the Content-Type request header */ + const char *content_type; + /** a pool which outlasts the bucket_alloc. */ + apr_pool_t *pool; + /** bucket allocator used to create bucket brigades */ + apr_bucket_alloc_t *bucket_alloc; + /** the maximum in-memory bytes a brigade may use */ + apr_size_t brigade_limit; + /** the directory for generating temporary files */ + const char *temp_dir; + /** linked list of hooks */ + apreq_hook_t *hook; + /** internal context pointer used by the parser function */ + void *ctx; +}; + + +/** + * Parse the incoming brigade into a table. Parsers normally + * consume all the buckets of the brigade during parsing. However + * parsers may leave "rejected" data in the brigade, even during a + * successful parse, so callers may need to clean up the brigade + * themselves (in particular, rejected buckets should not be + * passed back to the parser again). + * @remark bb == NULL is valid: the parser should return its + * public status: APR_INCOMPLETE, APR_SUCCESS, or an error code. + */ +static APR_INLINE +apr_status_t apreq_parser_run(struct apreq_parser_t *psr, apr_table_t *t, + apr_bucket_brigade *bb) +{ + return psr->parser(psr, t, bb); +} + +/** + * Run the hook with the current parameter and the incoming + * bucket brigade. The hook may modify the brigade if necessary. + * Once all hooks have completed, the contents of the brigade will + * be added to the parameter's bb attribute. + * @return APR_SUCCESS on success. All other values represent errors. + */ +static APR_INLINE +apr_status_t apreq_hook_run(struct apreq_hook_t *h, apreq_param_t *param, + apr_bucket_brigade *bb) +{ + return h->hook(h, param, bb); +} + + +/** + * RFC 822 Header parser. It will reject all data + * after the first CRLF CRLF sequence (an empty line). + * See apreq_parser_run() for more info on rejected data. + */ +APREQ_DECLARE_PARSER(apreq_parse_headers); + +/** + * RFC 2396 application/x-www-form-urlencoded parser. + */ +APREQ_DECLARE_PARSER(apreq_parse_urlencoded); + +/** + * RFC 2388 multipart/form-data (and XForms 1.0 multipart/related) + * parser. It will reject any buckets representing preamble and + * postamble text (this is normal behavior, not an error condition). + * See apreq_parser_run() for more info on rejected data. + */ +APREQ_DECLARE_PARSER(apreq_parse_multipart); + +/** + * Generic parser. No table entries will be added to + * the req->body table by this parser. The parser creates + * a dummy apreq_param_t to pass to any configured hooks. If + * no hooks are configured, the dummy param's bb slot will + * contain a copy of the request body. It can be retrieved + * by casting the parser's ctx pointer to (apreq_param_t **). + */ +APREQ_DECLARE_PARSER(apreq_parse_generic); + +/** + * apr_xml_parser hook. It will parse until EOS appears. + * The parsed document isn't available until parsing has + * completed successfully. The hook's ctx pointer may + * be cast as (apr_xml_doc **) to retrieve the + * parsed document. + */ +APREQ_DECLARE_HOOK(apreq_hook_apr_xml_parser); + +/** + * Construct a parser. + * + * @param pool Pool used to allocate the parser. + * @param ba bucket allocator used to create bucket brigades + * @param content_type Content-type that this parser can deal with. + * @param pfn The parser function. + * @param brigade_limit the maximum in-memory bytes a brigade may use + * @param temp_dir the directory used by the parser for temporary files + * @param hook Hooks to associate this parser with. + * @param ctx Parser's internal scratch pad. + * @return New parser. + */ +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); + +/** + * Construct a hook. + * + * @param pool used to allocate the hook. + * @param hook The hook function. + * @param next List of other hooks for this hook to call on. + * @param ctx Hook's internal scratch pad. + * @return New hook. + */ +APREQ_DECLARE(apreq_hook_t *) apreq_hook_make(apr_pool_t *pool, + apreq_hook_function_t hook, + apreq_hook_t *next, + void *ctx); + + +/** + * Add a new hook to the end of the parser's hook list. + * + * @param p Parser. + * @param h Hook to append. + */ +APREQ_DECLARE(apr_status_t) apreq_parser_add_hook(apreq_parser_t *p, + apreq_hook_t *h); + + +/** + * Fetch the default parser function associated with the given MIME type. + * @param enctype The desired enctype (can also be a full "Content-Type" + * header). + * @return The parser function, or NULL if the enctype is unrecognized. + */ +APREQ_DECLARE(apreq_parser_function_t)apreq_parser(const char *enctype); + + +/** + * Register a new parsing function with a MIME enctype. + * Registered parsers are added to apreq_parser()'s + * internal lookup table. + * + * @param enctype The MIME type. + * @param pfn The function to use during parsing. Setting + * parser == NULL will remove an existing parser. + * + * @return APR_SUCCESS or error. + */ + +APREQ_DECLARE(apr_status_t) apreq_register_parser(const char *enctype, + apreq_parser_function_t pfn); + + +/** + * Returns APREQ_ERROR_GENERAL. Effectively disables mfd parser + * if a file-upload field is present. + * + */ +APREQ_DECLARE_HOOK(apreq_hook_disable_uploads); + +/** + * Calls apr_brigade_cleanup on the incoming brigade + * after passing the brigade to any subsequent hooks. + */ +APREQ_DECLARE_HOOK(apreq_hook_discard_brigade); + +/** + * Special purpose utility for locating a parameter + * during parsing. The hook's ctx shoud be initialized + * to a const char *, which is a pointer to the desired + * param name. The hook's ctx will be reassigned to the + * first param found. + * + * @remarks When used, this should always be the first hook + * invoked, so add it manually as parser->hook instead of + * using apreq_parser_add_hook. + */ +APREQ_DECLARE_HOOK(apreq_hook_find_param); + + +#ifdef __cplusplus +} + +#endif +#endif /* APREQ_PARSERS_H */ diff --git a/srclib/libapreq/include/apreq_util.h b/srclib/libapreq/include/apreq_util.h new file mode 100644 index 0000000000..feb2d396ce --- /dev/null +++ b/srclib/libapreq/include/apreq_util.h @@ -0,0 +1,443 @@ +/* +** 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. +*/ + +#ifndef APREQ_UTIL_H +#define APREQ_UTIL_H + +#include "apr_file_io.h" +#include "apr_buckets.h" +#include "apreq.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * This header contains useful functions for creating new + * parsers, hooks or modules. It includes + * + * - string <-> array converters + * - substring search functions + * - simple encoders & decoders for urlencoded strings + * - simple time, date, & file-size converters + * @file apreq_util.h + * @brief Utility functions for apreq. + * @ingroup libapreq2 + */ + +/** + * Join an array of values. The result is an empty string if there are + * no values. + * + * @param p Pool to allocate return value. + * @param sep String that is inserted between the joined values. + * @param arr Array of apreq_value_t entries. + * @param mode Join type- see apreq_join_t. + * + * @return Joined string, or NULL on error + */ +APREQ_DECLARE(char *) apreq_join(apr_pool_t *p, + const char *sep, + const apr_array_header_t *arr, + apreq_join_t mode); + +/** + * Returns offset of match string's location, or -1 if no match is found. + * + * @param hay Location of bytes to scan. + * @param hlen Number of bytes available for scanning. + * @param ndl Search string + * @param nlen Length of search string. + * @param type Match type. + * + * @return Offset of match string, or -1 if no match is found. + * + */ +APREQ_DECLARE(apr_ssize_t) apreq_index(const char* hay, apr_size_t hlen, + const char* ndl, apr_size_t nlen, + const apreq_match_t type); + +/** + * Places a quoted copy of src into dest. Embedded quotes are escaped with a + * backslash ('\'). + * + * @param dest Location of quoted copy. Must be large enough to hold the copy + * and trailing null byte. + * @param src Original string. + * @param slen Length of original string. + * @param dest Destination string. + * + * @return length of quoted copy in dest. + */ +APREQ_DECLARE(apr_size_t) apreq_quote(char *dest, const char *src, + const apr_size_t slen); + +/** + * + * Same as apreq_quote() except when src begins and ends in quote marks. In + * that case it assumes src is quoted correctly, and just copies src to dest. + * + * @param dest Location of quoted copy. Must be large enough to hold the copy + * and trailing null byte. + * @param src Original string. + * @param slen Length of original string. + * @param dest Destination string. + * + * @return length of quoted copy in dest. + */ +APREQ_DECLARE(apr_size_t) apreq_quote_once(char *dest, const char *src, + const apr_size_t slen); + +/** + * Url-encodes a string. + * + * @param dest Location of url-encoded result string. Caller must ensure it + * is large enough to hold the encoded string and trailing '\\0'. + * @param src Original string. + * @param slen Length of original string. + * + * @return length of url-encoded string in dest; does not exceed 3 * slen. + */ +APREQ_DECLARE(apr_size_t) apreq_encode(char *dest, const char *src, + const apr_size_t slen); + +/** + * Convert a string from cp1252 to utf8. Caller must ensure it is large enough + * to hold the encoded string and trailing '\\0'. + * + * @param dest Location of utf8-encoded result string. Caller must ensure it + * is large enough to hold the encoded string and trailing '\\0'. + * @param src Original string. + * @param slen Length of original string. + * + * @return length of utf8-encoded string in dest; does not exceed 3 * slen. + */ +APREQ_DECLARE(apr_size_t) apreq_cp1252_to_utf8(char *dest, + const char *src, apr_size_t slen); + +/** + * Heuristically determine the charset of a string. + * + * @param src String to scan. + * @param slen Length of string. + * + * @return APREQ_CHARSET_ASCII if the string contains only 7-bit chars; + * @return APREQ_CHARSET_UTF8 if the string is a valid utf8 byte sequence; + * @return APREQ_CHARSET_LATIN1 if the string has no control chars; + * @return APREQ_CHARSET_CP1252 if the string has control chars. + */ +APREQ_DECLARE(apreq_charset_t) apreq_charset_divine(const char *src, + apr_size_t slen); + +/** + * Url-decodes a string. + * + * @param dest Location of url-encoded result string. Caller must ensure dest is + * large enough to hold the encoded string and trailing null character. + * @param dlen points to resultant length of url-decoded string in dest + * @param src Original string. + * @param slen Length of original string. + * + * @return APR_SUCCESS. + * @return APR_INCOMPLETE if the string + * ends in the middle of an escape sequence. + * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input. + * + * @remarks In the non-success case, dlen will be set to include + * the last succesfully decoded value. This function decodes + * \%uXXXX into a utf8 (wide) character, following ECMA-262 + * (the Javascript spec) Section B.2.1. + */ + +APREQ_DECLARE(apr_status_t) apreq_decode(char *dest, apr_size_t *dlen, + const char *src, apr_size_t slen); + +/** + * Url-decodes an iovec array. + * + * @param dest Location of url-encoded result string. Caller must ensure dest is + * large enough to hold the encoded string and trailing null character. + * @param dlen Resultant length of dest. + * @param v Array of iovecs that represent the source string + * @param nelts Number of iovecs in the array. + * + * @return APR_SUCCESS. + * @return APR_INCOMPLETE if the iovec + * ends in the middle of an escape sequence. + * @return ::APREQ_ERROR_BADSEQ or ::APREQ_ERROR_BADCHAR on malformed input. + * + * @remarks In the non-APR_SUCCESS case, dlen will be set to include + * the last succesfully decoded value. This function decodes + * \%uXXXX into a utf8 (wide) character, following ECMA-262 + * (the Javascript spec) Section B.2.1. + */ + +APREQ_DECLARE(apr_status_t) apreq_decodev(char *dest, apr_size_t *dlen, + struct iovec *v, int nelts); + +/** + * Returns an url-encoded copy of a string. + * + * @param p Pool used to allocate the return value. + * @param src Original string. + * @param slen Length of original string. + * + * @return The url-encoded string. + * + * @remarks Use this function insead of apreq_encode if its + * caller might otherwise overflow dest. + */ +static APR_INLINE +char *apreq_escape(apr_pool_t *p, const char *src, const apr_size_t slen) +{ + char *rv; + + if (src == NULL) + return NULL; + + rv = (char *)apr_palloc(p, 3 * slen + 1); + apreq_encode(rv, src, slen); + return rv; +} + +/** + * An \e in-situ url-decoder. + * + * @param str The string to decode + * + * @return Length of decoded string, or < 0 on error. + */ +static APR_INLINE apr_ssize_t apreq_unescape(char *str) +{ + apr_size_t len; + apr_status_t rv = apreq_decode(str, &len, str, strlen(str)); + if (rv == APR_SUCCESS) + return (apr_ssize_t)len; + else + return -1; +} + +/** + * Converts file sizes (KMG) to bytes + * + * @param s file size matching m/^\\d+[KMG]b?$/i + * + * @return 64-bit integer representation of s. + * + * @todo What happens when s is malformed? Should this return + * an unsigned value instead? + */ + +APREQ_DECLARE(apr_int64_t) apreq_atoi64f(const char *s); + +/** + * Converts time strings (YMDhms) to seconds + * + * @param s time string matching m/^\\+?\\d+[YMDhms]$/ + * + * @return 64-bit integer representation of s as seconds. + * + * @todo What happens when s is malformed? Should this return + * an unsigned value instead? + */ + +APREQ_DECLARE(apr_int64_t) apreq_atoi64t(const char *s); + +/** + * Writes brigade to a file. + * + * @param f File that gets the brigade. + * @param wlen On a successful return, wlen holds the length of + * the brigade, which is the amount of data written to + * the file. + * @param bb Bucket brigade. + * + * @return APR_SUCCESS. + * @return Error status code from either an unsuccessful apr_bucket_read(), + * or a failed apr_file_writev(). + * + * @remarks This function leaks a bucket brigade into bb->p whenever + * the final bucket in bb is a spool bucket. + */ + +APREQ_DECLARE(apr_status_t) apreq_brigade_fwrite(apr_file_t *f, + apr_off_t *wlen, + apr_bucket_brigade *bb); +/** + * Makes a temporary file. + * + * @param fp Points to the temporary apr_file_t on success. + * @param pool Pool to associate with the temp file. When the + * pool is destroyed, the temp file will be closed + * and deleted. + * @param path The base directory which will contain the temp file. + * If param == NULL, the directory will be selected via + * tempnam(). See the tempnam manpage for details. + * + * @return APR_SUCCESS. + * @return Error status code from unsuccessful apr_filepath_merge(), + * or a failed apr_file_mktemp(). + */ + +APREQ_DECLARE(apr_status_t) apreq_file_mktemp(apr_file_t **fp, + apr_pool_t *pool, + const char *path); + +/** + * Set aside all buckets in the brigade. + * + * @param bb Brigade. + * @param p Setaside buckets into this pool. + * @return APR_SUCCESS. + * @return Error status code from an unsuccessful apr_bucket_setaside(). + */ + +static APR_INLINE +apr_status_t apreq_brigade_setaside(apr_bucket_brigade *bb, apr_pool_t *p) +{ + apr_bucket *e; + for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + apr_status_t rv = apr_bucket_setaside(e, p); + if (rv != APR_SUCCESS) + return rv; + } + return APR_SUCCESS; +} + + +/** + * Copy a brigade. + * + * @param d (destination) Copied buckets are appended to this brigade. + * @param s (source) Brigade to copy from. + * + * @return APR_SUCCESS. + * @return Error status code from an unsuccessful apr_bucket_copy(). + * + * @remarks s == d produces Undefined Behavior. + */ + +static APR_INLINE +apr_status_t apreq_brigade_copy(apr_bucket_brigade *d, apr_bucket_brigade *s) { + apr_bucket *e; + for (e = APR_BRIGADE_FIRST(s); e != APR_BRIGADE_SENTINEL(s); + e = APR_BUCKET_NEXT(e)) + { + apr_bucket *c; + apr_status_t rv = apr_bucket_copy(e, &c); + if (rv != APR_SUCCESS) + return rv; + + APR_BRIGADE_INSERT_TAIL(d, c); + } + return APR_SUCCESS; +} + +/** + * Move the front of a brigade. + * + * @param d (destination) Append buckets to this brigade. + * @param s (source) Brigade to take buckets from. + * @param e First bucket of s after the move. All buckets + * before e are appended to d. + * + * @remarks This moves all buckets when e == APR_BRIGADE_SENTINEL(s). + */ + +static APR_INLINE +void apreq_brigade_move(apr_bucket_brigade *d, apr_bucket_brigade *s, + apr_bucket *e) +{ + apr_bucket *f; + + if (e != APR_BRIGADE_SENTINEL(s)) { + f = APR_RING_FIRST(&s->list); + if (f == e) /* zero buckets to be moved */ + return; + + /* obtain the last bucket to be moved */ + e = APR_RING_PREV(e, link); + + APR_RING_UNSPLICE(f, e, link); + APR_RING_SPLICE_HEAD(&d->list, f, e, apr_bucket, link); + } + else { + APR_BRIGADE_CONCAT(d, s); + } +} + + +/** + * Search a header string for the value of a particular named attribute. + * + * @param hdr Header string to scan. + * @param name Name of attribute to search for. + * @param nlen Length of name. + * @param val Location of (first) matching value. + * @param vlen Length of matching value. + * + * @return APR_SUCCESS. + * @return ::APREQ_ERROR_NOATTR if the attribute is not found. + * @return ::APREQ_ERROR_BADSEQ if an unpaired quote mark was detected. + */ +APREQ_DECLARE(apr_status_t) apreq_header_attribute(const char *hdr, + const char *name, + const apr_size_t nlen, + const char **val, + apr_size_t *vlen); + + +/** + * Concatenates the brigades, spooling large brigades into + * a tempfile (APREQ_SPOOL) bucket. + * + * @param pool Pool for creating a tempfile bucket. + * @param temp_dir Directory for tempfile creation. + * @param brigade_limit If out's length would exceed this value, + * the appended buckets get written to a tempfile. + * @param out Resulting brigade. + * @param in Brigade to append. + * + * @return APR_SUCCESS. + * @return Error status code resulting from either apr_brigade_length(), + * apreq_file_mktemp(), apreq_brigade_fwrite(), or apr_file_seek(). + * + * @todo Flesh out these error codes, making them as explicit as possible. + */ +APREQ_DECLARE(apr_status_t) apreq_brigade_concat(apr_pool_t *pool, + const char *temp_dir, + apr_size_t brigade_limit, + apr_bucket_brigade *out, + apr_bucket_brigade *in); + +/** + * Determines the spool file used by the brigade. Returns NULL if the + * brigade is not spooled in a file (does not use an APREQ_SPOOL + * bucket). + * + * @param bb the bucket brigade + * @return the spool file, or NULL. + */ +APREQ_DECLARE(apr_file_t *)apreq_brigade_spoolfile(apr_bucket_brigade *bb); + +#ifdef __cplusplus + } +#endif + +#endif /* APREQ_UTIL_H */ diff --git a/srclib/libapreq/include/apreq_version.h b/srclib/libapreq/include/apreq_version.h new file mode 100644 index 0000000000..056dda7f47 --- /dev/null +++ b/srclib/libapreq/include/apreq_version.h @@ -0,0 +1,105 @@ +/* +** 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. +*/ + +#ifndef APREQ_VERSION_H +#define APREQ_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "apr_version.h" +#include "apreq.h" + +/** + * @file apreq_version.h + * @brief Versioning API for libapreq + * @ingroup libapreq2 + * + * There are several different mechanisms for accessing the version. There + * is a string form, and a set of numbers; in addition, there are constants + * which can be compiled into your application, and you can query the library + * being used for its actual version. + * + * Note that it is possible for an application to detect that it has been + * compiled against a different version of libapreq by use of the compile-time + * constants and the use of the run-time query function. + * + * libapreq version numbering follows the guidelines specified in: + * + * http://apr.apache.org/versioning.html + */ + +/* The numeric compile-time version constants. These constants are the + * authoritative version numbers for libapreq. + */ + +/** major version + * Major API changes that could cause compatibility problems for older + * programs such as structure size changes. No binary compatibility is + * possible across a change in the major version. + */ +#define APREQ_MAJOR_VERSION 2 + +/** + * Minor API changes that do not cause binary compatibility problems. + * Should be reset to 0 when upgrading APREQ_MAJOR_VERSION + */ +#define APREQ_MINOR_VERSION 6 + +/** patch level */ +#define APREQ_PATCH_VERSION 3 + +/** + * This symbol is defined for internal, "development" copies of libapreq. + * This symbol will be \#undef'd for releases. + */ +#define APREQ_IS_DEV_VERSION + + +/** The formatted string of libapreq's version */ +#define APREQ_VERSION_STRING \ + APR_STRINGIFY(APREQ_MAJOR_VERSION) "." \ + APR_STRINGIFY(APREQ_MINOR_VERSION) "." \ + APR_STRINGIFY(APREQ_PATCH_VERSION) \ + APREQ_IS_DEV_STRING + +/** + * Return libapreq's version information information in a numeric form. + * + * @param pvsn Pointer to a version structure for returning the version + * information. + */ +APREQ_DECLARE(void) apreq_version(apr_version_t *pvsn); + +/** Return libapreq's version information as a string. */ +APREQ_DECLARE(const char *) apreq_version_string(void); + + +/** Internal: string form of the "is dev" flag */ +#ifdef APREQ_IS_DEV_VERSION +#define APREQ_IS_DEV_STRING "-dev" +#else +#define APREQ_IS_DEV_STRING "" +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* APREQ_VERSION_H */ diff --git a/srclib/libapreq/library/cookie.c b/srclib/libapreq/library/cookie.c new file mode 100644 index 0000000000..4ee451718a --- /dev/null +++ b/srclib/libapreq/library/cookie.c @@ -0,0 +1,662 @@ +/* +** 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 "apr_strings.h" +#include "apreq_cookie.h" +#include "apreq_error.h" +#include "apreq_module.h" +#include "apreq_util.h" +#include "at.h" + +static const char nscookies[] = "a=1; foo=bar; fl=left; fr=right;bad; " + "ns=foo=1&bar=2,frl=right-left; " + "flr=left-right; fll=left-left; " + "good_one=1;=;bad"; + +static const char rfccookies[] = "$Version=1; first=a;$domain=quux;second=be," + "$Version=1;third=cie"; + +static apr_table_t *jar, *jar2; +static apr_pool_t *p; + +static void jar_make(dAT) +{ + jar = apr_table_make(p, APREQ_DEFAULT_NELTS); + AT_not_null(jar); + AT_int_eq(apreq_parse_cookie_header(p, jar, nscookies), APREQ_ERROR_NOTOKEN); + jar2 = apr_table_make(p, APREQ_DEFAULT_NELTS); + AT_not_null(jar2); + AT_int_eq(apreq_parse_cookie_header(p, jar2, rfccookies), APR_SUCCESS); +} + +static void jar_get_rfc(dAT) +{ + const char *val; + AT_not_null(val = apr_table_get(jar2, "first")); + AT_str_eq(val, "a"); + AT_not_null(val = apr_table_get(jar2, "second")); + AT_str_eq(val, "be"); + AT_not_null(val = apr_table_get(jar2, "third")); + AT_str_eq(val, "cie"); +} + +static void jar_get_ns(dAT) +{ + + AT_str_eq(apr_table_get(jar, "a"), "1"); + + /* ignore wacky cookies that don't have an '=' sign */ + AT_is_null(apr_table_get(jar, "bad")); + + /* accept wacky cookies that contain multiple '=' */ + AT_str_eq(apr_table_get(jar, "ns"), "foo=1&bar=2"); + + AT_str_eq(apr_table_get(jar,"foo"), "bar"); + AT_str_eq(apr_table_get(jar,"fl"), "left"); + AT_str_eq(apr_table_get(jar,"fr"), "right"); + AT_str_eq(apr_table_get(jar,"frl"), "right-left"); + AT_str_eq(apr_table_get(jar,"flr"), "left-right"); + AT_str_eq(apr_table_get(jar,"fll"), "left-left"); + AT_is_null(apr_table_get(jar,"")); +} + + +static void netscape_cookie(dAT) +{ + char expires[APR_RFC822_DATE_LEN]; + char *val; + apreq_cookie_t *c; + + *(const char **)&val = apr_table_get(jar, "foo"); + AT_not_null(val); + + c = apreq_value_to_cookie(val); + + AT_str_eq(c->v.data, "bar"); + AT_int_eq(apreq_cookie_version(c), 0); + AT_str_eq(apreq_cookie_as_string(c, p), "foo=bar"); + + c->domain = apr_pstrdup(p, "example.com"); + AT_str_eq(apreq_cookie_as_string(c, p), "foo=bar; domain=example.com"); + + c->path = apr_pstrdup(p, "/quux"); + AT_str_eq(apreq_cookie_as_string(c, p), + "foo=bar; path=/quux; domain=example.com"); + + apreq_cookie_expires(c, "+1y"); + apr_rfc822_date(expires, apr_time_now() + + apr_time_from_sec(apreq_atoi64t("+1y"))); + expires[7] = '-'; + expires[11] = '-'; + val = apr_pstrcat(p, "foo=bar; path=/quux; domain=example.com; expires=", + expires, NULL); + + AT_str_eq(apreq_cookie_as_string(c, p), val); +} + + +static void rfc_cookie(dAT) +{ + apreq_cookie_t *c = apreq_cookie_make(p,"rfc",3,"out",3); + const char *expected; + long expires; + + AT_str_eq(c->v.data, "out"); + + apreq_cookie_version_set(c, 1); + AT_int_eq(apreq_cookie_version(c), 1); + AT_str_eq(apreq_cookie_as_string(c,p),"rfc=out; Version=1"); + + c->domain = apr_pstrdup(p, "example.com"); + +#ifndef WIN32 + + AT_str_eq(apreq_cookie_as_string(c,p), + "rfc=out; Version=1; domain=\"example.com\""); + c->path = apr_pstrdup(p, "/quux"); + AT_str_eq(apreq_cookie_as_string(c,p), + "rfc=out; Version=1; path=\"/quux\"; domain=\"example.com\""); + + apreq_cookie_expires(c, "+3m"); + expires = apreq_atoi64t("+3m"); + expected = apr_psprintf(p, "rfc=out; Version=1; path=\"/quux\"; " + "domain=\"example.com\"; max-age=%ld", + expires); + AT_str_eq(apreq_cookie_as_string(c,p), expected); + +#else + + expected = "rfc=out; Version=1; domain=\"example.com\""; + AT_str_eq(apreq_cookie_as_string(c,p), expected); + + c->path = apr_pstrdup(p, "/quux"); + expected = "rfc=out; Version=1; path=\"/quux\"; domain=\"example.com\""; + AT_str_eq(apreq_cookie_as_string(c,p), expected); + + apreq_cookie_expires(c, "+3m"); + expires = apreq_atoi64t("+3m"); + expected = apr_psprintf(p, "rfc=out; Version=1; path=\"/quux\"; " + "domain=\"example.com\"; max-age=%ld", + expires); + AT_str_eq(apreq_cookie_as_string(c,p), expected); + +#endif + +} + + +#define dT(func, plan) #func, func, plan + + +int main(int argc, char *argv[]) +{ + unsigned i, plan = 0; + dAT; + at_test_t test_list [] = { + { dT(jar_make, 4) }, + { dT(jar_get_rfc, 6), "1 3 5" }, + { dT(jar_get_ns, 10) }, + { dT(netscape_cookie, 7) }, + { dT(rfc_cookie, 6) }, + }; + + apr_initialize(); + atexit(apr_terminate); + + apr_pool_create(&p, NULL); + + AT = at_create(p, 0, at_report_stdout_make(p)); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + plan += test_list[i].plan; + + AT_begin(plan); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + AT_run(&test_list[i]); + + AT_end(); + + return 0; +} +/* +** 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_cookie.h" +#include "apreq_error.h" +#include "apreq_util.h" +#include "apr_strings.h" +#include "apr_lib.h" +#include "apr_date.h" + + +#define RFC 1 +#define NETSCAPE 0 + +#define ADD_COOKIE(j,c) apreq_value_table_add(&c->v, j) + +APREQ_DECLARE(void) apreq_cookie_expires(apreq_cookie_t *c, + const char *time_str) +{ + if (time_str == NULL) { + c->max_age = -1; + return; + } + + if (!strcasecmp(time_str, "now")) + c->max_age = 0; + else { + c->max_age = apr_date_parse_rfc(time_str); + if (c->max_age == APR_DATE_BAD) + c->max_age = apr_time_from_sec(apreq_atoi64t(time_str)); + else + c->max_age -= apr_time_now(); + } +} + +static apr_status_t apreq_cookie_attr(apr_pool_t *p, + apreq_cookie_t *c, + const char *attr, + apr_size_t alen, + const char *val, + apr_size_t vlen) +{ + if (alen < 2) + return APR_EBADARG; + + if ( attr[0] == '-' || attr[0] == '$' ) { + ++attr; + --alen; + } + + switch (apr_tolower(*attr)) { + + case 'n': /* name is not an attr */ + return APR_ENOTIMPL; + + case 'v': /* version; value is not an attr */ + if (alen == 5 && strncasecmp(attr,"value", 5) == 0) + return APR_ENOTIMPL; + + while (!apr_isdigit(*val)) { + if (vlen == 0) + return APREQ_ERROR_BADSEQ; + ++val; + --vlen; + } + apreq_cookie_version_set(c, *val - '0'); + return APR_SUCCESS; + + case 'e': case 'm': /* expires, max-age */ + apreq_cookie_expires(c, val); + return APR_SUCCESS; + + case 'd': + c->domain = apr_pstrmemdup(p,val,vlen); + return APR_SUCCESS; + + case 'p': + if (alen != 4) + break; + if (!strncasecmp("port", attr, 4)) { + c->port = apr_pstrmemdup(p,val,vlen); + return APR_SUCCESS; + } + else if (!strncasecmp("path", attr, 4)) { + c->path = apr_pstrmemdup(p,val,vlen); + return APR_SUCCESS; + } + break; + + case 'c': + if (!strncasecmp("commentURL", attr, 10)) { + c->commentURL = apr_pstrmemdup(p,val,vlen); + return APR_SUCCESS; + } + else if (!strncasecmp("comment", attr, 7)) { + c->comment = apr_pstrmemdup(p,val,vlen); + return APR_SUCCESS; + } + break; + + case 's': + if (vlen > 0 && *val != '0' && strncasecmp("off",val,vlen)) + apreq_cookie_secure_on(c); + else + apreq_cookie_secure_off(c); + return APR_SUCCESS; + + }; + + return APR_ENOTIMPL; +} + +APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(apr_pool_t *p, + const char *name, + const apr_size_t nlen, + const char *value, + const apr_size_t vlen) +{ + apreq_cookie_t *c; + apreq_value_t *v; + + c = apr_palloc(p, nlen + vlen + 1 + sizeof *c); + + if (c == NULL) + return NULL; + + *(const apreq_value_t **)&v = &c->v; + + if (vlen > 0 && value != NULL) + memcpy(v->data, value, vlen); + v->data[vlen] = 0; + v->dlen = vlen; + v->name = v->data + vlen + 1; + if (nlen && name != NULL) + memcpy(v->name, name, nlen); + v->name[nlen] = 0; + v->nlen = nlen; + + c->path = NULL; + c->domain = NULL; + c->port = NULL; + c->comment = NULL; + c->commentURL = NULL; + c->max_age = -1; /* session cookie is the default */ + c->flags = 0; + + + return c; +} + +static APR_INLINE +apr_status_t get_pair(apr_pool_t *p, const char **data, + const char **n, apr_size_t *nlen, + const char **v, apr_size_t *vlen, unsigned unquote) +{ + const char *hdr, *key, *val; + + hdr = *data; + + while (apr_isspace(*hdr) || *hdr == '=') + ++hdr; + + key = strchr(hdr, '='); + + if (key == NULL) + return APREQ_ERROR_NOTOKEN; + + val = key + 1; + + do --key; + while (key > hdr && apr_isspace(*key)); + + *n = key; + + while (key >= hdr && !apr_isspace(*key)) + --key; + + *nlen = *n - key; + *n = key + 1; + + while (apr_isspace(*val)) + ++val; + + if (*val == '"') { + unsigned saw_backslash = 0; + for (*v = (unquote) ? ++val : val++; *val; ++val) { + switch (*val) { + case '"': + *data = val + 1; + + if (!unquote) { + *vlen = (val - *v) + 1; + } + else if (!saw_backslash) { + *vlen = val - *v; + } + else { + char *dest = apr_palloc(p, val - *v), *d = dest; + const char *s = *v; + while (s < val) { + if (*s == '\\') + ++s; + *d++ = *s++; + } + + *vlen = d - dest; + *v = dest; + } + + return APR_SUCCESS; + case '\\': + saw_backslash = 1; + if (val[1] != 0) + ++val; + default: + break; + } + } + /* bad sequence: no terminating quote found */ + return APREQ_ERROR_BADSEQ; + } + else { + /* value is not wrapped in quotes */ + for (*v = val; *val; ++val) { + switch (*val) { + case ';': + case ',': + case ' ': + case '\t': + case '\r': + case '\n': + *data = val; + *vlen = val - *v; + return APR_SUCCESS; + default: + break; + } + } + } + + *data = val; + *vlen = val - *v; + + return APR_SUCCESS; +} + + + +APREQ_DECLARE(apr_status_t)apreq_parse_cookie_header(apr_pool_t *p, + apr_table_t *j, + const char *hdr) +{ + apreq_cookie_t *c; + unsigned version; + + parse_cookie_header: + + c = NULL; + version = NETSCAPE; + + while (apr_isspace(*hdr)) + ++hdr; + + + if (*hdr == '$') { + /* XXX cheat: assume "$..." => "$Version" => RFC Cookie header */ + version = RFC; + skip_version_string: + switch (*hdr++) { + case 0: + return APR_SUCCESS; + case ',': + goto parse_cookie_header; + case ';': + break; + default: + goto skip_version_string; + } + } + + for (;;) { + apr_status_t status; + const char *name, *value; + apr_size_t nlen, vlen; + + while (*hdr == ';' || apr_isspace(*hdr)) + ++hdr; + + switch (*hdr) { + + case 0: + /* this is the normal exit point */ + if (c != NULL) { + ADD_COOKIE(j, c); + } + return APR_SUCCESS; + + case ',': + ++hdr; + if (c != NULL) { + ADD_COOKIE(j, c); + } + goto parse_cookie_header; + + case '$': + if (c == NULL) { + return APREQ_ERROR_BADCHAR; + } + else if (version == NETSCAPE) { + return APREQ_ERROR_MISMATCH; + } + + ++hdr; + status = get_pair(p, &hdr, &name, &nlen, &value, &vlen, 1); + if (status != APR_SUCCESS) + return status; + + status = apreq_cookie_attr(p, c, name, nlen, value, vlen); + + switch (status) { + case APR_ENOTIMPL: + /* XXX: skip unrecognized attr? Not really correct, + but for now, just fall through */ + + case APR_SUCCESS: + break; + default: + return status; + } + + break; + + default: + if (c != NULL) { + ADD_COOKIE(j, c); + } + + status = get_pair(p, &hdr, &name, &nlen, &value, &vlen, 0); + + if (status != APR_SUCCESS) + return status; + + c = apreq_cookie_make(p, name, nlen, value, vlen); + apreq_cookie_tainted_on(c); + if (version != NETSCAPE) + apreq_cookie_version_set(c, version); + } + } + + /* NOT REACHED */ + return APREQ_ERROR_GENERAL; +} + + +APREQ_DECLARE(int) apreq_cookie_serialize(const apreq_cookie_t *c, + char *buf, apr_size_t len) +{ + /* The format string must be large enough to accomodate all + * of the cookie attributes. The current attributes sum to + * ~90 characters (w/ 6-8 padding chars per attr), so anything + * over 100 should be fine. + */ + + unsigned version = apreq_cookie_version(c); + char format[128] = "%s=%s"; + char *f = format + strlen(format); + + /* XXX protocol enforcement (for debugging, anyway) ??? */ + + if (c->v.name == NULL) + return -1; + +#define NULL2EMPTY(attr) (attr ? attr : "") + + + if (version == NETSCAPE) { + char expires[APR_RFC822_DATE_LEN] = {0}; + +#define ADD_NS_ATTR(name) do { \ + if (c->name != NULL) \ + strcpy(f, "; " #name "=%s"); \ + else \ + strcpy(f, "%0.s"); \ + f += strlen(f); \ +} while (0) + + ADD_NS_ATTR(path); + ADD_NS_ATTR(domain); + + if (c->max_age != -1) { + strcpy(f, "; expires=%s"); + apr_rfc822_date(expires, c->max_age + apr_time_now()); + expires[7] = '-'; + expires[11] = '-'; + } + else + strcpy(f, ""); + + f += strlen(f); + + if (apreq_cookie_is_secure(c)) + strcpy(f, "; secure"); + + return apr_snprintf(buf, len, format, c->v.name, c->v.data, + NULL2EMPTY(c->path), NULL2EMPTY(c->domain), expires); + } + + /* c->version == RFC */ + + strcpy(f,"; Version=%u"); + f += strlen(f); + +/* ensure RFC attributes are always quoted */ +#define ADD_RFC_ATTR(name) do { \ + if (c->name != NULL) \ + if (*c->name == '"') \ + strcpy(f, "; " #name "=%s"); \ + else \ + strcpy(f, "; " #name "=\"%s\""); \ + else \ + strcpy(f, "%0.s"); \ + f += strlen (f); \ +} while (0) + + ADD_RFC_ATTR(path); + ADD_RFC_ATTR(domain); + ADD_RFC_ATTR(port); + ADD_RFC_ATTR(comment); + ADD_RFC_ATTR(commentURL); + + strcpy(f, c->max_age != -1 ? "; max-age=%" APR_TIME_T_FMT : ""); + + f += strlen(f); + + if (apreq_cookie_is_secure(c)) + strcpy(f, "; secure"); + + return apr_snprintf(buf, len, format, c->v.name, c->v.data, version, + NULL2EMPTY(c->path), NULL2EMPTY(c->domain), + NULL2EMPTY(c->port), NULL2EMPTY(c->comment), + NULL2EMPTY(c->commentURL), apr_time_sec(c->max_age)); +} + + +APREQ_DECLARE(char*) apreq_cookie_as_string(const apreq_cookie_t *c, + apr_pool_t *p) +{ + int n = apreq_cookie_serialize(c, NULL, 0); + char *s = apr_palloc(p, n + 1); + apreq_cookie_serialize(c, s, n + 1); + return s; +} + diff --git a/srclib/libapreq/library/error.c b/srclib/libapreq/library/error.c new file mode 100644 index 0000000000..7086648b35 --- /dev/null +++ b/srclib/libapreq/library/error.c @@ -0,0 +1,198 @@ +/* +** 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 "apr_strings.h" +#include "apreq_error.h" +#include "at.h" + + +static void test_strerror(dAT) +{ + char buf[256], *str; + + str = apreq_strerror(APREQ_ERROR_GENERAL, buf, sizeof buf); + AT_ptr_eq(str, buf); + AT_str_eq(str, "Internal apreq error"); + + str = apreq_strerror(APREQ_ERROR_TAINTED, buf, sizeof buf); + AT_str_eq(str, "Attempt to perform unsafe action with tainted data"); + + str = apreq_strerror(APREQ_ERROR_BADSEQ, buf, sizeof buf); + AT_str_eq(str, "Invalid byte sequence"); + + str = apreq_strerror(APREQ_ERROR_NODATA, buf, sizeof buf); + AT_str_eq(str, "Missing input data"); + + str = apreq_strerror(APREQ_ERROR_GENERAL+99, buf, sizeof buf); + AT_str_eq(str, "Error string not yet specified by apreq"); + + + + + /* Test some common APR status codes also */ + + str = apreq_strerror(APR_EINIT, buf, sizeof buf); + AT_str_eq(str, "There is no error, this value signifies an initialized " + "error code"); + + str = apreq_strerror(APR_INCOMPLETE, buf, sizeof buf); + AT_str_eq(str, "Partial results are valid but processing is incomplete"); + + str = apreq_strerror(APR_EOF, buf, sizeof buf); + AT_str_eq(str, "End of file found"); + + str = apreq_strerror(APR_ENOTIMPL, buf, sizeof buf); + AT_str_eq(str, "This function has not been implemented on this platform"); + + } + +#define dT(func, plan) #func, func, plan + + +int main(int argc, char *argv[]) +{ + unsigned i, plan = 0; + apr_pool_t *p; + dAT; + at_test_t test_list [] = { + { dT(test_strerror, 10), "1" } + }; + + apr_initialize(); + atexit(apr_terminate); + + apr_pool_create(&p, NULL); + + AT = at_create(p, 0, at_report_stdout_make(p)); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + plan += test_list[i].plan; + + AT_begin(plan); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + AT_run(&test_list[i]); + + AT_end(); + + return 0; +} +/* 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 "apr_strings.h" + +/* + * stuffbuffer - like apr_cpystrn() but returns the address of the + * dest buffer instead of the address of the terminating '\0' + */ +static char *stuffbuffer(char *buf, apr_size_t bufsize, const char *s) +{ + apr_cpystrn(buf,s,bufsize); + return buf; +} + +static const char *apreq_error_string(apr_status_t statcode) +{ + switch (statcode) { + + +/* 0's: generic error status codes */ + + case APREQ_ERROR_GENERAL: + return "Internal apreq error"; + + case APREQ_ERROR_TAINTED: + return "Attempt to perform unsafe action with tainted data"; + + +/* 10's: malformed input */ + + case APREQ_ERROR_BADDATA: + return "Malformed input data"; + + case APREQ_ERROR_BADCHAR: + return "Invalid character"; + + case APREQ_ERROR_BADSEQ: + return "Invalid byte sequence"; + + case APREQ_ERROR_BADATTR: + return "Unrecognized attribute"; + + case APREQ_ERROR_BADHEADER: + return "Malformed header string"; + + +/* 20's: missing input */ + + case APREQ_ERROR_NODATA: + return "Missing input data"; + + case APREQ_ERROR_NOTOKEN: + return "Expected token not present"; + + case APREQ_ERROR_NOATTR: + return "Missing attribute"; + + case APREQ_ERROR_NOHEADER: + return "Missing header"; + + case APREQ_ERROR_NOPARSER: + return "Missing parser"; + + +/* 30's: configuration conflicts */ + + case APREQ_ERROR_MISMATCH: + return "Conflicting information"; + + case APREQ_ERROR_OVERLIMIT: + return "Exceeds configured maximum limit"; + + case APREQ_ERROR_NOTEMPTY: + return "Setting already configured"; + + + default: + return "Error string not yet specified by apreq"; + } +} + + +APREQ_DECLARE(char *) apreq_strerror(apr_status_t statcode, char *buf, + apr_size_t bufsize) +{ + if (statcode < APR_OS_START_USERERR || statcode >= APR_OS_START_EAIERR) + return apr_strerror(statcode, buf, bufsize); + return stuffbuffer(buf, bufsize, apreq_error_string(statcode)); +} + diff --git a/srclib/libapreq/library/module.c b/srclib/libapreq/library/module.c new file mode 100644 index 0000000000..9ba5a765ed --- /dev/null +++ b/srclib/libapreq/library/module.c @@ -0,0 +1,65 @@ +/* +** 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_module.h" +#include "apreq_error.h" +#include "apr_strings.h" +#include "apr_lib.h" +#include "apr_file_io.h" + +APREQ_DECLARE(apreq_param_t *)apreq_param(apreq_handle_t *req, const char *key) +{ + apreq_param_t *param = apreq_args_get(req, key); + if (param == NULL) + return apreq_body_get(req, key); + else + return param; +} + +APREQ_DECLARE(apr_table_t *)apreq_params(apreq_handle_t *req, apr_pool_t *p) +{ + const apr_table_t *args, *body; + apreq_args(req, &args); + apreq_body(req, &body); + + if (args != NULL) + if (body != NULL) + return apr_table_overlay(p, args, body); + else + return apr_table_copy(p, args); + else + if (body != NULL) + return apr_table_copy(p, body); + else + return NULL; + +} + +APREQ_DECLARE(apr_table_t *)apreq_cookies(apreq_handle_t *req, apr_pool_t *p) +{ + const apr_table_t *jar; + apreq_jar(req, &jar); + + if (jar != NULL) + return apr_table_copy(p, jar); + else + return NULL; + +} + + +/** @} */ diff --git a/srclib/libapreq/library/module_cgi.c b/srclib/libapreq/library/module_cgi.c new file mode 100644 index 0000000000..ff1abfaaa2 --- /dev/null +++ b/srclib/libapreq/library/module_cgi.c @@ -0,0 +1,689 @@ +/* +** 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 <assert.h> + +#include "apreq_module.h" +#include "apreq_error.h" +#include "apr_strings.h" +#include "apr_lib.h" +#include "apr_env.h" +#include "apreq_util.h" + +#define USER_DATA_KEY "apreq" + +/* Parroting APLOG_* ... */ + +#define CGILOG_EMERG 0 /* system is unusable */ +#define CGILOG_ALERT 1 /* action must be taken immediately */ +#define CGILOG_CRIT 2 /* critical conditions */ +#define CGILOG_ERR 3 /* error conditions */ +#define CGILOG_WARNING 4 /* warning conditions */ +#define CGILOG_NOTICE 5 /* normal but significant condition */ +#define CGILOG_INFO 6 /* informational */ +#define CGILOG_DEBUG 7 /* debug-level messages */ + +#define CGILOG_LEVELMASK 7 +#define CGILOG_MARK __FILE__, __LINE__ + + + + +struct cgi_handle { + struct apreq_handle_t handle; + + apr_table_t *jar, *args, *body; + apr_status_t jar_status, + args_status, + body_status; + + apreq_parser_t *parser; + apreq_hook_t *hook_queue; + apreq_hook_t *find_param; + + const char *temp_dir; + apr_size_t brigade_limit; + apr_uint64_t read_limit; + apr_uint64_t bytes_read; + + apr_bucket_brigade *in; + apr_bucket_brigade *tmpbb; + +}; + +#define CRLF "\015\012" + +typedef struct { + const char *t_name; + int t_val; +} TRANS; + +static const TRANS priorities[] = { + {"emerg", CGILOG_EMERG}, + {"alert", CGILOG_ALERT}, + {"crit", CGILOG_CRIT}, + {"error", CGILOG_ERR}, + {"warn", CGILOG_WARNING}, + {"notice", CGILOG_NOTICE}, + {"info", CGILOG_INFO}, + {"debug", CGILOG_DEBUG}, + {NULL, -1}, +}; + +static const char *cgi_header_in(apreq_handle_t *handle, + const char *name) +{ + apr_pool_t *p = handle->pool; + char *key = apr_pstrcat(p, "HTTP_", name, NULL); + char *k, *value = NULL; + for (k = key; *k; ++k) { + if (*k == '-') + *k = '_'; + else + *k = apr_toupper(*k); + } + + if (!strcmp(key, "HTTP_CONTENT_TYPE") + || !strcmp(key, "HTTP_CONTENT_LENGTH")) + { + key += 5; /* strlen("HTTP_") */ + } + + apr_env_get(&value, key, p); + + return value; +} + + + + +static void cgi_log_error(const char *file, int line, int level, + apr_status_t status, apreq_handle_t *handle, + const char *fmt, ...) +{ + apr_pool_t *p = handle->pool; + char buf[256]; + char *log_level_string, *ra; + const char *remote_addr; + unsigned log_level = CGILOG_WARNING; + char date[APR_CTIME_LEN]; + va_list vp; +#ifndef WIN32 + apr_file_t *err; +#endif + + va_start(vp, fmt); + + if (apr_env_get(&log_level_string, "LOG_LEVEL", p) == APR_SUCCESS) + log_level = (log_level_string[0] - '0'); + + level &= CGILOG_LEVELMASK; + + if (level < (int)log_level) { + + if (apr_env_get(&ra, "REMOTE_ADDR", p) == APR_SUCCESS) + remote_addr = ra; + else + remote_addr = "address unavailable"; + + apr_ctime(date, apr_time_now()); + +#ifndef WIN32 + + apr_file_open_stderr(&err, p); + apr_file_printf(err, "[%s] [%s] [%s] %s(%d): %s: %s\n", + date, priorities[level].t_name, remote_addr, file, line, + apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp)); + apr_file_flush(err); + +#else + fprintf(stderr, "[%s] [%s] [%s] %s(%d): %s: %s\n", + date, priorities[level].t_name, remote_addr, file, line, + apr_strerror(status,buf,255),apr_pvsprintf(p,fmt,vp)); +#endif + } + + va_end(vp); + +} + + +APR_INLINE +static const char *cgi_query_string(apreq_handle_t *handle) +{ + char *value = NULL, qs[] = "QUERY_STRING"; + apr_env_get(&value, qs, handle->pool); + return value; +} + + +static void init_body(apreq_handle_t *handle) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + const char *cl_header = cgi_header_in(handle, "Content-Length"); + apr_bucket_alloc_t *ba = handle->bucket_alloc; + apr_pool_t *pool = handle->pool; + apr_file_t *file; + apr_bucket *eos, *pipe; + + req->body = apr_table_make(pool, APREQ_DEFAULT_NELTS); + + if (cl_header != NULL) { + char *dummy; + apr_int64_t content_length = apr_strtoi64(cl_header, &dummy, 0); + + if (dummy == NULL || *dummy != 0) { + req->body_status = APREQ_ERROR_BADHEADER; + cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle, + "Invalid Content-Length header (%s)", cl_header); + return; + } + else if ((apr_uint64_t)content_length > req->read_limit) { + req->body_status = APREQ_ERROR_OVERLIMIT; + cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle, + "Content-Length header (%s) exceeds configured " + "max_body limit (%" APR_UINT64_T_FMT ")", + cl_header, req->read_limit); + return; + } + } + + if (req->parser == NULL) { + const char *ct_header = cgi_header_in(handle, "Content-Type"); + + if (ct_header != NULL) { + apreq_parser_function_t pf = apreq_parser(ct_header); + + if (pf != NULL) { + req->parser = apreq_parser_make(pool, + ba, + ct_header, + pf, + req->brigade_limit, + req->temp_dir, + req->hook_queue, + NULL); + } + else { + req->body_status = APREQ_ERROR_NOPARSER; + return; + } + } + else { + req->body_status = APREQ_ERROR_NOHEADER; + return; + } + } + else { + if (req->parser->brigade_limit > req->brigade_limit) + req->parser->brigade_limit = req->brigade_limit; + if (req->temp_dir != NULL) + req->parser->temp_dir = req->temp_dir; + if (req->hook_queue != NULL) + apreq_parser_add_hook(req->parser, req->hook_queue); + } + + req->hook_queue = NULL; + req->in = apr_brigade_create(pool, ba); + req->tmpbb = apr_brigade_create(pool, ba); + + apr_file_open_stdin(&file, pool); // error status? + pipe = apr_bucket_pipe_create(file, ba); + eos = apr_bucket_eos_create(ba); + APR_BRIGADE_INSERT_HEAD(req->in, pipe); + APR_BRIGADE_INSERT_TAIL(req->in, eos); + + req->body_status = APR_INCOMPLETE; + +} + +static apr_status_t cgi_read(apreq_handle_t *handle, + apr_off_t bytes) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + apr_bucket *e; + apr_status_t s; + + if (req->body_status == APR_EINIT) + init_body(handle); + + if (req->body_status != APR_INCOMPLETE) + return req->body_status; + + + switch (s = apr_brigade_partition(req->in, bytes, &e)) { + apr_off_t len; + + case APR_SUCCESS: + + apreq_brigade_move(req->tmpbb, req->in, e); + req->bytes_read += bytes; + + if (req->bytes_read > req->read_limit) { + req->body_status = APREQ_ERROR_OVERLIMIT; + cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, + handle, "Bytes read (%" APR_UINT64_T_FMT + ") exceeds configured limit (%" APR_UINT64_T_FMT ")", + req->bytes_read, req->read_limit); + break; + } + + req->body_status = + apreq_parser_run(req->parser, req->body, req->tmpbb); + apr_brigade_cleanup(req->tmpbb); + break; + + + case APR_INCOMPLETE: + + apreq_brigade_move(req->tmpbb, req->in, e); + s = apr_brigade_length(req->tmpbb, 1, &len); + + if (s != APR_SUCCESS) { + req->body_status = s; + break; + } + req->bytes_read += len; + + if (req->bytes_read > req->read_limit) { + req->body_status = APREQ_ERROR_OVERLIMIT; + cgi_log_error(CGILOG_MARK, CGILOG_ERR, req->body_status, handle, + "Bytes read (%" APR_UINT64_T_FMT + ") exceeds configured limit (%" APR_UINT64_T_FMT ")", + req->bytes_read, req->read_limit); + + break; + } + + req->body_status = + apreq_parser_run(req->parser, req->body, req->tmpbb); + apr_brigade_cleanup(req->tmpbb); + break; + + default: + req->body_status = s; + } + + return req->body_status; +} + + + +static apr_status_t cgi_jar(apreq_handle_t *handle, + const apr_table_t **t) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + if (req->jar_status == APR_EINIT) { + const char *cookies = cgi_header_in(handle, "Cookie"); + if (cookies != NULL) { + req->jar = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS); + req->jar_status = + apreq_parse_cookie_header(handle->pool, req->jar, cookies); + } + else + req->jar_status = APREQ_ERROR_NODATA; + } + + *t = req->jar; + return req->jar_status; +} + +static apr_status_t cgi_args(apreq_handle_t *handle, + const apr_table_t **t) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + if (req->args_status == APR_EINIT) { + const char *qs = cgi_query_string(handle); + if (qs != NULL) { + req->args = apr_table_make(handle->pool, APREQ_DEFAULT_NELTS); + req->args_status = + apreq_parse_query_string(handle->pool, req->args, qs); + } + else + req->args_status = APREQ_ERROR_NODATA; + } + + *t = req->args; + return req->args_status; +} + + + + +static apreq_cookie_t *cgi_jar_get(apreq_handle_t *handle, + const char *name) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + const apr_table_t *t; + const char *val; + + if (req->jar_status == APR_EINIT) + cgi_jar(handle, &t); + else + t = req->jar; + + if (t == NULL) + return NULL; + + val = apr_table_get(t, name); + if (val == NULL) + return NULL; + + return apreq_value_to_cookie(val); +} + +static apreq_param_t *cgi_args_get(apreq_handle_t *handle, + const char *name) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + const apr_table_t *t; + const char *val; + + if (req->args_status == APR_EINIT) + cgi_args(handle, &t); + else + t = req->args; + + if (t == NULL) + return NULL; + + val = apr_table_get(t, name); + if (val == NULL) + return NULL; + + return apreq_value_to_param(val); +} + + + +static apr_status_t cgi_body(apreq_handle_t *handle, + const apr_table_t **t) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + switch (req->body_status) { + + case APR_EINIT: + init_body(handle); + if (req->body_status != APR_INCOMPLETE) + break; + + case APR_INCOMPLETE: + while (cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE) + == APR_INCOMPLETE) + ; /*loop*/ + } + + *t = req->body; + return req->body_status; +} + +static apreq_param_t *cgi_body_get(apreq_handle_t *handle, + const char *name) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + const char *val; + apreq_hook_t *h; + + switch (req->body_status) { + + case APR_SUCCESS: + + val = apr_table_get(req->body, name); + if (val != NULL) + return apreq_value_to_param(val); + return NULL; + + + case APR_EINIT: + + init_body(handle); + if (req->body_status != APR_INCOMPLETE) + return NULL; + cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE); + + + case APR_INCOMPLETE: + + val = apr_table_get(req->body, name); + if (val != NULL) + return apreq_value_to_param(val); + + /* Not seen yet, so we need to scan for + param while prefetching the body */ + + if (req->find_param == NULL) + req->find_param = apreq_hook_make(handle->pool, + apreq_hook_find_param, + NULL, NULL); + h = req->find_param; + h->next = req->parser->hook; + req->parser->hook = h; + h->ctx = (void *)name; + + do { + cgi_read(handle, APREQ_DEFAULT_READ_BLOCK_SIZE); + if (h->ctx != name) { + req->parser->hook = h->next; + return h->ctx; + } + } while (req->body_status == APR_INCOMPLETE); + + req->parser->hook = h->next; + return NULL; + + + default: + + if (req->body == NULL) + return NULL; + + val = apr_table_get(req->body, name); + if (val != NULL) + return apreq_value_to_param(val); + return NULL; + } + + /* not reached */ + return NULL; +} + +static apr_status_t cgi_parser_get(apreq_handle_t *handle, + const apreq_parser_t **parser) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + *parser = req->parser; + return APR_SUCCESS; +} + +static apr_status_t cgi_parser_set(apreq_handle_t *handle, + apreq_parser_t *parser) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + if (req->parser == NULL) { + + if (req->hook_queue != NULL) { + apr_status_t s = apreq_parser_add_hook(parser, req->hook_queue); + if (s != APR_SUCCESS) + return s; + } + if (req->temp_dir != NULL) { + parser->temp_dir = req->temp_dir; + } + if (req->brigade_limit < parser->brigade_limit) { + parser->brigade_limit = req->brigade_limit; + } + + req->hook_queue = NULL; + req->parser = parser; + return APR_SUCCESS; + } + else + return APREQ_ERROR_MISMATCH; +} + + +static apr_status_t cgi_hook_add(apreq_handle_t *handle, + apreq_hook_t *hook) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + if (req->parser != NULL) { + return apreq_parser_add_hook(req->parser, hook); + } + else if (req->hook_queue != NULL) { + apreq_hook_t *h = req->hook_queue; + while (h->next != NULL) + h = h->next; + h->next = hook; + } + else { + req->hook_queue = hook; + } + return APR_SUCCESS; + +} + +static apr_status_t cgi_brigade_limit_set(apreq_handle_t *handle, + apr_size_t bytes) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + apr_size_t *limit = (req->parser == NULL) + ? &req->brigade_limit + : &req->parser->brigade_limit; + + if (*limit > bytes) { + *limit = bytes; + return APR_SUCCESS; + } + + return APREQ_ERROR_MISMATCH; +} + +static apr_status_t cgi_brigade_limit_get(apreq_handle_t *handle, + apr_size_t *bytes) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + *bytes = (req->parser == NULL) + ? req->brigade_limit + : req->parser->brigade_limit; + + return APR_SUCCESS; +} + +static apr_status_t cgi_read_limit_set(apreq_handle_t *handle, + apr_uint64_t bytes) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + + if (req->read_limit > bytes && req->bytes_read < bytes) { + req->read_limit = bytes; + return APR_SUCCESS; + } + + return APREQ_ERROR_MISMATCH; +} + + +static apr_status_t cgi_read_limit_get(apreq_handle_t *handle, + apr_uint64_t *bytes) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + *bytes = req->read_limit; + return APR_SUCCESS; +} + + +static apr_status_t cgi_temp_dir_set(apreq_handle_t *handle, + const char *path) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + const char **temp_dir = (req->parser == NULL) + ? &req->temp_dir + : &req->parser->temp_dir; + + + if (*temp_dir == NULL && req->bytes_read == 0) { + if (path != NULL) + *temp_dir = apr_pstrdup(handle->pool, path); + return APR_SUCCESS; + } + + return APREQ_ERROR_MISMATCH; +} + + +static apr_status_t cgi_temp_dir_get(apreq_handle_t *handle, + const char **path) +{ + struct cgi_handle *req = (struct cgi_handle *)handle; + *path = (req->parser == NULL) + ? req->temp_dir + : req->parser->temp_dir; + return APR_SUCCESS; +} + + + +#ifdef APR_POOL_DEBUG +static apr_status_t ba_cleanup(void *data) +{ + apr_bucket_alloc_t *ba = data; + apr_bucket_alloc_destroy(ba); + return APR_SUCCESS; +} +#endif + +static APREQ_MODULE(cgi, 20050425); + +APREQ_DECLARE(apreq_handle_t *)apreq_handle_cgi(apr_pool_t *pool) +{ + apr_bucket_alloc_t *ba; + struct cgi_handle *req; + void *data; + + apr_pool_userdata_get(&data, USER_DATA_KEY, pool); + + if (data != NULL) + return data; + + req = apr_pcalloc(pool, sizeof *req); + ba = apr_bucket_alloc_create(pool); + + /* check pool's userdata first. */ + + req->handle.module = &cgi_module; + req->handle.pool = pool; + req->handle.bucket_alloc = ba; + req->read_limit = (apr_uint64_t) -1; + req->brigade_limit = APREQ_DEFAULT_BRIGADE_LIMIT; + + req->args_status = + req->jar_status = + req->body_status = APR_EINIT; + + apr_pool_userdata_setn(&req->handle, USER_DATA_KEY, NULL, pool); + +#ifdef APR_POOL_DEBUG + apr_pool_cleanup_register(pool, ba, ba_cleanup, ba_cleanup); +#endif + + return &req->handle; +} diff --git a/srclib/libapreq/library/module_custom.c b/srclib/libapreq/library/module_custom.c new file mode 100644 index 0000000000..e1e6f58bfe --- /dev/null +++ b/srclib/libapreq/library/module_custom.c @@ -0,0 +1,304 @@ +/* +** 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 "apr_strings.h" +#include "apreq_module.h" +#include "apreq_error.h" +#include "apreq_util.h" + +#define READ_BYTES (64 * 1024) + +struct custom_handle { + struct apreq_handle_t handle; + + apr_table_t *jar, *args, *body; + apr_status_t jar_status, + args_status, + body_status; + + apreq_parser_t *parser; + + apr_uint64_t read_limit; + apr_uint64_t bytes_read; + apr_bucket_brigade *in; + apr_bucket_brigade *tmpbb; +}; + + +static apr_status_t custom_parse_brigade(apreq_handle_t *handle, apr_uint64_t bytes) +{ + struct custom_handle *req = (struct custom_handle *)handle; + apr_status_t s; + apr_bucket *e; + + if (req->body_status != APR_INCOMPLETE) + return req->body_status; + + switch (s = apr_brigade_partition(req->in, bytes, &e)) { + apr_off_t len; + + case APR_SUCCESS: + apreq_brigade_move(req->tmpbb, req->in, e); + req->bytes_read += bytes; + + if (req->bytes_read > req->read_limit) { + req->body_status = APREQ_ERROR_OVERLIMIT; + break; + } + + req->body_status = + apreq_parser_run(req->parser, req->body, req->tmpbb); + + apr_brigade_cleanup(req->tmpbb); + break; + + case APR_INCOMPLETE: + apreq_brigade_move(req->tmpbb, req->in, e); + s = apr_brigade_length(req->tmpbb, 1, &len); + if (s != APR_SUCCESS) { + req->body_status = s; + break; + } + req->bytes_read += len; + + if (req->bytes_read > req->read_limit) { + req->body_status = APREQ_ERROR_OVERLIMIT; + break; + } + req->body_status = + apreq_parser_run(req->parser, req->body, req->tmpbb); + + apr_brigade_cleanup(req->tmpbb); + break; + + default: + req->body_status = s; + } + + return req->body_status; +} + + + +static apr_status_t custom_jar(apreq_handle_t *handle, const apr_table_t **t) +{ + struct custom_handle *req = (struct custom_handle *)handle; + *t = req->jar; + return req->jar_status; +} + +static apr_status_t custom_args(apreq_handle_t *handle, const apr_table_t **t) +{ + struct custom_handle *req = (struct custom_handle*)handle; + *t = req->args; + return req->args_status; +} + +static apr_status_t custom_body(apreq_handle_t *handle, const apr_table_t **t) +{ + struct custom_handle *req = (struct custom_handle*)handle; + while (req->body_status == APR_INCOMPLETE) + custom_parse_brigade(handle, READ_BYTES); + *t = req->body; + return req->body_status; +} + + + +static apreq_cookie_t *custom_jar_get(apreq_handle_t *handle, const char *name) +{ + struct custom_handle *req = (struct custom_handle*)handle; + const char *val; + + if (req->jar == NULL || name == NULL) + return NULL; + + val = apr_table_get(req->jar, name); + + if (val == NULL) + return NULL; + + return apreq_value_to_cookie(val); +} + +static apreq_param_t *custom_args_get(apreq_handle_t *handle, const char *name) +{ + struct custom_handle *req = (struct custom_handle*)handle; + const char *val; + + if (req->args == NULL || name == NULL) + return NULL; + + val = apr_table_get(req->args, name); + + if (val == NULL) + return NULL; + + return apreq_value_to_param(val); +} + +static apreq_param_t *custom_body_get(apreq_handle_t *handle, const char *name) +{ + struct custom_handle *req = (struct custom_handle*)handle; + const char *val; + + if (req->body == NULL || name == NULL) + return NULL; + + while (1) { + *(const char **)&val = apr_table_get(req->body, name); + if (val != NULL) + break; + + if (req->body_status == APR_INCOMPLETE) + custom_parse_brigade(handle, READ_BYTES); + else + return NULL; + } + + return apreq_value_to_param(val); +} + + + +static apr_status_t custom_parser_get(apreq_handle_t *handle, + const apreq_parser_t **parser) +{ + struct custom_handle *req = (struct custom_handle*)handle; + *parser = req->parser; + + return APR_SUCCESS; +} + +static apr_status_t custom_parser_set(apreq_handle_t *handle, + apreq_parser_t *parser) +{ + (void)handle; + (void)parser; + return APR_ENOTIMPL; +} + +static apr_status_t custom_hook_add(apreq_handle_t *handle, + apreq_hook_t *hook) +{ + struct custom_handle *req = (struct custom_handle*)handle; + apreq_parser_add_hook(req->parser, hook); + return APR_SUCCESS; +} + +static apr_status_t custom_brigade_limit_get(apreq_handle_t *handle, + apr_size_t *bytes) +{ + struct custom_handle *req = (struct custom_handle*)handle; + *bytes = req->parser->brigade_limit; + return APR_SUCCESS; +} + +static apr_status_t custom_brigade_limit_set(apreq_handle_t *handle, + apr_size_t bytes) +{ + (void)handle; + (void)bytes; + return APR_ENOTIMPL; +} + +static apr_status_t custom_read_limit_get(apreq_handle_t *handle, + apr_uint64_t *bytes) +{ + struct custom_handle *req = (struct custom_handle*)handle; + *bytes = req->read_limit; + return APR_SUCCESS; +} + +static apr_status_t custom_read_limit_set(apreq_handle_t *handle, + apr_uint64_t bytes) +{ + (void)handle; + (void)bytes; + return APR_ENOTIMPL; +} + +static apr_status_t custom_temp_dir_get(apreq_handle_t *handle, + const char **path) +{ + struct custom_handle *req = (struct custom_handle*)handle; + + *path = req->parser->temp_dir; + return APR_SUCCESS; +} + +static apr_status_t custom_temp_dir_set(apreq_handle_t *handle, + const char *path) +{ + (void)handle; + (void)path; + return APR_ENOTIMPL; +} + + +static APREQ_MODULE(custom, 20070428); + +APREQ_DECLARE(apreq_handle_t *)apreq_handle_custom(apr_pool_t *pool, + const char *query_string, + const char *cookie, + apreq_parser_t *parser, + apr_uint64_t read_limit, + apr_bucket_brigade *in) +{ + struct custom_handle *req; + req = apr_palloc(pool, sizeof(*req)); + req->handle.module = &custom_module; + req->handle.pool = pool; + req->handle.bucket_alloc = in->bucket_alloc; + req->read_limit = read_limit; + req->bytes_read = 0; + req->parser = parser; + req->in = apr_brigade_create(pool, in->bucket_alloc); + req->tmpbb = apr_brigade_create(pool, in->bucket_alloc); + req->body = apr_table_make(pool, APREQ_DEFAULT_NELTS); + req->body_status = APR_INCOMPLETE; + APR_BRIGADE_CONCAT(req->in, in); + + if (cookie != NULL) { + req->jar = apr_table_make(pool, APREQ_DEFAULT_NELTS); + req->jar_status = + apreq_parse_cookie_header(pool, req->jar, cookie); + } + else { + req->jar = NULL; + req->jar_status = APREQ_ERROR_NODATA; + } + + + if (query_string != NULL) { + req->args = apr_table_make(pool, APREQ_DEFAULT_NELTS); + req->args_status = + apreq_parse_query_string(pool, req->args, query_string); + } + else { + req->args = NULL; + req->args_status = APREQ_ERROR_NODATA; + } + + if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(req->in))) { + apr_bucket *eos = apr_bucket_eos_create(in->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(req->in, eos); + } + + return &req->handle; +} + diff --git a/srclib/libapreq/library/param.c b/srclib/libapreq/library/param.c new file mode 100644 index 0000000000..83e185b595 --- /dev/null +++ b/srclib/libapreq/library/param.c @@ -0,0 +1,272 @@ +/* +** 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_param.h" +#include "apreq_error.h" +#include "apreq_util.h" +#include "apr_strings.h" +#include "apr_lib.h" + +#define MAX_LEN (1024 * 1024) +#define MAX_BRIGADE_LEN (1024 * 256) +#define MAX_READ_AHEAD (1024 * 64) + + +APREQ_DECLARE(apreq_param_t *) apreq_param_make(apr_pool_t *p, + const char *name, + const apr_size_t nlen, + const char *val, + const apr_size_t vlen) +{ + apreq_param_t *param; + apreq_value_t *v; + + param = apr_palloc(p, nlen + vlen + 1 + sizeof *param); + + if (param == NULL) + return NULL; + + param->info = NULL; + param->upload = NULL; + param->flags = 0; + + *(const apreq_value_t **)&v = ¶m->v; + + if (vlen && val != NULL) + memcpy(v->data, val, vlen); + v->data[vlen] = 0; + v->dlen = vlen; + + v->name = v->data + vlen + 1; + if (nlen && name != NULL) + memcpy(v->name, name, nlen); + v->name[nlen] = 0; + v->nlen = nlen; + + return param; +} + +APREQ_DECLARE(apr_status_t) apreq_param_decode(apreq_param_t **param, + apr_pool_t *pool, + const char *word, + apr_size_t nlen, + apr_size_t vlen) +{ + apr_status_t status; + apreq_value_t *v; + apreq_param_t *p; + apreq_charset_t charset; + + if (nlen == 0) { + *param = NULL; + return APR_EBADARG; + } + + p = apr_palloc(pool, nlen + vlen + 1 + sizeof *p); + p->info = NULL; + p->upload = NULL; + p->flags = 0; + *(const apreq_value_t **)&v = &p->v; + + if (vlen > 0) { + status = apreq_decode(v->data, &v->dlen, word + nlen + 1, vlen); + if (status != APR_SUCCESS) { + *param = NULL; + return status; + } + charset = apreq_charset_divine(v->data, v->dlen); + } + else { + v->data[0] = 0; + v->dlen = 0; + charset = APREQ_CHARSET_ASCII; + } + v->name = v->data + vlen + 1; + + status = apreq_decode(v->name, &v->nlen, word, nlen); + if (status != APR_SUCCESS) { + *param = NULL; + return status; + } + + switch (apreq_charset_divine(v->name, v->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; + } + + apreq_param_charset_set(p, charset); + *param = p; + + return APR_SUCCESS; +} + + +APREQ_DECLARE(char *) apreq_param_encode(apr_pool_t *pool, + const apreq_param_t *param) +{ + apr_size_t dlen; + char *data; + data = apr_palloc(pool, 3 * (param->v.nlen + param->v.dlen) + 2); + dlen = apreq_encode(data, param->v.name, param->v.nlen); + data[dlen++] = '='; + dlen += apreq_encode(data + dlen, param->v.data, param->v.dlen); + + return data; +} + +APREQ_DECLARE(apr_status_t) apreq_parse_query_string(apr_pool_t *pool, + apr_table_t *t, + const char *qs) +{ + const char *start = qs; + apr_size_t nlen = 0; + + for (;;++qs) { + switch (*qs) { + + case '=': + if (nlen == 0) { + nlen = qs - start; + } + break; + + case '&': + case ';': + case 0: + if (qs > start) { + apr_size_t vlen = 0; + apreq_param_t *param; + apr_status_t s; + if (nlen == 0) + nlen = qs - start; + else + vlen = qs - start - nlen - 1; + + s = apreq_param_decode(¶m, pool, start, nlen, vlen); + if (s != APR_SUCCESS) + return s; + + apreq_param_tainted_on(param); + apreq_value_table_add(¶m->v, t); + } + + if (*qs == 0) + return APR_SUCCESS; + + nlen = 0; + start = qs + 1; + } + } + /* not reached */ + return APR_INCOMPLETE; +} + + + + +static int param_push(void *data, const char *key, const char *val) +{ + apr_array_header_t *arr = data; + *(apreq_param_t **)apr_array_push(arr) = + apreq_value_to_param(val); + return 1; /* keep going */ +} + + +APREQ_DECLARE(apr_array_header_t *) apreq_params_as_array(apr_pool_t *p, + const apr_table_t *t, + const char *key) +{ + apr_array_header_t *arr; + + arr = apr_array_make(p, apr_table_elts(t)->nelts, + sizeof(apreq_param_t *)); + + apr_table_do(param_push, arr, t, key, NULL); + return arr; +} + +APREQ_DECLARE(const char *) apreq_params_as_string(apr_pool_t *p, + const apr_table_t *t, + const char *key, + apreq_join_t mode) +{ + apr_array_header_t *arr = apreq_params_as_array(p, t, key); + apreq_param_t **elt = (apreq_param_t **)arr->elts; + apreq_param_t **const end = elt + arr->nelts; + if (arr->nelts == 0) + return apr_pstrdup(p, ""); + + while (elt < end) { + *(const apreq_value_t **)elt = &(**elt).v; + ++elt; + } + return apreq_join(p, ", ", arr, mode); +} + + + +static int upload_push(void *data, const char *key, const char *val) +{ + apr_table_t *t = data; + apreq_param_t *p = apreq_value_to_param(val); + + if (p->upload != NULL) + apreq_value_table_add(&p->v, t); + return 1; /* keep going */ +} + + +APREQ_DECLARE(const apr_table_t *) apreq_uploads(const apr_table_t *body, + apr_pool_t *pool) +{ + apr_table_t *t = apr_table_make(pool, APREQ_DEFAULT_NELTS); + apr_table_do(upload_push, t, body, NULL); + return t; +} + +static int upload_set(void *data, const char *key, const char *val) +{ + const apreq_param_t **q = data; + apreq_param_t *p = apreq_value_to_param(val); + + if (p->upload != NULL) { + *q = p; + return 0; /* upload found, stop */ + } + else + return 1; /* keep searching */ +} + + +APREQ_DECLARE(const apreq_param_t *) apreq_upload(const apr_table_t *body, + const char *name) +{ + apreq_param_t *param = NULL; + apr_table_do(upload_set, ¶m, body, name, NULL); + return param; +} diff --git a/srclib/libapreq/library/parser.c b/srclib/libapreq/library/parser.c new file mode 100644 index 0000000000..1d7c46dceb --- /dev/null +++ b/srclib/libapreq/library/parser.c @@ -0,0 +1,353 @@ +/* +** 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) +{ + const char *key = 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 && strcasecmp(key, param->v.name) == 0) + hook->ctx = param; + + return s; +} diff --git a/srclib/libapreq/library/parser_header.c b/srclib/libapreq/library/parser_header.c new file mode 100644 index 0000000000..ae2e030062 --- /dev/null +++ b/srclib/libapreq/library/parser_header.c @@ -0,0 +1,365 @@ +/* +** 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 <assert.h> +#include "apreq_parser.h" +#include "apreq_error.h" +#include "apreq_util.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 hdr_ctx { + apr_bucket_brigade *bb; + apr_size_t nlen; + apr_size_t glen; + apr_size_t vlen; + enum { + HDR_NAME, + HDR_GAP, + HDR_VALUE, + HDR_NEWLINE, + HDR_CONTINUE, + HDR_COMPLETE, + HDR_ERROR + } status; +}; + +/********************* header parsing utils ********************/ + + +static apr_status_t split_header_line(apreq_param_t **p, + apr_pool_t *pool, + apr_bucket_brigade *bb, + apr_size_t nlen, + apr_size_t glen, + 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], *iov, *end; + apr_array_header_t arr; + char *dest; + const char *data; + apr_size_t dlen; + + if (nlen == 0) + return APR_EBADARG; + + param = apreq_param_make(pool, NULL, nlen, NULL, vlen - 1); /*drop (CR)LF */ + *(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; + + e = APR_BRIGADE_FIRST(bb); + + /* store name in a temporary iovec array */ + + while (nlen > 0) { + apr_size_t len; + end = apr_array_push(&arr); + s = apr_bucket_read(e, (const char **)&end->iov_base, + &len, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + assert(nlen >= len); + end->iov_len = len; + nlen -= len; + + e = APR_BUCKET_NEXT(e); + } + + /* skip gap */ + + while (glen > 0) { + s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + assert(glen >= dlen); + glen -= dlen; + e = APR_BUCKET_NEXT(e); + } + + /* copy value */ + assert(vlen > 0); + dest = v->data; + while (vlen > 0) { + + s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + memcpy(dest, data, dlen); + dest += dlen; + assert(vlen >= dlen); + vlen -= dlen; + e = APR_BUCKET_NEXT(e); + } + + assert(dest[-1] == '\n'); + + if (dest[-2] == '\r') + --dest; + + dest[-1] = 0; + v->dlen = (dest - v->data) - 1; + + /* write name */ + v->name = dest; + iov = (struct iovec *)arr.elts; + + while (iov <= end) { + memcpy(dest, iov->iov_base, iov->iov_len); + dest += iov->iov_len; + ++iov; + } + *dest = 0; + nlen = dest - v->name; + + while ((f = APR_BRIGADE_FIRST(bb)) != e) + apr_bucket_delete(f); + + apreq_param_tainted_on(param); + *p = param; + return APR_SUCCESS; + +} + + +APREQ_DECLARE_PARSER(apreq_parse_headers) +{ + apr_pool_t *pool = parser->pool; + apr_bucket *e; + struct hdr_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 = HDR_NAME; + } + else + ctx = parser->ctx; + + PARSER_STATUS_CHECK(HDR); + e = APR_BRIGADE_LAST(ctx->bb); + APR_BRIGADE_CONCAT(ctx->bb, bb); + + parse_hdr_brigade: + + + /* parse the brigade for CRLF_CRLF-terminated header block, + * each time starting from the front of the brigade. + */ + + for (e = APR_BUCKET_NEXT(e); + e != APR_BRIGADE_SENTINEL(ctx->bb); + e = APR_BUCKET_NEXT(e)) + { + apr_size_t off = 0, dlen; + const char *data; + apr_status_t s; + apreq_param_t *param = NULL; /* silences gcc-4.0 warning */ + + if (APR_BUCKET_IS_EOS(e)) { + ctx->status = HDR_COMPLETE; + APR_BRIGADE_CONCAT(bb, ctx->bb); + return APR_SUCCESS; + } + s = apr_bucket_read(e, &data, &dlen, APR_BLOCK_READ); + + if ( s != APR_SUCCESS ) { + ctx->status = HDR_ERROR; + return s; + } + if (dlen == 0) + continue; + + parse_hdr_bucket: + + /* gap nlen = 13 + * vvv glen = 3 + * Sample-Header: grape vlen = 5 + * ^^^^^^^^^^^^^ ^^^^^ + * name value + */ + + switch (ctx->status) { + + case HDR_NAME: + + while (off < dlen) { + switch (data[off++]) { + + case '\n': + if (off < dlen) + apr_bucket_split(e, off); + e = APR_BUCKET_NEXT(e); + + do { + apr_bucket *f = APR_BRIGADE_FIRST(ctx->bb); + apr_bucket_delete(f); + } while (e != APR_BRIGADE_FIRST(ctx->bb)); + APR_BRIGADE_CONCAT(bb, ctx->bb); + ctx->status = HDR_COMPLETE; + return APR_SUCCESS; + + case ':': + if (off > 1) { + apr_bucket_split(e, off - 1); + dlen -= off - 1; + data += off - 1; + off = 1; + e = APR_BUCKET_NEXT(e); + } + ++ctx->glen; + ctx->status = HDR_GAP; + goto parse_hdr_bucket; + + default: + ++ctx->nlen; + } + + } + + break; + + + case HDR_GAP: + + while (off < dlen) { + switch (data[off++]) { + case ' ': + case '\t': + ++ctx->glen; + break; + + case '\n': + ctx->status = HDR_NEWLINE; + goto parse_hdr_bucket; + + default: + ctx->status = HDR_VALUE; + if (off > 1) { + apr_bucket_split(e, off - 1); + dlen -= off - 1; + data += off - 1; + off = 1; + e = APR_BUCKET_NEXT(e); + } + ++ctx->vlen; + goto parse_hdr_bucket; + } + } + break; + + + case HDR_VALUE: + + while (off < dlen) { + ++ctx->vlen; + if (data[off++] == '\n') { + ctx->status = HDR_NEWLINE; + goto parse_hdr_bucket; + } + } + break; + + + case HDR_NEWLINE: + + if (off == dlen) + break; + else { + switch (data[off]) { + + case ' ': + case '\t': + ctx->status = HDR_CONTINUE; + ++off; + ++ctx->vlen; + break; + + default: + /* can parse brigade now */ + if (off > 0) + apr_bucket_split(e, off); + s = split_header_line(¶m, pool, ctx->bb, ctx->nlen, ctx->glen, ctx->vlen); + if (parser->hook != NULL && s == APR_SUCCESS) + s = apreq_hook_run(parser->hook, param, NULL); + + if (s != APR_SUCCESS) { + ctx->status = HDR_ERROR; + return s; + } + + apreq_value_table_add(¶m->v, t); + e = APR_BRIGADE_SENTINEL(ctx->bb); + ctx->status = HDR_NAME; + ctx->nlen = 0; + ctx->vlen = 0; + ctx->glen = 0; + + goto parse_hdr_brigade; + } + + /* cases ' ', '\t' fall through to HDR_CONTINUE */ + } + + + case HDR_CONTINUE: + + while (off < dlen) { + switch (data[off++]) { + case ' ': + case '\t': + ++ctx->vlen; + break; + + case '\n': + ctx->status = HDR_NEWLINE; + goto parse_hdr_bucket; + + default: + ctx->status = HDR_VALUE; + ++ctx->vlen; + goto parse_hdr_bucket; + } + } + break; + + default: + ; /* not reached */ + } + } + apreq_brigade_setaside(ctx->bb,pool); + return APR_INCOMPLETE; +} diff --git a/srclib/libapreq/library/parser_multipart.c b/srclib/libapreq/library/parser_multipart.c new file mode 100644 index 0000000000..60b5bad9de --- /dev/null +++ b/srclib/libapreq/library/parser_multipart.c @@ -0,0 +1,661 @@ +/* +** 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_error.h" +#include "apreq_util.h" +#include "apr_strings.h" +#include "apr_strmatch.h" + +#ifndef CRLF +#define CRLF "\015\012" +#endif + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#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); + +/* maximum recursion level in the mfd parser */ +#define MAX_LEVEL 8 + +struct mfd_ctx { + apr_table_t *info; + apr_bucket_brigade *in; + apr_bucket_brigade *bb; + apreq_parser_t *hdr_parser; + apreq_parser_t *next_parser; + const apr_strmatch_pattern *pattern; + char *bdry; + enum { + MFD_INIT, + MFD_NEXTLINE, + MFD_HEADER, + MFD_POST_HEADER, + MFD_PARAM, + MFD_UPLOAD, + MFD_MIXED, + MFD_COMPLETE, + MFD_ERROR + } status; + apr_bucket *eos; + const char *param_name; + apreq_param_t *upload; + unsigned level; +}; + + +/********************* multipart/form-data *********************/ + +APR_INLINE +static apr_status_t brigade_start_string(apr_bucket_brigade *bb, + const char *start_string) +{ + apr_bucket *e; + apr_size_t slen = strlen(start_string); + + for (e = APR_BRIGADE_FIRST(bb); e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + const char *buf; + apr_status_t s, bytes_to_check; + apr_size_t blen; + + if (slen == 0) + return APR_SUCCESS; + + if (APR_BUCKET_IS_EOS(e)) + return APR_EOF; + + s = apr_bucket_read(e, &buf, &blen, APR_BLOCK_READ); + + if (s != APR_SUCCESS) + return s; + + if (blen == 0) + continue; + + bytes_to_check = MIN(slen,blen); + + if (strncmp(buf,start_string,bytes_to_check) != 0) + return APREQ_ERROR_GENERAL; + + slen -= bytes_to_check; + start_string += bytes_to_check; + } + + /* slen > 0, so brigade isn't large enough yet */ + return APR_INCOMPLETE; +} + + +static apr_status_t split_on_bdry(apr_bucket_brigade *out, + apr_bucket_brigade *in, + const apr_strmatch_pattern *pattern, + const char *bdry) +{ + apr_bucket *e = APR_BRIGADE_FIRST(in); + apr_size_t blen = strlen(bdry), off = 0; + + while ( e != APR_BRIGADE_SENTINEL(in) ) { + apr_ssize_t idx; + apr_size_t len; + const char *buf; + apr_status_t s; + + if (APR_BUCKET_IS_EOS(e)) + return APR_EOF; + + s = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + if (len == 0) { + apr_bucket *f = e; + e = APR_BUCKET_NEXT(e); + apr_bucket_delete(f); + continue; + } + + look_for_boundary_up_front: + if (strncmp(bdry + off, buf, MIN(len, blen - off)) == 0) { + if ( len >= blen - off ) { + /* complete match */ + if (len > blen - off) + apr_bucket_split(e, blen - off); + e = APR_BUCKET_NEXT(e); + + do { + apr_bucket *f = APR_BRIGADE_FIRST(in); + apr_bucket_delete(f); + } while (APR_BRIGADE_FIRST(in) != e); + + return APR_SUCCESS; + } + /* partial match */ + off += len; + e = APR_BUCKET_NEXT(e); + continue; + } + else if (off > 0) { + /* prior (partial) strncmp failed, + * so we can move previous buckets across + * and retest buf against the full bdry. + */ + + /* give hints to GCC by making the brigade volatile, otherwise the + * loop below will end up being endless. See: + * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=193740 + */ + apr_bucket_brigade * volatile in_v = in; + + do { + apr_bucket *f = APR_BRIGADE_FIRST(in_v); + APR_BUCKET_REMOVE(f); + APR_BRIGADE_INSERT_TAIL(out, f); + } while (e != APR_BRIGADE_FIRST(in_v)); + off = 0; + goto look_for_boundary_up_front; + } + + if (pattern != NULL && len >= blen) { + const char *match = apr_strmatch(pattern, buf, len); + if (match != NULL) + idx = match - buf; + else { + idx = apreq_index(buf + len-blen, blen, bdry, blen, + APREQ_MATCH_PARTIAL); + if (idx >= 0) + idx += len-blen; + } + } + else + idx = apreq_index(buf, len, bdry, blen, APREQ_MATCH_PARTIAL); + + /* Theoretically idx should never be 0 here, because we + * already tested the front of the brigade for a potential match. + * However, it doesn't hurt to allow for the possibility, + * since this will just start the whole loop over again. + */ + if (idx >= 0) + apr_bucket_split(e, idx); + + APR_BUCKET_REMOVE(e); + APR_BRIGADE_INSERT_TAIL(out, e); + e = APR_BRIGADE_FIRST(in); + } + + return APR_INCOMPLETE; +} + + +static +struct mfd_ctx * create_multipart_context(const char *content_type, + apr_pool_t *pool, + apr_bucket_alloc_t *ba, + apr_size_t brigade_limit, + const char *temp_dir, + unsigned level) + +{ + apr_status_t s; + apr_size_t blen; + struct mfd_ctx *ctx = apr_palloc(pool, sizeof *ctx); + char *ct = apr_pstrdup(pool, content_type); + + ct = strchr(ct, ';'); + if (ct == NULL) + return NULL; /* missing semicolon */ + + *ct++ = 0; + s = apreq_header_attribute(ct, "boundary", 8, + (const char **)&ctx->bdry, &blen); + + if (s != APR_SUCCESS) + return NULL; /* missing boundary */ + + ctx->bdry[blen] = 0; + + *--ctx->bdry = '-'; + *--ctx->bdry = '-'; + *--ctx->bdry = '\n'; + *--ctx->bdry = '\r'; + + ctx->status = MFD_INIT; + ctx->pattern = apr_strmatch_precompile(pool, ctx->bdry, 1); + ctx->hdr_parser = apreq_parser_make(pool, ba, "", + apreq_parse_headers, + brigade_limit, + temp_dir, NULL, NULL); + ctx->info = NULL; + ctx->bb = apr_brigade_create(pool, ba); + ctx->in = apr_brigade_create(pool, ba); + ctx->eos = apr_bucket_eos_create(ba); + ctx->next_parser = NULL; + ctx->param_name = NULL; + ctx->upload = NULL; + ctx->level = level; + + return ctx; +} + +APREQ_DECLARE_PARSER(apreq_parse_multipart) +{ + apr_pool_t *pool = parser->pool; + apr_bucket_alloc_t *ba = parser->bucket_alloc; + struct mfd_ctx *ctx = parser->ctx; + apr_status_t s; + + if (ctx == NULL) { + ctx = create_multipart_context(parser->content_type, + pool, ba, + parser->brigade_limit, + parser->temp_dir, 1); + if (ctx == NULL) + return APREQ_ERROR_GENERAL; + + + parser->ctx = ctx; + } + + PARSER_STATUS_CHECK(MFD); + APR_BRIGADE_CONCAT(ctx->in, bb); + + mfd_parse_brigade: + + switch (ctx->status) { + + case MFD_INIT: + { + s = split_on_bdry(ctx->bb, ctx->in, NULL, ctx->bdry + 2); + if (s != APR_SUCCESS) { + apreq_brigade_setaside(ctx->in, pool); + apreq_brigade_setaside(ctx->bb, pool); + return s; + } + ctx->status = MFD_NEXTLINE; + /* Be polite and return any preamble text to the caller. */ + APR_BRIGADE_CONCAT(bb, ctx->bb); + } + + /* fall through */ + + case MFD_NEXTLINE: + { + s = split_on_bdry(ctx->bb, ctx->in, NULL, CRLF); + if (s == APR_EOF) { + ctx->status = MFD_COMPLETE; + return APR_SUCCESS; + } + if (s != APR_SUCCESS) { + apreq_brigade_setaside(ctx->in, pool); + apreq_brigade_setaside(ctx->bb, pool); + return s; + } + if (!APR_BRIGADE_EMPTY(ctx->bb)) { + char *line; + apr_size_t len; + apr_brigade_pflatten(ctx->bb, &line, &len, pool); + + if (len >= 2 && strncmp(line, "--", 2) == 0) { + APR_BRIGADE_CONCAT(bb, ctx->in); + ctx->status = MFD_COMPLETE; + return APR_SUCCESS; + } + apr_brigade_cleanup(ctx->bb); + } + + ctx->status = MFD_HEADER; + ctx->info = NULL; + } + /* fall through */ + + case MFD_HEADER: + { + if (ctx->info == NULL) { + ctx->info = apr_table_make(pool, APREQ_DEFAULT_NELTS); + /* flush out header parser internal structs for reuse */ + ctx->hdr_parser->ctx = NULL; + } + s = apreq_parser_run(ctx->hdr_parser, ctx->info, ctx->in); + switch (s) { + case APR_SUCCESS: + ctx->status = MFD_POST_HEADER; + break; + case APR_INCOMPLETE: + apreq_brigade_setaside(ctx->in, pool); + return APR_INCOMPLETE; + default: + ctx->status = MFD_ERROR; + return s; + } + } + /* fall through */ + + case MFD_POST_HEADER: + { + /* Must handle special case of missing CRLF (mainly + * coming from empty file uploads). See RFC2065 S5.1.1: + * + * body-part = MIME-part-header [CRLF *OCTET] + * + * So the CRLF we already matched in MFD_HEADER may have been + * part of the boundary string! Both Konqueror (v??) and + * Mozilla-0.97 are known to emit such blocks. + * + * Here we first check for this condition with + * brigade_start_string, and prefix the brigade with + * an additional CRLF bucket if necessary. + */ + + const char *cd, *ct, *name, *filename; + apr_size_t nlen, flen; + apr_bucket *e; + + switch (brigade_start_string(ctx->in, ctx->bdry + 2)) { + + case APR_INCOMPLETE: + apreq_brigade_setaside(ctx->in, pool); + return APR_INCOMPLETE; + + case APR_SUCCESS: + /* part has no body- return CRLF to front */ + e = apr_bucket_immortal_create(CRLF, 2, + ctx->bb->bucket_alloc); + APR_BRIGADE_INSERT_HEAD(ctx->in, e); + break; + + default: + ; /* has body, ok */ + } + + cd = apr_table_get(ctx->info, "Content-Disposition"); + + /* First check to see if must descend into a new multipart + * block. If we do, create a new parser and pass control + * to it. + */ + + ct = apr_table_get(ctx->info, "Content-Type"); + + if (ct != NULL && strncmp(ct, "multipart/", 10) == 0) { + struct mfd_ctx *next_ctx; + + if (ctx->level >= MAX_LEVEL) { + ctx->status = MFD_ERROR; + goto mfd_parse_brigade; + } + + next_ctx = create_multipart_context(ct, pool, ba, + parser->brigade_limit, + parser->temp_dir, + ctx->level + 1); + + next_ctx->param_name = ""; + + if (cd != NULL) { + s = apreq_header_attribute(cd, "name", 4, + &name, &nlen); + if (s == APR_SUCCESS) { + next_ctx->param_name + = apr_pstrmemdup(pool, name, nlen); + } + else { + const char *cid = apr_table_get(ctx->info, + "Content-ID"); + if (cid != NULL) + next_ctx->param_name = apr_pstrdup(pool, cid); + } + + } + + ctx->next_parser = apreq_parser_make(pool, ba, ct, + apreq_parse_multipart, + parser->brigade_limit, + parser->temp_dir, + parser->hook, + next_ctx); + ctx->status = MFD_MIXED; + goto mfd_parse_brigade; + + } + + /* Look for a normal form-data part. */ + + if (cd != NULL && strncmp(cd, "form-data", 9) == 0) { + s = apreq_header_attribute(cd, "name", 4, &name, &nlen); + if (s != APR_SUCCESS) { + ctx->status = MFD_ERROR; + goto mfd_parse_brigade; + } + + s = apreq_header_attribute(cd, "filename", + 8, &filename, &flen); + if (s == APR_SUCCESS) { + apreq_param_t *param; + + param = apreq_param_make(pool, name, nlen, + filename, flen); + apreq_param_tainted_on(param); + param->info = ctx->info; + param->upload + = apr_brigade_create(pool, ctx->bb->bucket_alloc); + ctx->upload = param; + ctx->status = MFD_UPLOAD; + goto mfd_parse_brigade; + } + else { + ctx->param_name = apr_pstrmemdup(pool, name, nlen); + ctx->status = MFD_PARAM; + /* fall thru */ + } + } + + /* else check for a file part in a multipart section */ + else if (cd != NULL && strncmp(cd, "file", 4) == 0) { + apreq_param_t *param; + + s = apreq_header_attribute(cd, "filename", + 8, &filename, &flen); + if (s != APR_SUCCESS || ctx->param_name == NULL) { + ctx->status = MFD_ERROR; + goto mfd_parse_brigade; + } + name = ctx->param_name; + nlen = strlen(name); + param = apreq_param_make(pool, name, nlen, + filename, flen); + apreq_param_tainted_on(param); + param->info = ctx->info; + param->upload = apr_brigade_create(pool, + ctx->bb->bucket_alloc); + ctx->upload = param; + ctx->status = MFD_UPLOAD; + goto mfd_parse_brigade; + } + + /* otherwise look for Content-ID in multipart/mixed case */ + else { + const char *cid = apr_table_get(ctx->info, "Content-ID"); + apreq_param_t *param; + + if (cid != NULL) { + name = cid; + nlen = strlen(name); + } + else { + name = ""; + nlen = 0; + } + + filename = ""; + flen = 0; + param = apreq_param_make(pool, name, nlen, + filename, flen); + apreq_param_tainted_on(param); + param->info = ctx->info; + param->upload = apr_brigade_create(pool, + ctx->bb->bucket_alloc); + ctx->upload = param; + ctx->status = MFD_UPLOAD; + goto mfd_parse_brigade; + } + } + /* fall through */ + + case MFD_PARAM: + { + apreq_param_t *param; + apreq_value_t *v; + apr_size_t len; + apr_off_t off; + + s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); + + switch (s) { + + case APR_INCOMPLETE: + apreq_brigade_setaside(ctx->in, pool); + apreq_brigade_setaside(ctx->bb, pool); + return s; + + case APR_SUCCESS: + s = apr_brigade_length(ctx->bb, 1, &off); + if (s != APR_SUCCESS) { + ctx->status = MFD_ERROR; + return s; + } + len = off; + param = apreq_param_make(pool, ctx->param_name, + strlen(ctx->param_name), + NULL, len); + apreq_param_tainted_on(param); + param->info = ctx->info; + + *(const apreq_value_t **)&v = ¶m->v; + apr_brigade_flatten(ctx->bb, v->data, &len); + v->data[len] = 0; + + if (parser->hook != NULL) { + s = apreq_hook_run(parser->hook, param, NULL); + if (s != APR_SUCCESS) { + ctx->status = MFD_ERROR; + return s; + } + } + + apreq_param_charset_set(param, + apreq_charset_divine(v->data, len)); + apreq_value_table_add(v, t); + ctx->status = MFD_NEXTLINE; + ctx->param_name = NULL; + apr_brigade_cleanup(ctx->bb); + goto mfd_parse_brigade; + + default: + ctx->status = MFD_ERROR; + return s; + } + + + } + break; /* not reached */ + + case MFD_UPLOAD: + { + apreq_param_t *param = ctx->upload; + + s = split_on_bdry(ctx->bb, ctx->in, ctx->pattern, ctx->bdry); + switch (s) { + + case APR_INCOMPLETE: + if (parser->hook != NULL) { + s = apreq_hook_run(parser->hook, param, ctx->bb); + if (s != APR_SUCCESS) { + ctx->status = MFD_ERROR; + return s; + } + } + apreq_brigade_setaside(ctx->bb, pool); + apreq_brigade_setaside(ctx->in, pool); + s = apreq_brigade_concat(pool, parser->temp_dir, + parser->brigade_limit, + param->upload, ctx->bb); + return (s == APR_SUCCESS) ? APR_INCOMPLETE : s; + + case APR_SUCCESS: + if (parser->hook != NULL) { + APR_BRIGADE_INSERT_TAIL(ctx->bb, ctx->eos); + s = apreq_hook_run(parser->hook, param, ctx->bb); + APR_BUCKET_REMOVE(ctx->eos); + if (s != APR_SUCCESS) { + ctx->status = MFD_ERROR; + return s; + } + } + apreq_value_table_add(¶m->v, t); + apreq_brigade_setaside(ctx->bb, pool); + s = apreq_brigade_concat(pool, parser->temp_dir, + parser->brigade_limit, + param->upload, ctx->bb); + + if (s != APR_SUCCESS) + return s; + + ctx->status = MFD_NEXTLINE; + goto mfd_parse_brigade; + + default: + ctx->status = MFD_ERROR; + return s; + } + + } + break; /* not reached */ + + + case MFD_MIXED: + { + s = apreq_parser_run(ctx->next_parser, t, ctx->in); + switch (s) { + case APR_SUCCESS: + ctx->status = MFD_INIT; + ctx->param_name = NULL; + goto mfd_parse_brigade; + case APR_INCOMPLETE: + APR_BRIGADE_CONCAT(bb, ctx->in); + return APR_INCOMPLETE; + default: + ctx->status = MFD_ERROR; + return s; + } + + } + break; /* not reached */ + + default: + return APREQ_ERROR_GENERAL; + } + + return APR_INCOMPLETE; +} 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; +} + + diff --git a/srclib/libapreq/library/util.c b/srclib/libapreq/library/util.c new file mode 100644 index 0000000000..f982309d97 --- /dev/null +++ b/srclib/libapreq/library/util.c @@ -0,0 +1,1508 @@ +/* +** 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 "apr_strings.h" +#include "apreq_error.h" +#include "apreq_util.h" +#include "at.h" + + +static void test_atoi64f(dAT) +{ + AT_int_eq(apreq_atoi64f("0"), 0); + AT_int_eq(apreq_atoi64f("-1"), -1); + AT_int_eq(apreq_atoi64f("-"), 0); + AT_int_eq(apreq_atoi64f("5"), 5); + AT_int_eq(apreq_atoi64f("3.333"), 3); + AT_int_eq(apreq_atoi64f("33k"), 33 * 1024); + AT_int_eq(apreq_atoi64f(" +8M "), 8 * 1024 * 1024); + AT_ok(apreq_atoi64f("44GB") == (apr_int64_t)44 * 1024 * 1024 * 1024, + "44GB test"); + AT_ok(apreq_atoi64f("0xaBcDefg") == (apr_int64_t)11259375 * 1024 * 1024 * 1024, + "hex test"); +} + +static void test_atoi64t(dAT) +{ + AT_int_eq(apreq_atoi64t("0"), 0); + AT_int_eq(apreq_atoi64t("-1"), -1); + AT_int_eq(apreq_atoi64t("-g088l3dyg00k"), 0); + AT_int_eq(apreq_atoi64t("5s"), 5); + AT_int_eq(apreq_atoi64t("3.333"), 3); + AT_int_eq(apreq_atoi64t("33d"), 33 * 60 * 60 * 24); + AT_int_eq(apreq_atoi64t(" +8M "), 8 * 60 * 60 * 24 * 30); + AT_int_eq(apreq_atoi64t("+9m"), 9 * 60); + AT_int_eq(apreq_atoi64t("6h"), 6 * 60 * 60); + +} + +static void test_index(dAT) +{ + const char haystack[] = "Four score and seven years ago"; + apr_size_t hlen = sizeof haystack - 1; + AT_int_eq(apreq_index(haystack, hlen, "Four", 4, APREQ_MATCH_FULL), + 0); + AT_int_eq(apreq_index(haystack, hlen, "Four", 4, APREQ_MATCH_PARTIAL), + 0); + AT_int_eq(apreq_index(haystack, hlen, "Fourteen", 8, APREQ_MATCH_FULL), + -1); + AT_int_eq(apreq_index(haystack, hlen, "Fourteen", 8, APREQ_MATCH_PARTIAL), + -1); + AT_int_eq(apreq_index(haystack, hlen, "agoraphobia", 11, APREQ_MATCH_FULL), + -1); + AT_int_eq(apreq_index(haystack, hlen, "agoraphobia", 11, APREQ_MATCH_PARTIAL), + hlen - 3); +} + +#define A_GRAVE 0xE5 +#define KATAKANA_A 0xFF71 + +static void test_decode(dAT) +{ + apr_size_t elen; + char src1[] = "%C3%80%E3%82%a2"; /* A_GRAVE KATAKANA_A as utf8 */ + unsigned char expect[6]; + + AT_int_eq(apreq_decode((char *)expect, &elen, src1, sizeof(src1) -1), + APR_SUCCESS); + AT_int_eq(elen, 5); + AT_int_eq(expect[0], 0xC3); + AT_int_eq(expect[1], 0x80); + AT_int_eq(expect[2], 0xE3); + AT_int_eq(expect[3], 0x82); + AT_int_eq(expect[4], 0xA2); +} + +static void test_charset_divine(dAT) +{ + apr_size_t elen; + char src1[] = "%C3%80%E3%82%a2"; /* A_GRAVE KATAKANA_A as utf8 */ + char src2[] = "pound%A3";/* latin-1 */ + char src3[] = "euro%80";/* cp-1252 */ + char expect[7]; + + AT_int_eq(apreq_decode(expect, &elen, src1, sizeof(src1) -1), + APR_SUCCESS); + + AT_int_eq(apreq_charset_divine(expect, elen), APREQ_CHARSET_UTF8); + + AT_int_eq(apreq_decode(expect, &elen, src2, sizeof(src2) -1), + APR_SUCCESS); + + AT_int_eq(apreq_charset_divine(expect, elen), APREQ_CHARSET_LATIN1); + AT_int_eq(apreq_decode(expect, &elen, src3, sizeof(src3) -1), + APR_SUCCESS); + + AT_int_eq(apreq_charset_divine(expect, elen), APREQ_CHARSET_CP1252); + +} + + +static void test_decodev(dAT) +{ + char src1[] = "%2540%2"; + char src2[] = "0%u0"; + char src3[] = "041"; + struct iovec iovec1[] = { + { src1, sizeof(src1) - 1 }, + { src2, sizeof(src2) - 1 }, + { src3, sizeof(src3) - 1 }, + }; + struct iovec iovec2[] = { + { src1, sizeof(src1) - 1 }, + { src2, sizeof(src2) - 1 }, + }; + const char expect1[] = "%40 A"; + const char expect2[] = "%40 "; + char dest[sizeof(src1) + sizeof(src2) + sizeof(src3)]; + apr_size_t dest_len; + apr_status_t status; + + status = apreq_decodev(dest, &dest_len, iovec1, 3); + AT_int_eq(status, APR_SUCCESS); + AT_int_eq(dest_len, sizeof(expect1) - 1); + AT_mem_eq(dest, expect1, sizeof(expect1) - 1); + + status = apreq_decodev(dest, &dest_len, iovec2, 2); + AT_int_eq(status, APR_INCOMPLETE); + AT_int_eq(dest_len, sizeof(expect2) - 1); + AT_mem_eq(dest, expect2, sizeof(expect2) - 1); +} + + +static void test_encode(dAT) +{ + +} + +static void test_cp1252_to_utf8(dAT) +{ + char src1[] = "%C3%80%E3%82%a2"; /* A_GRAVE KATAKANA_A as utf8 */ + char src2[5]; + unsigned char expect[16]; + apr_size_t slen; + + AT_int_eq(apreq_decode((char *)src2, &slen, src1, sizeof(src1) -1), + APR_SUCCESS); + AT_int_eq(apreq_cp1252_to_utf8((char *)expect, src2, 5), + 12); + + /* 0xC3 */ + AT_int_eq(expect[0], 0xC0 | (0xC3 >> 6)); + AT_int_eq(expect[1], 0xC3 - 0x40); + + /* 0x20AC */ + AT_int_eq(expect[2], 0xE0 | (0x20AC >> 12)); + AT_int_eq(expect[3], 0x80 | ((0x20AC >> 6) & 0x3F)); + AT_int_eq(expect[4], 0x80 | (0x20AC & 0x3F)); + + /* 0xE3 */ + AT_int_eq(expect[5], 0xC3); + AT_int_eq(expect[6], 0xE3 - 0x40); + + /* 0x201A */ + AT_int_eq(expect[7], 0xE0 | (0x201A >> 12)); + AT_int_eq(expect[8], 0x80 | ((0x201A >> 6) & 0x3F)); + AT_int_eq(expect[9], 0x80 | (0x201A & 0x3F)); + + + /* 0xA2 */ + AT_int_eq(expect[10], 0xC0 | (0xA2 >> 6)); + AT_int_eq(expect[11], 0xA2); + +} + +static void test_quote(dAT) +{ + size_t len; + char dst[64]; + + len = apreq_quote(dst, "foo", 3); + AT_int_eq(len, 5); + AT_str_eq(dst, "\"foo\""); + + len = apreq_quote(dst, "\"foo", 4); + AT_int_eq(len, 7); + AT_str_eq(dst, "\"\\\"foo\""); + + len = apreq_quote(dst, "foo\\bar", 7); + AT_int_eq(len, 10); + AT_str_eq(dst, "\"foo\\\\bar\""); + + len = apreq_quote(dst, "foo\0bar", 7); + AT_int_eq(len, 10); + AT_str_eq(dst, "\"foo\\0bar\""); +} + +static void test_quote_once(dAT) +{ + size_t len; + char dst[64]; + + len = apreq_quote_once(dst, "foo", 3); + AT_int_eq(len, 5); + AT_str_eq(dst, "\"foo\""); + + len = apreq_quote_once(dst, "\"foo", 4); + AT_int_eq(len, 7); + AT_str_eq(dst, "\"\\\"foo\""); + + len = apreq_quote_once(dst, "foo\"", 4); + AT_int_eq(len, 7); + AT_str_eq(dst, "\"foo\\\"\""); + + len = apreq_quote_once(dst, "foo\0bar", 7); + AT_int_eq(len, 10); + AT_str_eq(dst, "\"foo\\0bar\""); + + /* null byte must be escaped, even when there are already double + quotes */ + len = apreq_quote_once(dst, "\"foo\0bar\"", 9); + AT_int_eq(len, 14); + AT_str_eq(dst, "\"\\\"foo\\0bar\\\"\""); + + len = apreq_quote_once(dst, "\"foo\"", 5); + AT_int_eq(len, 5); + AT_str_eq(dst, "\"foo\""); + + len = apreq_quote_once(dst, "'foo'", 5); + AT_int_eq(len, 7); + AT_str_eq(dst, "\"'foo'\""); + + len = apreq_quote_once(dst, "\"fo\\o\"", 6); + AT_int_eq(len, 6); + AT_str_eq(dst, "\"fo\\o\""); + + len = apreq_quote_once(dst, "\"foo\"bar\"", 9); + AT_int_eq(len, 14); + AT_str_eq(dst, "\"\\\"foo\\\"bar\\\"\""); +} + +static void test_join(dAT) +{ + +} + +static void test_brigade_fwrite(dAT) +{ + +} + +static void test_file_mktemp(dAT) +{ + + +} + +static void test_header_attribute(dAT) +{ + const char hdr[] = "filename=\"filename=foo\" filename=\"quux.txt\""; + const char *val; + apr_size_t vlen; + + AT_int_eq(apreq_header_attribute(hdr+4, "name", 4, &val, &vlen), + APR_SUCCESS); + AT_int_eq(vlen, 12); + AT_mem_eq("filename=foo", val, 12); + + AT_int_eq(apreq_header_attribute(hdr+4, "filename", 8, &val, &vlen), + APR_SUCCESS); + AT_int_eq(vlen, 8); + AT_mem_eq("quux.txt", val, 8); + +} + +static void test_brigade_concat(dAT) +{ + +} + + + +#define dT(func, plan) #func, func, plan + + +int main(int argc, char *argv[]) +{ + unsigned i, plan = 0; + apr_pool_t *p; + dAT; + at_test_t test_list [] = { + { dT(test_atoi64f, 9) }, + { dT(test_atoi64t, 9) }, + { dT(test_index, 6) }, + { dT(test_decode, 7) }, + { dT(test_charset_divine, 6) }, + { dT(test_decodev, 6) }, + { dT(test_encode, 0) }, + { dT(test_cp1252_to_utf8, 14) }, + { dT(test_quote, 8) }, + { dT(test_quote_once, 18), }, + { dT(test_join, 0) }, + { dT(test_brigade_fwrite, 0) }, + { dT(test_file_mktemp, 0) }, + { dT(test_header_attribute, 6) }, + { dT(test_brigade_concat, 0) }, + }; + + apr_initialize(); + atexit(apr_terminate); + + apr_pool_create(&p, NULL); + + AT = at_create(p, 0, at_report_stdout_make(p)); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + plan += test_list[i].plan; + + AT_begin(plan); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + AT_run(&test_list[i]); + + AT_end(); + + return 0; +} +/* +** 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_util.h" +#include "apreq_error.h" +#include "apr_time.h" +#include "apr_strings.h" +#include "apr_lib.h" +#include <assert.h> + +#undef MAX +#undef MIN +#define MIN(a,b) ( (a) < (b) ? (a) : (b) ) +#define MAX(a,b) ( (a) > (b) ? (a) : (b) ) + +/* used for specifying file sizes */ + +APREQ_DECLARE(apr_int64_t) apreq_atoi64f(const char *s) +{ + apr_int64_t n = 0; + char *p; + if (s == NULL) + return 0; + + n = apr_strtoi64(s, &p, 0); + + if (p == NULL) + return n; + while (apr_isspace(*p)) + ++p; + + switch (*p) { + case 'G': /* fall thru */ + case 'g': return n * 1024*1024*1024; + case 'M': /* fall thru */ + case 'm': return n * 1024*1024; + case 'K': /* fall thru */ + case 'k': return n * 1024; + } + + return n; +} + + +/* converts date offsets (e.g. "+3M") to seconds */ + +APREQ_DECLARE(apr_int64_t) apreq_atoi64t(const char *s) +{ + apr_int64_t n = 0; + char *p; + if (s == NULL) + return 0; + n = apr_strtoi64(s, &p, 0); /* XXX: what about overflow? */ + + if (p == NULL) + return n; + while (apr_isspace(*p)) + ++p; + + switch (*p) { + case 'Y': /* fall thru */ + case 'y': return n * 60*60*24*365; + case 'M': return n * 60*60*24*30; + case 'D': /* fall thru */ + case 'd': return n * 60*60*24; + case 'H': /* fall thru */ + case 'h': return n * 60*60; + case 'm': return n * 60; + case 's': /* fall thru */ + default: + return n; + } + /* should never get here */ + return -1; +} + + +APREQ_DECLARE(apr_ssize_t ) apreq_index(const char* hay, apr_size_t hlen, + const char* ndl, apr_size_t nlen, + const apreq_match_t type) +{ + apr_size_t len = hlen; + const char *end = hay + hlen; + const char *begin = hay; + + while ( (hay = memchr(hay, ndl[0], len)) ) { + len = end - hay; + + /* done if matches up to capacity of buffer */ + if ( memcmp(hay, ndl, MIN(nlen, len)) == 0 ) { + if (type == APREQ_MATCH_FULL && len < nlen) + hay = NULL; /* insufficient room for match */ + break; + } + --len; + ++hay; + } + + return hay ? hay - begin : -1; +} + + +static const char c2x_table[] = "0123456789ABCDEF"; +static APR_INLINE unsigned char hex2_to_char(const char *what) +{ + register unsigned char digit; + +#if !APR_CHARSET_EBCDIC + digit = (what[0] >= 'A' ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0')); +#else /*APR_CHARSET_EBCDIC*/ + char xstr[5]; + xstr[0]='0'; + xstr[1]='x'; + xstr[2]=what[0]; + xstr[3]=what[1]; + xstr[4]='\0'; + digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFF & strtol(xstr, NULL, 16)); +#endif /*APR_CHARSET_EBCDIC*/ + return (digit); +} + + +/* Unicode notes: "bmp" refers to the 16-bit + * Unicode Basic Multilingual Plane. Here we're + * restricting our unicode internals to 16-bit + * codepoints, to keep the code as simple as possible. + * This should be sufficient for apreq itself, since + * we really only need to validate RFC3986-encoded utf8. + */ + +/* Converts Windows cp1252 to Unicode. */ + +static APR_INLINE +apr_uint16_t cp1252_to_bmp(unsigned char c) +{ + /* We only need to deal with iso-8859-1 control chars + * in the 0x80 - 0x9F range. + */ + if ((c & 0xE0) != 0x80) + return c; + + switch (c) { + case 0x80: return 0x20AC; + case 0x82: return 0x201A; + case 0x83: return 0x192; + case 0x84: return 0x201E; + case 0x85: return 0x2026; + case 0x86: return 0x2020; + case 0x87: return 0x2021; + case 0x88: return 0x2C6; + case 0x89: return 0x2030; + case 0x8A: return 0x160; + case 0x8B: return 0x2039; + case 0x8C: return 0x152; + case 0x8E: return 0x17D; + case 0x91: return 0x2018; + case 0x92: return 0x2019; + case 0x93: return 0x201C; + case 0x94: return 0x201D; + case 0x95: return 0x2022; + case 0x96: return 0x2013; + case 0x97: return 0x2014; + case 0x98: return 0x2DC; + case 0x99: return 0x2122; + case 0x9A: return 0x161; + case 0x9B: return 0x203A; + case 0x9C: return 0x153; + case 0x9E: return 0x17E; + case 0x9F: return 0x178; + } + return c; +} + +/* converts cp1252 to utf8 */ +APREQ_DECLARE(apr_size_t) apreq_cp1252_to_utf8(char *dest, + const char *src, apr_size_t slen) +{ + const unsigned char *s = (unsigned const char *)src; + const unsigned char *end = s + slen; + unsigned char *d = (unsigned char *)dest; + apr_uint16_t c; + + while (s < end) { + c = cp1252_to_bmp(*s++); + + if (c < 0x80) { + *d++ = c; + } + else if (c < 0x800) { + *d++ = 0xC0 | (c >> 6); + *d++ = 0x80 | (c & 0x3F); + } + else { + *d++ = 0xE0 | (c >> 12); + *d++ = 0x80 | ((c >> 6) & 0x3F); + *d++ = 0x80 | (c & 0x3F); + } + } + *d = 0; + return d - (unsigned char *)dest; +} + + +/** + * Valid utf8 bit patterns: (true utf8 must satisfy a minimality condition) + * + * 0aaaaaaa + * 110bbbba 10aaaaaa minimality mask: 0x1E + * 1110cccc 10cbbbba 10aaaaaa 0x0F || 0x20 + * 11110ddd 10ddcccc 10cbbbba 10aaaaaa 0x07 || 0x30 + * 111110ee 10eeeddd 10ddcccc 10cbbbba 10aaaaaa 0x03 || 0x38 + * 1111110f 10ffffee 10eeeddd 10ddcccc 10cbbbba 10aaaaaa 0x01 || 0x3C + * + * Charset divination heuristics: + * 1) presume ascii; if not, then + * 2) presume utf8; if not, then + * 3) presume latin1; unless there are control chars, in which case + * 4) punt to cp1252. + * + * Note: in downgrading from 2 to 3, we need to be careful + * about earlier control characters presumed to be valid utf8. + */ + +APREQ_DECLARE(apreq_charset_t) apreq_charset_divine(const char *src, + apr_size_t slen) + +{ + apreq_charset_t rv = APREQ_CHARSET_ASCII; + register unsigned char trail = 0, saw_cntrl = 0, mask = 0; + register const unsigned char *s = (const unsigned char *)src; + const unsigned char *end = s + slen; + + for (; s < end; ++s) { + if (trail) { + if ((*s & 0xC0) == 0x80 && (mask == 0 || (mask & *s))) { + mask = 0; + --trail; + + if ((*s & 0xE0) == 0x80) { + saw_cntrl = 1; + } + } + else { + trail = 0; + if (saw_cntrl) + return APREQ_CHARSET_CP1252; + rv = APREQ_CHARSET_LATIN1; + } + } + else if (*s < 0x80) { + /* do nothing */ + } + else if (*s < 0xA0) { + return APREQ_CHARSET_CP1252; + } + else if (*s < 0xC0) { + if (saw_cntrl) + return APREQ_CHARSET_CP1252; + rv = APREQ_CHARSET_LATIN1; + } + else if (rv == APREQ_CHARSET_LATIN1) { + /* do nothing */ + } + + /* utf8 cases */ + + else if (*s < 0xE0) { + if (*s & 0x1E) { + rv = APREQ_CHARSET_UTF8; + trail = 1; + mask = 0; + } + else if (saw_cntrl) + return APREQ_CHARSET_CP1252; + else + rv = APREQ_CHARSET_LATIN1; + } + else if (*s < 0xF0) { + mask = (*s & 0x0F) ? 0 : 0x20; + rv = APREQ_CHARSET_UTF8; + trail = 2; + } + else if (*s < 0xF8) { + mask = (*s & 0x07) ? 0 : 0x30; + rv = APREQ_CHARSET_UTF8; + trail = 3; + } + else if (*s < 0xFC) { + mask = (*s & 0x03) ? 0 : 0x38; + rv = APREQ_CHARSET_UTF8; + trail = 4; + } + else if (*s < 0xFE) { + mask = (*s & 0x01) ? 0 : 0x3C; + rv = APREQ_CHARSET_UTF8; + trail = 5; + } + else { + rv = APREQ_CHARSET_UTF8; + } + } + + return trail ? saw_cntrl ? + APREQ_CHARSET_CP1252 : APREQ_CHARSET_LATIN1 : rv; +} + + +static APR_INLINE apr_uint16_t hex4_to_bmp(const char *what) { + register apr_uint16_t digit = 0; + +#if !APR_CHARSET_EBCDIC + digit = (what[0] >= 'A' ? ((what[0] & 0xDF)-'A') + 10 : (what[0]-'0')); + digit *= 16; + digit += (what[1] >= 'A' ? ((what[1] & 0xDF)-'A') + 10 : (what[1]-'0')); + digit *= 16; + digit += (what[2] >= 'A' ? ((what[2] & 0xDF)-'A') + 10 : (what[2]-'0')); + digit *= 16; + digit += (what[3] >= 'A' ? ((what[3] & 0xDF)-'A') + 10 : (what[3]-'0')); + +#else /*APR_CHARSET_EBCDIC*/ + char xstr[7]; + xstr[0]='0'; + xstr[1]='x'; + xstr[2]=what[0]; + xstr[3]=what[1]; + xstr[4]=what[2]; + xstr[5]=what[3]; + xstr[6]='\0'; + digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 0xFFFF & strtol(xstr, NULL, 16)); +#endif /*APR_CHARSET_EBCDIC*/ + return (digit); +} + + +static apr_status_t url_decode(char *dest, apr_size_t *dlen, + const char *src, apr_size_t *slen) +{ + register const char *s = src; + unsigned char *start = (unsigned char *)dest; + register unsigned char *d = (unsigned char *)dest; + const char *end = src + *slen; + + for (; s < end; ++d, ++s) { + switch (*s) { + + case '+': + *d = ' '; + break; + + case '%': + if (s + 2 < end && apr_isxdigit(s[1]) && apr_isxdigit(s[2])) + { + *d = hex2_to_char(s + 1); + s += 2; + } + else if (s + 5 < end && (s[1] == 'u' || s[1] == 'U') && + apr_isxdigit(s[2]) && apr_isxdigit(s[3]) && + apr_isxdigit(s[4]) && apr_isxdigit(s[5])) + { + apr_uint16_t c = hex4_to_bmp(s+2); + + if (c < 0x80) { + *d = c; + } + else if (c < 0x800) { + *d++ = 0xC0 | (c >> 6); + *d = 0x80 | (c & 0x3F); + } + else { + *d++ = 0xE0 | (c >> 12); + *d++ = 0x80 | ((c >> 6) & 0x3F); + *d = 0x80 | (c & 0x3F); + } + s += 5; + } + else { + *dlen = d - start; + *slen = s - src; + if (s + 5 < end + || (s + 2 < end && !apr_isxdigit(s[2])) + || (s + 1 < end && !apr_isxdigit(s[1]) + && s[1] != 'u' && s[1] != 'U')) + { + *d = 0; + return APREQ_ERROR_BADSEQ; + } + + memmove(d, s, end - s); + d[end - s] = 0; + return APR_INCOMPLETE; + } + break; + + default: + if (*s > 0) { + *d = *s; + } + else { + *d = 0; + *dlen = d - start; + *slen = s - src; + return APREQ_ERROR_BADCHAR; + } + } + } + + *d = 0; + *dlen = d - start; + *slen = s - src; + return APR_SUCCESS; +} + + +APREQ_DECLARE(apr_status_t) apreq_decode(char *d, apr_size_t *dlen, + const char *s, apr_size_t slen) +{ + apr_size_t len = 0; + const char *end = s + slen; + + if (s == (const char *)d) { /* optimize for src = dest case */ + for ( ; d < end; ++d) { + if (*d == '%' || *d == '+') + break; + else if (*d == 0) { + *dlen = (const char *)d - s; + return APREQ_ERROR_BADCHAR; + } + } + len = (const char *)d - s; + s = (const char *)d; + slen -= len; + } + + return url_decode(d, dlen, s, &slen); +} + +APREQ_DECLARE(apr_status_t) apreq_decodev(char *d, apr_size_t *dlen, + struct iovec *v, int nelts) +{ + apr_status_t status = APR_SUCCESS; + int n = 0; + + *dlen = 0; + + while (n < nelts) { + apr_size_t slen, len; + + slen = v[n].iov_len; + switch (status = url_decode(d, &len, v[n].iov_base, &slen)) { + + case APR_SUCCESS: + d += len; + *dlen += len; + ++n; + continue; + + case APR_INCOMPLETE: + d += len; + *dlen += len; + slen = v[n].iov_len - slen; + + if (++n == nelts) { + return status; + } + memcpy(d + slen, v[n].iov_base, v[n].iov_len); + v[n].iov_len += slen; + v[n].iov_base = d; + continue; + + default: + *dlen += len; + return status; + } + } + + return status; +} + + +APREQ_DECLARE(apr_size_t) apreq_encode(char *dest, const char *src, + const apr_size_t slen) +{ + char *d = dest; + const unsigned char *s = (const unsigned char *)src; + unsigned char c; + + for ( ; s < (const unsigned char *)src + slen; ++s) { + c = *s; + if ( c < 0x80 && (apr_isalnum(c) + || c == '-' || c == '.' + || c == '_' || c == '~') ) + *d++ = c; + + else if ( c == ' ' ) + *d++ = '+'; + + else { +#if APR_CHARSET_EBCDIC + c = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)c); +#endif + *d++ = '%'; + *d++ = c2x_table[c >> 4]; + *d++ = c2x_table[c & 0xf]; + } + } + *d = 0; + + return d - dest; +} + +static int is_quoted(const char *p, const apr_size_t len) { + if (len > 1 && p[0] == '"' && p[len-1] == '"') { + apr_size_t i; + int backslash = 0; + + for (i = 1; i < len - 1; i++) { + if (p[i] == '\\') + backslash = !backslash; + else if (p[i] == 0 || (p[i] == '"' && !backslash)) + return 0; + else + backslash = 0; + } + + return !backslash; + } + + return 0; +} + +APREQ_DECLARE(apr_size_t) apreq_quote_once(char *dest, const char *src, + const apr_size_t slen) +{ + if (is_quoted(src, slen)) { + /* looks like src is already quoted */ + memcpy(dest, src, slen); + dest[slen] = 0; + return slen; + } + else + return apreq_quote(dest, src, slen); +} + +APREQ_DECLARE(apr_size_t) apreq_quote(char *dest, const char *src, + const apr_size_t slen) +{ + char *d = dest; + const char *s = src; + const char *const last = src + slen - 1; + + if (slen == 0) { + *d = 0; + return 0; + } + + *d++ = '"'; + + while (s <= last) { + switch (*s) { + case 0: + *d++ = '\\'; + *d++ = '0'; + s++; + break; + + case '\\': + case '"': + *d++ = '\\'; + + default: + *d++ = *s++; + } + } + + *d++ = '"'; + *d = 0; + + return d - dest; +} + +APREQ_DECLARE(char *) apreq_join(apr_pool_t *p, + const char *sep, + const apr_array_header_t *arr, + apreq_join_t mode) +{ + apr_size_t len, slen; + char *rv; + const apreq_value_t **a = (const apreq_value_t **)arr->elts; + char *d; + const int n = arr->nelts; + int j; + + slen = sep ? strlen(sep) : 0; + + if (n == 0) + return apr_pstrdup(p, ""); + + for (j=0, len=0; j < n; ++j) + len += a[j]->dlen + slen + 1; + + /* Allocated the required space */ + + switch (mode) { + case APREQ_JOIN_ENCODE: + len += 2 * len; + break; + case APREQ_JOIN_QUOTE: + len = 2 * (len + n); + break; + case APREQ_JOIN_AS_IS: + case APREQ_JOIN_DECODE: + /* nothing special required, just here to keep noisy compilers happy */ + break; + } + + rv = apr_palloc(p, len); + + /* Pass two --- copy the argument strings into the result space */ + + d = rv; + + switch (mode) { + + case APREQ_JOIN_ENCODE: + d += apreq_encode(d, a[0]->data, a[0]->dlen); + + for (j = 1; j < n; ++j) { + memcpy(d, sep, slen); + d += slen; + d += apreq_encode(d, a[j]->data, a[j]->dlen); + } + break; + + case APREQ_JOIN_DECODE: + if (apreq_decode(d, &len, a[0]->data, a[0]->dlen)) + return NULL; + else + d += len; + + for (j = 1; j < n; ++j) { + memcpy(d, sep, slen); + d += slen; + + if (apreq_decode(d, &len, a[j]->data, a[j]->dlen)) + return NULL; + else + d += len; + } + break; + + + case APREQ_JOIN_QUOTE: + d += apreq_quote_once(d, a[0]->data, a[0]->dlen); + + for (j = 1; j < n; ++j) { + memcpy(d, sep, slen); + d += slen; + d += apreq_quote_once(d, a[j]->data, a[j]->dlen); + } + break; + + + case APREQ_JOIN_AS_IS: + memcpy(d,a[0]->data, a[0]->dlen); + d += a[0]->dlen; + + for (j = 1; j < n ; ++j) { + memcpy(d, sep, slen); + d += slen; + memcpy(d, a[j]->data, a[j]->dlen); + d += a[j]->dlen; + } + break; + } + + *d = 0; + return rv; +} + +/* + * This is intentionally not apr_file_writev() + * note, this is iterative and not recursive + */ +APR_INLINE +static apr_status_t apreq_fwritev(apr_file_t *f, struct iovec *v, + int *nelts, apr_size_t *bytes_written) +{ + apr_size_t len; + int n; + apr_status_t s; + + *bytes_written = 0; + + while (1) { + /* try to write */ + s = apr_file_writev(f, v, *nelts, &len); + + *bytes_written += len; + + if (s != APR_SUCCESS) + return s; + + /* see how far we've come */ + n = 0; + +#ifdef SOLARIS2 +# ifdef __GNUC__ + /* + * iovec.iov_len is a long here + * which causes a comparison between + * signed(long) and unsigned(apr_size_t) + * + */ + while (n < *nelts && len >= (apr_size_t)v[n].iov_len) +# else + /* + * Sun C however defines this as size_t which is unsigned + * + */ + while (n < *nelts && len >= v[n].iov_len) +# endif /* !__GNUC__ */ +#else + /* + * Hopefully everything else does this + * (this was the default for years) + */ + while (n < *nelts && len >= v[n].iov_len) +#endif + len -= v[n++].iov_len; + + if (n == *nelts) { + /* nothing left to write, report success */ + *nelts = 0; + return APR_SUCCESS; + } + + /* incomplete write: must shift v */ + v[n].iov_len -= len; + v[n].iov_base = (char *)(v[n].iov_base) + len; + + if (n > 0) { + /* we're satisfied for now if we can remove one iovec from + the "v" array */ + (*nelts) -= n; + memmove(v, v + n, sizeof(*v) * *nelts); + + return APR_SUCCESS; + } + + /* we're still in the first iovec - check for endless loop, + and then try again */ + if (len == 0) + return APREQ_ERROR_GENERAL; + } +} + + + + +struct cleanup_data { + const char *fname; + apr_pool_t *pool; +}; + +static apr_status_t apreq_file_cleanup(void *d) +{ + struct cleanup_data *data = d; + return apr_file_remove(data->fname, data->pool); +} + +/* + * The reason we need the above cleanup is because on Windows, APR_DELONCLOSE + * forces applications to open the file with FILE_SHARED_DELETE + * set, which is, unfortunately, a property that is preserved + * across NTFS "hard" links. This breaks apps that link() the temp + * file to a permanent location, and subsequently expect to open it + * before the original tempfile is closed+deleted. In fact, even + * Apache::Upload does this, so it is a common enough event that the + * apreq_file_cleanup workaround is necessary. + */ + +APREQ_DECLARE(apr_status_t) apreq_file_mktemp(apr_file_t **fp, + apr_pool_t *pool, + const char *path) +{ + apr_status_t rc; + char *tmpl; + struct cleanup_data *data; + apr_int32_t flag; + + if (path == NULL) { + rc = apr_temp_dir_get(&path, pool); + if (rc != APR_SUCCESS) + return rc; + } + rc = apr_filepath_merge(&tmpl, path, "apreqXXXXXX", + APR_FILEPATH_NOTRELATIVE, pool); + + if (rc != APR_SUCCESS) + return rc; + + data = apr_palloc(pool, sizeof *data); + /* cleanups are LIFO, so this one will run just after + the cleanup set by mktemp */ + apr_pool_cleanup_register(pool, data, + apreq_file_cleanup, apreq_file_cleanup); + + /* NO APR_DELONCLOSE! see comment above */ + flag = APR_CREATE | APR_READ | APR_WRITE | APR_EXCL | APR_BINARY; + + rc = apr_file_mktemp(fp, tmpl, flag, pool); + + if (rc == APR_SUCCESS) { + apr_file_name_get(&data->fname, *fp); + data->pool = pool; + } + else { + apr_pool_cleanup_kill(pool, data, apreq_file_cleanup); + } + + return rc; +} + + +/* + * is_2616_token() is the verbatim definition from section 2.2 + * in the rfc itself. We try to optimize it around the + * expectation that the argument is not a token, which + * should be the typical usage. + */ + +static APR_INLINE +unsigned is_2616_token(const char c) { + switch (c) { + case ' ': case ';': case ',': case '"': case '\t': + /* The chars we are expecting are listed above; + the chars below are just for completeness. */ + case '?': case '=': case '@': case ':': case '\\': case '/': + case '(': case ')': + case '<': case '>': + case '{': case '}': + case '[': case ']': + return 0; + default: + if (apr_iscntrl(c)) + return 0; + } + return 1; +} + +APREQ_DECLARE(apr_status_t) + apreq_header_attribute(const char *hdr, + const char *name, const apr_size_t nlen, + const char **val, apr_size_t *vlen) +{ + const char *key, *v; + + /* Must ensure first char isn't '=', so we can safely backstep. */ + while (*hdr == '=') + ++hdr; + + while ((key = strchr(hdr, '=')) != NULL) { + + v = key + 1; + --key; + + while (apr_isspace(*key) && key > hdr + nlen) + --key; + + key -= nlen - 1; + + while (apr_isspace(*v)) + ++v; + + if (*v == '"') { + ++v; + *val = v; + + look_for_end_quote: + switch (*v) { + case '"': + break; + case 0: + return APREQ_ERROR_BADSEQ; + case '\\': + if (v[1] != 0) + ++v; + default: + ++v; + goto look_for_end_quote; + } + } + else { + *val = v; + + look_for_terminator: + switch (*v) { + case 0: + case ' ': + case ';': + case ',': + case '\t': + case '\r': + case '\n': + break; + default: + ++v; + goto look_for_terminator; + } + } + + if (key >= hdr && strncasecmp(key, name, nlen) == 0) { + *vlen = v - *val; + if (key == hdr || ! is_2616_token(key[-1])) + return APR_SUCCESS; + } + hdr = v; + } + + return APREQ_ERROR_NOATTR; +} + + + +#define BUCKET_IS_SPOOL(e) ((e)->type == &spool_bucket_type) +#define FILE_BUCKET_LIMIT ((apr_size_t)-1 - 1) + +static +void spool_bucket_destroy(void *data) +{ + apr_bucket_type_file.destroy(data); +} + +static +apr_status_t spool_bucket_read(apr_bucket *e, const char **str, + apr_size_t *len, apr_read_type_e block) +{ + return apr_bucket_type_file.read(e, str, len, block); +} + +static +apr_status_t spool_bucket_setaside(apr_bucket *data, apr_pool_t *reqpool) +{ + return apr_bucket_type_file.setaside(data, reqpool); +} + +static +apr_status_t spool_bucket_split(apr_bucket *a, apr_size_t point) +{ + apr_status_t rv = apr_bucket_shared_split(a, point); + a->type = &apr_bucket_type_file; + return rv; +} + +static +apr_status_t spool_bucket_copy(apr_bucket *e, apr_bucket **c) +{ + apr_status_t rv = apr_bucket_shared_copy(e, c); + (*c)->type = &apr_bucket_type_file; + return rv; +} + +static const apr_bucket_type_t spool_bucket_type = { + "APREQ_SPOOL", 5, APR_BUCKET_DATA, + spool_bucket_destroy, + spool_bucket_read, + spool_bucket_setaside, + spool_bucket_split, + spool_bucket_copy, +}; + +APREQ_DECLARE(apr_file_t *)apreq_brigade_spoolfile(apr_bucket_brigade *bb) +{ + apr_bucket *last; + + last = APR_BRIGADE_LAST(bb); + if (BUCKET_IS_SPOOL(last)) + return ((apr_bucket_file *)last->data)->fd; + + return NULL; +} + +APREQ_DECLARE(apr_status_t) apreq_brigade_concat(apr_pool_t *pool, + const char *temp_dir, + apr_size_t heap_limit, + apr_bucket_brigade *out, + apr_bucket_brigade *in) +{ + apr_status_t s; + apr_bucket_file *f; + apr_off_t wlen; + apr_file_t *file; + apr_off_t in_len, out_len; + apr_bucket *last_in, *last_out; + + last_out = APR_BRIGADE_LAST(out); + + if (APR_BUCKET_IS_EOS(last_out)) + return APR_EOF; + + s = apr_brigade_length(out, 0, &out_len); + if (s != APR_SUCCESS) + return s; + + /* This cast, when out_len = -1, is intentional */ + if ((apr_uint64_t)out_len < heap_limit) { + + s = apr_brigade_length(in, 0, &in_len); + if (s != APR_SUCCESS) + return s; + + /* This cast, when in_len = -1, is intentional */ + if ((apr_uint64_t)in_len < heap_limit - (apr_uint64_t)out_len) { + APR_BRIGADE_CONCAT(out, in); + return APR_SUCCESS; + } + } + + if (!BUCKET_IS_SPOOL(last_out)) { + + s = apreq_file_mktemp(&file, pool, temp_dir); + if (s != APR_SUCCESS) + return s; + + s = apreq_brigade_fwrite(file, &wlen, out); + + if (s != APR_SUCCESS) + return s; + + last_out = apr_bucket_file_create(file, wlen, 0, + out->p, out->bucket_alloc); + last_out->type = &spool_bucket_type; + APR_BRIGADE_INSERT_TAIL(out, last_out); + f = last_out->data; + } + else { + f = last_out->data; + /* Need to seek here, just in case our spool bucket + * was read from between apreq_brigade_concat calls. + */ + wlen = last_out->start + last_out->length; + s = apr_file_seek(f->fd, APR_SET, &wlen); + if (s != APR_SUCCESS) + return s; + } + + if (in == out) + return APR_SUCCESS; + + last_in = APR_BRIGADE_LAST(in); + + if (APR_BUCKET_IS_EOS(last_in)) + APR_BUCKET_REMOVE(last_in); + + s = apreq_brigade_fwrite(f->fd, &wlen, in); + + if (s == APR_SUCCESS) { + + /* We have to deal with the possibility that the new + * data may be too large to be represented by a single + * temp_file bucket. + */ + + while ((apr_uint64_t)wlen > FILE_BUCKET_LIMIT - last_out->length) { + apr_bucket *e; + + apr_bucket_copy(last_out, &e); + e->length = 0; + e->start = last_out->start + FILE_BUCKET_LIMIT; + wlen -= FILE_BUCKET_LIMIT - last_out->length; + last_out->length = FILE_BUCKET_LIMIT; + + /* Copying makes the bucket types exactly the + * opposite of what we need here. + */ + last_out->type = &apr_bucket_type_file; + e->type = &spool_bucket_type; + + APR_BRIGADE_INSERT_TAIL(out, e); + last_out = e; + } + + last_out->length += wlen; + + if (APR_BUCKET_IS_EOS(last_in)) + APR_BRIGADE_INSERT_TAIL(out, last_in); + + } + else if (APR_BUCKET_IS_EOS(last_in)) + APR_BRIGADE_INSERT_TAIL(in, last_in); + + apr_brigade_cleanup(in); + return s; +} + +APREQ_DECLARE(apr_status_t) apreq_brigade_fwrite(apr_file_t *f, + apr_off_t *wlen, + apr_bucket_brigade *bb) +{ + struct iovec v[APREQ_DEFAULT_NELTS]; + apr_status_t s; + apr_bucket *e, *first; + int n = 0; + apr_bucket_brigade *tmp = bb; + *wlen = 0; + + if (BUCKET_IS_SPOOL(APR_BRIGADE_LAST(bb))) { + tmp = apr_brigade_create(bb->p, bb->bucket_alloc); + + s = apreq_brigade_copy(tmp, bb); + if (s != APR_SUCCESS) + return s; + } + + for (e = APR_BRIGADE_FIRST(tmp); e != APR_BRIGADE_SENTINEL(tmp); + e = APR_BUCKET_NEXT(e)) + { + apr_size_t len; + if (n == APREQ_DEFAULT_NELTS) { + s = apreq_fwritev(f, v, &n, &len); + if (s != APR_SUCCESS) + return s; + + if (tmp != bb) { + while ((first = APR_BRIGADE_FIRST(tmp)) != e) + apr_bucket_delete(first); + } + + *wlen += len; + } + s = apr_bucket_read(e, (const char **)&(v[n].iov_base), + &len, APR_BLOCK_READ); + if (s != APR_SUCCESS) + return s; + + v[n++].iov_len = len; + } + + while (n > 0) { + apr_size_t len; + s = apreq_fwritev(f, v, &n, &len); + if (s != APR_SUCCESS) + return s; + *wlen += len; + + if (tmp != bb) { + while ((first = APR_BRIGADE_FIRST(tmp)) != e) + apr_bucket_delete(first); + } + } + return APR_SUCCESS; +} diff --git a/srclib/libapreq/library/version.c b/srclib/libapreq/library/version.c new file mode 100644 index 0000000000..05ebca54b3 --- /dev/null +++ b/srclib/libapreq/library/version.c @@ -0,0 +1,105 @@ +/* +** 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_version.h" +#include "at.h" + +static void version_string(dAT) +{ + const char *vstring = apreq_version_string(); + AT_not_null(vstring); + AT_str_eq(vstring, APREQ_VERSION_STRING); +} +static void version_type(dAT) +{ + apr_version_t v; + apreq_version(&v); + AT_int_eq(v.major, APREQ_MAJOR_VERSION); + AT_int_eq(v.minor, APREQ_MINOR_VERSION); + AT_int_eq(v.patch, APREQ_PATCH_VERSION); +#ifdef APREQ_IS_DEV_VERSION + AT_int_eq(v.is_dev, 1); +#else + AT_int_eq(v.is_dev, 0); +#endif +} + +int main(int argc, char *argv[]) +{ + apr_pool_t *p; + unsigned i, plan = 0; + dAT; + at_test_t test_list [] = { + {"version_string", version_string, 2, "1"}, + {"version_type", version_type, 4} + }; + + apr_initialize(); + atexit(apr_terminate); + + apr_pool_create(&p, NULL); + + AT = at_create(p, 0, at_report_stdout_make(p)); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + plan += test_list[i].plan; + + AT_begin(plan); + + for (i = 0; i < sizeof(test_list) / sizeof(at_test_t); ++i) + AT_run(&test_list[i]); + + AT_end(); + + return 0; +} +/* +** 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_version.h" +#include "apr_general.h" /* for APR_STRINGIFY */ + +APREQ_DECLARE(void) apreq_version(apr_version_t *pvsn) +{ + pvsn->major = APREQ_MAJOR_VERSION; + pvsn->minor = APREQ_MINOR_VERSION; + pvsn->patch = APREQ_PATCH_VERSION; +#ifdef APREQ_IS_DEV_VERSION + pvsn->is_dev = 1; +#else + pvsn->is_dev = 0; +#endif +} + +APREQ_DECLARE(const char *) apreq_version_string(void) +{ + return APREQ_VERSION_STRING; +} |