| /* |
| ** 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 "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; |
| apreq_hook_find_param_ctx_t *hook_ctx; |
| |
| 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 */ |
| hook_ctx = apr_palloc(handle->pool, sizeof *hook_ctx); |
| |
| 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; |
| h->ctx = hook_ctx; |
| ctx->parser->hook = h; |
| hook_ctx->name = name; |
| hook_ctx->param = NULL; |
| hook_ctx->prev = ctx->parser->hook; |
| |
| do { |
| apreq_filter_prefetch(f, APREQ_DEFAULT_READ_BLOCK_SIZE); |
| if (hook_ctx->param != NULL) |
| return hook_ctx->param; |
| } 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; |
| |
| } |