|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_hash.h"
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
#include "http_log.h"
|
|
Packit |
90a5c9 |
#include "util_filter.h"
|
|
Packit |
90a5c9 |
#include "ap_expr.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
module AP_MODULE_DECLARE_DATA filter_module;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* @brief is a filter provider, as defined and implemented by mod_filter.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The struct is a linked list, with dispatch criteria
|
|
Packit |
90a5c9 |
* defined for each filter. The provider implementation itself is a
|
|
Packit |
90a5c9 |
* (2.0-compatible) ap_filter_rec_t* frec.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
struct ap_filter_provider_t {
|
|
Packit |
90a5c9 |
ap_expr_info_t *expr;
|
|
Packit |
90a5c9 |
const char **types;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/** The filter that implements this provider */
|
|
Packit |
90a5c9 |
ap_filter_rec_t *frec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/** The next provider in the list */
|
|
Packit |
90a5c9 |
ap_filter_provider_t *next;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/** we need provider_ctx to save ctx values set by providers in filter_init */
|
|
Packit |
90a5c9 |
typedef struct provider_ctx provider_ctx;
|
|
Packit |
90a5c9 |
struct provider_ctx {
|
|
Packit |
90a5c9 |
ap_filter_provider_t *provider;
|
|
Packit |
90a5c9 |
void *ctx;
|
|
Packit |
90a5c9 |
provider_ctx *next;
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
ap_out_filter_func func;
|
|
Packit |
90a5c9 |
void *fctx;
|
|
Packit |
90a5c9 |
provider_ctx *init_ctx;
|
|
Packit |
90a5c9 |
} harness_ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct mod_filter_chain {
|
|
Packit |
90a5c9 |
const char *fname;
|
|
Packit |
90a5c9 |
struct mod_filter_chain *next;
|
|
Packit |
90a5c9 |
} mod_filter_chain;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
apr_hash_t *live_filters;
|
|
Packit |
90a5c9 |
mod_filter_chain *chain;
|
|
Packit |
90a5c9 |
} mod_filter_cfg;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
typedef struct {
|
|
Packit |
90a5c9 |
const char* range ;
|
|
Packit |
90a5c9 |
} mod_filter_ctx ;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void filter_trace(conn_rec *c, int debug, const char *fname,
|
|
Packit |
90a5c9 |
apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_bucket *b;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (debug) {
|
|
Packit |
90a5c9 |
case 0: /* normal, operational use */
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
case 1: /* mod_diagnostics level */
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01375) "%s", fname);
|
|
Packit |
90a5c9 |
for (b = APR_BRIGADE_FIRST(bb);
|
|
Packit |
90a5c9 |
b != APR_BRIGADE_SENTINEL(bb);
|
|
Packit |
90a5c9 |
b = APR_BUCKET_NEXT(b)) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01376)
|
|
Packit |
90a5c9 |
"%s: type: %s, length: %" APR_SIZE_T_FMT,
|
|
Packit |
90a5c9 |
fname, b->type->name ? b->type->name : "(unknown)",
|
|
Packit |
90a5c9 |
b->length);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int filter_init(ap_filter_t *f)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_filter_provider_t *p;
|
|
Packit |
90a5c9 |
provider_ctx *pctx;
|
|
Packit |
90a5c9 |
int err;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *filter = f->frec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
|
|
Packit |
90a5c9 |
for (p = filter->providers; p; p = p->next) {
|
|
Packit |
90a5c9 |
if (p->frec->filter_init_func == filter_init) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01377)
|
|
Packit |
90a5c9 |
"Chaining of FilterProviders not supported");
|
|
Packit |
90a5c9 |
return HTTP_INTERNAL_SERVER_ERROR;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (p->frec->filter_init_func) {
|
|
Packit |
90a5c9 |
f->ctx = NULL;
|
|
Packit |
90a5c9 |
if ((err = p->frec->filter_init_func(f)) != OK) {
|
|
Packit |
90a5c9 |
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01378)
|
|
Packit |
90a5c9 |
"filter_init for %s failed", p->frec->name);
|
|
Packit |
90a5c9 |
return err; /* if anyone errors out here, so do we */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (f->ctx != NULL) {
|
|
Packit |
90a5c9 |
/* the filter init function set a ctx - we need to record it */
|
|
Packit |
90a5c9 |
pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
|
|
Packit |
90a5c9 |
pctx->provider = p;
|
|
Packit |
90a5c9 |
pctx->ctx = f->ctx;
|
|
Packit |
90a5c9 |
pctx->next = fctx->init_ctx;
|
|
Packit |
90a5c9 |
fctx->init_ctx = pctx;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
f->ctx = fctx;
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_filter_provider_t *provider;
|
|
Packit |
90a5c9 |
int match = 0;
|
|
Packit |
90a5c9 |
const char *err = NULL;
|
|
Packit |
90a5c9 |
request_rec *r = f->r;
|
|
Packit |
90a5c9 |
harness_ctx *ctx = f->ctx;
|
|
Packit |
90a5c9 |
provider_ctx *pctx;
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
unsigned int proto_flags;
|
|
Packit |
90a5c9 |
mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
|
|
Packit |
90a5c9 |
&filter_module);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check registered providers in order */
|
|
Packit |
90a5c9 |
for (provider = filter->providers; provider; provider = provider->next) {
|
|
Packit |
90a5c9 |
if (provider->expr) {
|
|
Packit |
90a5c9 |
match = ap_expr_exec(r, provider->expr, &err;;
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
|
|
Packit |
90a5c9 |
"Error evaluating filter dispatch condition: %s",
|
|
Packit |
90a5c9 |
err);
|
|
Packit |
90a5c9 |
match = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
Packit |
90a5c9 |
"Expression condition for '%s' %s",
|
|
Packit |
90a5c9 |
provider->frec->name,
|
|
Packit |
90a5c9 |
match ? "matched" : "did not match");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (r->content_type) {
|
|
Packit |
90a5c9 |
const char **type = provider->types;
|
|
Packit |
90a5c9 |
size_t len = strcspn(r->content_type, "; \t");
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(type != NULL);
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
|
|
Packit |
90a5c9 |
"Content-Type '%s' ...", r->content_type);
|
|
Packit |
90a5c9 |
while (*type) {
|
|
Packit |
90a5c9 |
/* Handle 'content-type;charset=...' correctly */
|
|
Packit |
90a5c9 |
if (strncmp(*type, r->content_type, len) == 0
|
|
Packit |
90a5c9 |
&& (*type)[len] == '\0') {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
|
|
Packit |
90a5c9 |
"... matched '%s'", *type);
|
|
Packit |
90a5c9 |
match = 1;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
|
|
Packit |
90a5c9 |
"... did not match '%s'", *type);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
type++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
Packit |
90a5c9 |
"Content-Type condition for '%s' %s",
|
|
Packit |
90a5c9 |
provider->frec->name,
|
|
Packit |
90a5c9 |
match ? "matched" : "did not match");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
|
|
Packit |
90a5c9 |
"Content-Type condition for '%s' did not match: "
|
|
Packit |
90a5c9 |
"no Content-Type", provider->frec->name);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (match) {
|
|
Packit |
90a5c9 |
/* condition matches this provider */
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
/* check protocol
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* FIXME:
|
|
Packit |
90a5c9 |
* This is a quick hack and almost certainly buggy.
|
|
Packit |
90a5c9 |
* The idea is that by putting this in mod_filter, we relieve
|
|
Packit |
90a5c9 |
* filter implementations of the burden of fixing up HTTP headers
|
|
Packit |
90a5c9 |
* for cases that are routinely affected by filters.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Default is ALWAYS to do nothing, so as not to tread on the
|
|
Packit |
90a5c9 |
* toes of filters which want to do it themselves.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
proto_flags = provider->frec->proto_flags;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* some specific things can't happen in a proxy */
|
|
Packit |
90a5c9 |
if (r->proxyreq) {
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
|
|
Packit |
90a5c9 |
/* can't use this provider; try next */
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
|
|
Packit |
90a5c9 |
const char *str = apr_table_get(r->headers_out,
|
|
Packit |
90a5c9 |
"Cache-Control");
|
|
Packit |
90a5c9 |
if (str) {
|
|
Packit |
90a5c9 |
if (ap_strcasestr(str, "no-transform")) {
|
|
Packit |
90a5c9 |
/* can't use this provider; try next */
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_table_addn(r->headers_out, "Warning",
|
|
Packit |
90a5c9 |
apr_psprintf(r->pool,
|
|
Packit |
90a5c9 |
"214 %s Transformation applied",
|
|
Packit |
90a5c9 |
r->hostname));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* things that are invalidated if the filter transforms content */
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_CHANGE) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Content-MD5");
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "ETag");
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Content-Length");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* no-cache is for a filter that has different effect per-hit */
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_out, "Last-Modified");
|
|
Packit |
90a5c9 |
apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Accept-Ranges", "none");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (rctx && rctx->range) {
|
|
Packit |
90a5c9 |
/* restore range header we saved earlier */
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_in, "Range", rctx->range);
|
|
Packit |
90a5c9 |
rctx->range = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
|
|
Packit |
90a5c9 |
if (pctx->provider == provider) {
|
|
Packit |
90a5c9 |
ctx->fctx = pctx->ctx ;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ctx->func = provider->frec->filter_func.out_func;
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* No provider matched */
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_status_t ret;
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
const char *cachecontrol;
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
harness_ctx *ctx = f->ctx;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *filter = f->frec;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (f->r->status != 200
|
|
Packit |
90a5c9 |
&& !apr_table_get(f->r->subprocess_env, "filter-errordocs")) {
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
filter_trace(f->c, filter->debug, f->frec->name, bb);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* look up a handler function if we haven't already set it */
|
|
Packit |
90a5c9 |
if (!ctx->func) {
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
if (f->r->proxyreq) {
|
|
Packit |
90a5c9 |
if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
|
|
Packit |
90a5c9 |
cachecontrol = apr_table_get(f->r->headers_out,
|
|
Packit |
90a5c9 |
"Cache-Control");
|
|
Packit |
90a5c9 |
if (cachecontrol) {
|
|
Packit |
90a5c9 |
if (ap_strcasestr(cachecontrol, "no-transform")) {
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
if (!filter_lookup(f, filter)) {
|
|
Packit |
90a5c9 |
ap_remove_output_filter(f);
|
|
Packit |
90a5c9 |
return ap_pass_brigade(f->next, bb);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
AP_DEBUG_ASSERT(ctx->func != NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* call the content filter with its own context, then restore our
|
|
Packit |
90a5c9 |
* context
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
f->ctx = ctx->fctx;
|
|
Packit |
90a5c9 |
ret = ctx->func(f, bb);
|
|
Packit |
90a5c9 |
ctx->fctx = f->ctx;
|
|
Packit |
90a5c9 |
f->ctx = ctx;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return ret;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
|
|
Packit |
90a5c9 |
const char *pname, const char *proto)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
static const char *sep = ";, \t";
|
|
Packit |
90a5c9 |
char *arg;
|
|
Packit |
90a5c9 |
char *tok = 0;
|
|
Packit |
90a5c9 |
unsigned int flags = 0;
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = CFG;
|
|
Packit |
90a5c9 |
ap_filter_provider_t *provider = NULL;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
|
|
Packit |
90a5c9 |
APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!filter) {
|
|
Packit |
90a5c9 |
return "FilterProtocol: No such filter";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Fixup the args: it's really pname that's optional */
|
|
Packit |
90a5c9 |
if (proto == NULL) {
|
|
Packit |
90a5c9 |
proto = pname;
|
|
Packit |
90a5c9 |
pname = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* Find provider */
|
|
Packit |
90a5c9 |
for (provider = filter->providers; provider; provider = provider->next) {
|
|
Packit |
90a5c9 |
if (!strcasecmp(provider->frec->name, pname)) {
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!provider) {
|
|
Packit |
90a5c9 |
return "FilterProtocol: No such provider for this filter";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Now set flags from our args */
|
|
Packit |
90a5c9 |
for (arg = apr_strtok(apr_pstrdup(cmd->temp_pool, proto), sep, &tok;;
|
|
Packit |
90a5c9 |
arg; arg = apr_strtok(NULL, sep, &tok)) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!strcasecmp(arg, "change=yes")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (!strcasecmp(arg, "change=no")) {
|
|
Packit |
90a5c9 |
flags &= ~(AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(arg, "change=1:1")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_CHANGE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(arg, "byteranges=no")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_NO_BYTERANGE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(arg, "proxy=no")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_NO_PROXY;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(arg, "proxy=transform")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_TRANSFORM;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(arg, "cache=no")) {
|
|
Packit |
90a5c9 |
flags |= AP_FILTER_PROTO_NO_CACHE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (pname) {
|
|
Packit |
90a5c9 |
provider->frec->proto_flags = flags;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
filter->proto_flags = flags;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
|
|
Packit |
90a5c9 |
const char *place)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *filter;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
|
|
Packit |
90a5c9 |
apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
filter->name = fname;
|
|
Packit |
90a5c9 |
filter->filter_init_func = filter_init;
|
|
Packit |
90a5c9 |
filter->filter_func.out_func = filter_harness;
|
|
Packit |
90a5c9 |
filter->ftype = AP_FTYPE_RESOURCE;
|
|
Packit |
90a5c9 |
filter->next = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (place) {
|
|
Packit |
90a5c9 |
if (!strcasecmp(place, "CONTENT_SET")) {
|
|
Packit |
90a5c9 |
filter->ftype = AP_FTYPE_CONTENT_SET;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(place, "PROTOCOL")) {
|
|
Packit |
90a5c9 |
filter->ftype = AP_FTYPE_PROTOCOL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(place, "CONNECTION")) {
|
|
Packit |
90a5c9 |
filter->ftype = AP_FTYPE_CONNECTION;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!strcasecmp(place, "NETWORK")) {
|
|
Packit |
90a5c9 |
filter->ftype = AP_FTYPE_NETWORK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *add_filter(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *fname, const char *pname,
|
|
Packit |
90a5c9 |
const char *expr, const char **types)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = CFG;
|
|
Packit |
90a5c9 |
ap_filter_provider_t *provider;
|
|
Packit |
90a5c9 |
const char *c;
|
|
Packit |
90a5c9 |
ap_filter_rec_t* frec;
|
|
Packit |
90a5c9 |
ap_filter_rec_t* provider_frec;
|
|
Packit |
90a5c9 |
ap_expr_info_t *node;
|
|
Packit |
90a5c9 |
const char *err = NULL;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* if provider has been registered, we can look it up */
|
|
Packit |
90a5c9 |
provider_frec = ap_get_output_filter_handle(pname);
|
|
Packit |
90a5c9 |
if (!provider_frec) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* fname has been declared with DeclareFilter, so we can look it up */
|
|
Packit |
90a5c9 |
frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* or if provider is mod_filter itself, we can also look it up */
|
|
Packit |
90a5c9 |
if (!frec) {
|
|
Packit |
90a5c9 |
c = filter_declare(cmd, CFG, fname, NULL);
|
|
Packit |
90a5c9 |
if ( c ) {
|
|
Packit |
90a5c9 |
return c;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
frec->ftype = provider_frec->ftype;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!frec) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
|
|
Packit |
90a5c9 |
if (expr) {
|
|
Packit |
90a5c9 |
node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
|
|
Packit |
90a5c9 |
if (err) {
|
|
Packit |
90a5c9 |
return apr_pstrcat(cmd->pool,
|
|
Packit |
90a5c9 |
"Error parsing FilterProvider expression:", err,
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
provider->expr = node;
|
|
Packit |
90a5c9 |
provider->types = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
provider->types = types;
|
|
Packit |
90a5c9 |
provider->expr = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
provider->frec = provider_frec;
|
|
Packit |
90a5c9 |
provider->next = frec->providers;
|
|
Packit |
90a5c9 |
frec->providers = provider;
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_provider(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *fname, const char *pname,
|
|
Packit |
90a5c9 |
const char *expr)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return add_filter(cmd, CFG, fname, pname, expr, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_chain *p;
|
|
Packit |
90a5c9 |
mod_filter_chain *q;
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = CFG;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (arg[0]) {
|
|
Packit |
90a5c9 |
case '+': /* add to end of chain */
|
|
Packit |
90a5c9 |
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->fname = arg+1;
|
|
Packit |
90a5c9 |
if (cfg->chain) {
|
|
Packit |
90a5c9 |
for (q = cfg->chain; q->next; q = q->next);
|
|
Packit |
90a5c9 |
q->next = p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
cfg->chain = p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '@': /* add to start of chain */
|
|
Packit |
90a5c9 |
p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->fname = arg+1;
|
|
Packit |
90a5c9 |
p->next = cfg->chain;
|
|
Packit |
90a5c9 |
cfg->chain = p;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '-': /* remove from chain */
|
|
Packit |
90a5c9 |
if (cfg->chain) {
|
|
Packit |
90a5c9 |
if (strcasecmp(cfg->chain->fname, arg+1)) {
|
|
Packit |
90a5c9 |
for (p = cfg->chain; p->next; p = p->next) {
|
|
Packit |
90a5c9 |
if (!strcasecmp(p->next->fname, arg+1)) {
|
|
Packit |
90a5c9 |
p->next = p->next->next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
cfg->chain = cfg->chain->next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '!': /* Empty the chain */
|
|
Packit |
90a5c9 |
/** IG: Add a NULL provider to the beginning so that
|
|
Packit |
90a5c9 |
* we can ensure that we'll empty everything before
|
|
Packit |
90a5c9 |
* this when doing config merges later */
|
|
Packit |
90a5c9 |
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->fname = NULL;
|
|
Packit |
90a5c9 |
cfg->chain = p;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case '=': /* initialise chain with this arg */
|
|
Packit |
90a5c9 |
/** IG: Prepend a NULL provider to the beginning as above */
|
|
Packit |
90a5c9 |
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->fname = NULL;
|
|
Packit |
90a5c9 |
p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->next->fname = arg+1;
|
|
Packit |
90a5c9 |
cfg->chain = p;
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default: /* add to end */
|
|
Packit |
90a5c9 |
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
p->fname = arg;
|
|
Packit |
90a5c9 |
if (cfg->chain) {
|
|
Packit |
90a5c9 |
for (q = cfg->chain; q->next; q = q->next);
|
|
Packit |
90a5c9 |
q->next = p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
cfg->chain = p;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
break;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_bytype1(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
const char *pname, const char **types)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *rv;
|
|
Packit |
90a5c9 |
const char *fname;
|
|
Packit |
90a5c9 |
int seen_name = 0;
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = CFG;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* construct fname from name */
|
|
Packit |
90a5c9 |
fname = apr_pstrcat(cmd->pool, "BYTYPE:", pname, NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* check whether this is already registered, in which case
|
|
Packit |
90a5c9 |
* it's already in the filter chain
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING)) {
|
|
Packit |
90a5c9 |
seen_name = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rv = add_filter(cmd, CFG, fname, pname, NULL, types);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If it's the first time through, add to filterchain */
|
|
Packit |
90a5c9 |
if (rv == NULL && !seen_name) {
|
|
Packit |
90a5c9 |
rv = filter_chain(cmd, CFG, fname);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_bytype(cmd_parms *cmd, void *CFG,
|
|
Packit |
90a5c9 |
int argc, char *const argv[])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* back compatibility, need to parse multiple components in filter name */
|
|
Packit |
90a5c9 |
char *pname;
|
|
Packit |
90a5c9 |
char *strtok_state = NULL;
|
|
Packit |
90a5c9 |
char *name;
|
|
Packit |
90a5c9 |
const char **types;
|
|
Packit |
90a5c9 |
const char *rv = NULL;
|
|
Packit |
90a5c9 |
if (argc < 2)
|
|
Packit |
90a5c9 |
return "AddOutputFilterByType requires at least two arguments";
|
|
Packit |
90a5c9 |
name = apr_pstrdup(cmd->temp_pool, argv[0]);
|
|
Packit |
90a5c9 |
types = apr_palloc(cmd->pool, argc * sizeof(char *));
|
|
Packit |
90a5c9 |
memcpy(types, &argv[1], (argc - 1) * sizeof(char *));
|
|
Packit |
90a5c9 |
types[argc-1] = NULL;
|
|
Packit |
90a5c9 |
for (pname = apr_strtok(name, ";", &strtok_state);
|
|
Packit |
90a5c9 |
pname != NULL && rv == NULL;
|
|
Packit |
90a5c9 |
pname = apr_strtok(NULL, ";", &strtok_state)) {
|
|
Packit |
90a5c9 |
rv = filter_bytype1(cmd, CFG, pname, types);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return rv;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
|
|
Packit |
90a5c9 |
const char *level)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = CFG;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
|
|
Packit |
90a5c9 |
APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
if (!frec) {
|
|
Packit |
90a5c9 |
return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
frec->debug = atoi(level);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void filter_insert(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_chain *p;
|
|
Packit |
90a5c9 |
ap_filter_rec_t *filter;
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
|
|
Packit |
90a5c9 |
&filter_module);
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
int ranges = 1;
|
|
Packit |
90a5c9 |
mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
|
|
Packit |
90a5c9 |
ap_set_module_config(r->request_config, &filter_module, ctx);
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/** IG: Now that we've merged to the final config, go one last time
|
|
Packit |
90a5c9 |
* through the chain, and prune out the NULL filters */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (p = cfg->chain; p; p = p->next) {
|
|
Packit |
90a5c9 |
if (p->fname == NULL)
|
|
Packit |
90a5c9 |
cfg->chain = p->next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (p = cfg->chain; p; p = p->next) {
|
|
Packit |
90a5c9 |
filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
if (filter == NULL) {
|
|
Packit |
90a5c9 |
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380)
|
|
Packit |
90a5c9 |
"Unknown filter %s not added", p->fname);
|
|
Packit |
90a5c9 |
continue;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_add_output_filter_handle(filter, NULL, r, r->connection);
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
if (ranges && (filter->proto_flags
|
|
Packit |
90a5c9 |
& (AP_FILTER_PROTO_NO_BYTERANGE
|
|
Packit |
90a5c9 |
| AP_FILTER_PROTO_CHANGE_LENGTH))) {
|
|
Packit |
90a5c9 |
ctx->range = apr_table_get(r->headers_in, "Range");
|
|
Packit |
90a5c9 |
apr_table_unset(r->headers_in, "Range");
|
|
Packit |
90a5c9 |
ranges = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void filter_hooks(apr_pool_t *pool)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *filter_config(apr_pool_t *pool, char *x)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
|
|
Packit |
90a5c9 |
cfg->live_filters = apr_hash_make(pool);
|
|
Packit |
90a5c9 |
cfg->chain = NULL;
|
|
Packit |
90a5c9 |
return cfg;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
mod_filter_cfg *base = BASE;
|
|
Packit |
90a5c9 |
mod_filter_cfg *add = ADD;
|
|
Packit |
90a5c9 |
mod_filter_chain *savelink = 0;
|
|
Packit |
90a5c9 |
mod_filter_chain *newlink;
|
|
Packit |
90a5c9 |
mod_filter_chain *p;
|
|
Packit |
90a5c9 |
mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
conf->live_filters = apr_hash_overlay(pool, add->live_filters,
|
|
Packit |
90a5c9 |
base->live_filters);
|
|
Packit |
90a5c9 |
if (base->chain && add->chain) {
|
|
Packit |
90a5c9 |
for (p = base->chain; p; p = p->next) {
|
|
Packit |
90a5c9 |
newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
if (newlink->fname == NULL) {
|
|
Packit |
90a5c9 |
conf->chain = savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (savelink) {
|
|
Packit |
90a5c9 |
savelink->next = newlink;
|
|
Packit |
90a5c9 |
savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
conf->chain = savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (p = add->chain; p; p = p->next) {
|
|
Packit |
90a5c9 |
newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
|
|
Packit |
90a5c9 |
/** Filter out merged chain resets */
|
|
Packit |
90a5c9 |
if (newlink->fname == NULL) {
|
|
Packit |
90a5c9 |
conf->chain = savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (savelink) {
|
|
Packit |
90a5c9 |
savelink->next = newlink;
|
|
Packit |
90a5c9 |
savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
conf->chain = savelink = newlink;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (add->chain) {
|
|
Packit |
90a5c9 |
conf->chain = add->chain;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
conf->chain = base->chain;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return conf;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const command_rec filter_cmds[] = {
|
|
Packit |
90a5c9 |
AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
|
|
Packit |
90a5c9 |
"filter-name [filter-type]"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE3("FilterProvider", filter_provider, NULL, OR_OPTIONS,
|
|
Packit |
90a5c9 |
"filter-name provider-name match-expression"),
|
|
Packit |
90a5c9 |
AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
|
|
Packit |
90a5c9 |
"list of filter names with optional [+-=!@]"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
|
|
Packit |
90a5c9 |
"filter-name debug-level"),
|
|
Packit |
90a5c9 |
AP_INIT_TAKE_ARGV("AddOutputFilterByType", filter_bytype, NULL, OR_FILEINFO,
|
|
Packit |
90a5c9 |
"output filter name followed by one or more content-types"),
|
|
Packit |
90a5c9 |
#ifndef NO_PROTOCOL
|
|
Packit |
90a5c9 |
AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
|
|
Packit |
90a5c9 |
"filter-name [provider-name] protocol-args"),
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
{ NULL }
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE_MODULE(filter) = {
|
|
Packit |
90a5c9 |
STANDARD20_MODULE_STUFF,
|
|
Packit |
90a5c9 |
filter_config,
|
|
Packit |
90a5c9 |
filter_merge,
|
|
Packit |
90a5c9 |
NULL,
|
|
Packit |
90a5c9 |
NULL,
|
|
Packit |
90a5c9 |
filter_cmds,
|
|
Packit |
90a5c9 |
filter_hooks
|
|
Packit |
90a5c9 |
};
|