123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874 |
- /* 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.
- */
- #define APR_WANT_STRFUNC
- #include "apr_want.h"
- #include "apr_lib.h"
- #include "apr_strings.h"
- #include "apr_hash.h"
- #include "httpd.h"
- #include "http_config.h"
- #include "http_request.h"
- #include "http_log.h"
- #include "util_filter.h"
- module AP_MODULE_DECLARE_DATA filter_module;
- /**
- * @brief is a filter provider, as defined and implemented by mod_filter.
- *
- * The struct is a linked list, with dispatch criteria
- * defined for each filter. The provider implementation itself is a
- * (2.0-compatible) ap_filter_rec_t* frec.
- */
- struct ap_filter_provider_t {
- /** How to match this provider to filter dispatch criterion */
- enum {
- STRING_MATCH,
- STRING_CONTAINS,
- REGEX_MATCH,
- INT_EQ,
- INT_LT,
- INT_LE,
- INT_GT,
- INT_GE,
- DEFINED
- } match_type;
- /** negation on match_type */
- int not;
- /** The dispatch match itself - union member depends on match_type */
- union {
- const char *string;
- ap_regex_t *regex;
- int number;
- } match;
- /** The filter that implements this provider */
- ap_filter_rec_t *frec;
- /** The next provider in the list */
- ap_filter_provider_t *next;
- /** Dispatch criteria for filter providers */
- enum {
- HANDLER,
- REQUEST_HEADERS,
- RESPONSE_HEADERS,
- SUBPROCESS_ENV,
- CONTENT_TYPE
- } dispatch;
- /** Match value for filter providers */
- const char* value;
- };
- /** we need provider_ctx to save ctx values set by providers in filter_init */
- typedef struct provider_ctx provider_ctx;
- struct provider_ctx {
- ap_filter_provider_t *provider;
- void *ctx;
- provider_ctx *next;
- };
- typedef struct {
- ap_out_filter_func func;
- void *fctx;
- provider_ctx *init_ctx;
- } harness_ctx;
- typedef struct mod_filter_chain {
- const char *fname;
- struct mod_filter_chain *next;
- } mod_filter_chain;
- typedef struct {
- apr_hash_t *live_filters;
- mod_filter_chain *chain;
- } mod_filter_cfg;
- typedef struct {
- const char* range ;
- } mod_filter_ctx ;
- static void filter_trace(conn_rec *c, int debug, const char *fname,
- apr_bucket_brigade *bb)
- {
- apr_bucket *b;
- switch (debug) {
- case 0: /* normal, operational use */
- return;
- case 1: /* mod_diagnostics level */
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s", fname);
- for (b = APR_BRIGADE_FIRST(bb);
- b != APR_BRIGADE_SENTINEL(bb);
- b = APR_BUCKET_NEXT(b)) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
- "%s: type: %s, length: %" APR_SIZE_T_FMT,
- fname, b->type->name ? b->type->name : "(unknown)",
- b->length);
- }
- break;
- }
- }
- static int filter_init(ap_filter_t *f)
- {
- ap_filter_provider_t *p;
- provider_ctx *pctx;
- int err;
- ap_filter_rec_t *filter = f->frec;
- harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
- for (p = filter->providers; p; p = p->next) {
- if (p->frec->filter_init_func == filter_init) {
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
- "Chaining of FilterProviders not supported");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- else if (p->frec->filter_init_func) {
- f->ctx = NULL;
- if ((err = p->frec->filter_init_func(f)) != OK) {
- ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
- "filter_init for %s failed", p->frec->name);
- return err; /* if anyone errors out here, so do we */
- }
- if (f->ctx != NULL) {
- /* the filter init function set a ctx - we need to record it */
- pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
- pctx->provider = p;
- pctx->ctx = f->ctx;
- pctx->next = fctx->init_ctx;
- fctx->init_ctx = pctx;
- }
- }
- }
- f->ctx = fctx;
- return OK;
- }
- static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
- {
- ap_filter_provider_t *provider;
- const char *str = NULL;
- char *str1;
- int match;
- unsigned int proto_flags;
- request_rec *r = f->r;
- harness_ctx *ctx = f->ctx;
- provider_ctx *pctx;
- mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
- &filter_module);
- /* Check registered providers in order */
- for (provider = filter->providers; provider; provider = provider->next) {
- match = 1;
- switch (provider->dispatch) {
- case REQUEST_HEADERS:
- str = apr_table_get(r->headers_in, provider->value);
- break;
- case RESPONSE_HEADERS:
- str = apr_table_get(r->headers_out, provider->value);
- break;
- case SUBPROCESS_ENV:
- str = apr_table_get(r->subprocess_env, provider->value);
- break;
- case CONTENT_TYPE:
- str = r->content_type;
- break;
- case HANDLER:
- str = r->handler;
- break;
- }
- /* treat nulls so we don't have to check every strcmp individually
- * Not sure if there's anything better to do with them
- */
- if (!str) {
- match = 0;
- }
- /* we can't check for NULL in provider as that kills integer 0
- * so we have to test each string/regexp case in the switch
- */
- else {
- switch (provider->match_type) {
- case STRING_MATCH:
- if (strcasecmp(str, provider->match.string)) {
- match = 0;
- }
- break;
- case STRING_CONTAINS:
- str1 = apr_pstrdup(r->pool, str);
- ap_str_tolower(str1);
- if (!strstr(str1, provider->match.string)) {
- match = 0;
- }
- break;
- case REGEX_MATCH:
- if (ap_regexec(provider->match.regex, str, 0, NULL, 0)
- == AP_REG_NOMATCH) {
- match = 0;
- }
- break;
- case INT_EQ:
- if (atoi(str) != provider->match.number) {
- match = 0;
- }
- break;
- /* Integer comparisons should be [var] OP [match]
- * We need to set match = 0 if the condition fails
- */
- case INT_LT:
- if (atoi(str) >= provider->match.number) {
- match = 0;
- }
- break;
- case INT_LE:
- if (atoi(str) > provider->match.number) {
- match = 0;
- }
- break;
- case INT_GT:
- if (atoi(str) <= provider->match.number) {
- match = 0;
- }
- break;
- case INT_GE:
- if (atoi(str) < provider->match.number) {
- match = 0;
- }
- break;
- case DEFINED: /* we already handled this:-) */
- break;
- }
- }
- if (match != provider->not) {
- /* condition matches this provider */
- #ifndef NO_PROTOCOL
- /* check protocol
- *
- * FIXME:
- * This is a quick hack and almost certainly buggy.
- * The idea is that by putting this in mod_filter, we relieve
- * filter implementations of the burden of fixing up HTTP headers
- * for cases that are routinely affected by filters.
- *
- * Default is ALWAYS to do nothing, so as not to tread on the
- * toes of filters which want to do it themselves.
- *
- */
- proto_flags = provider->frec->proto_flags;
- /* some specific things can't happen in a proxy */
- if (r->proxyreq) {
- if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
- /* can't use this provider; try next */
- continue;
- }
- if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
- str = apr_table_get(r->headers_out, "Cache-Control");
- if (str) {
- str1 = apr_pstrdup(r->pool, str);
- ap_str_tolower(str1);
- if (strstr(str1, "no-transform")) {
- /* can't use this provider; try next */
- continue;
- }
- }
- apr_table_addn(r->headers_out, "Warning",
- apr_psprintf(r->pool,
- "214 %s Transformation applied",
- r->hostname));
- }
- }
- /* things that are invalidated if the filter transforms content */
- if (proto_flags & AP_FILTER_PROTO_CHANGE) {
- apr_table_unset(r->headers_out, "Content-MD5");
- apr_table_unset(r->headers_out, "ETag");
- if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
- apr_table_unset(r->headers_out, "Content-Length");
- }
- }
- /* no-cache is for a filter that has different effect per-hit */
- if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
- apr_table_unset(r->headers_out, "Last-Modified");
- apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
- }
- if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
- apr_table_unset(r->headers_out, "Accept-Ranges");
- }
- else if (rctx && rctx->range) {
- /* restore range header we saved earlier */
- apr_table_setn(r->headers_in, "Range", rctx->range);
- rctx->range = NULL;
- }
- #endif
- for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
- if (pctx->provider == provider) {
- ctx->fctx = pctx->ctx ;
- }
- }
- ctx->func = provider->frec->filter_func.out_func;
- return 1;
- }
- }
- /* No provider matched */
- return 0;
- }
- static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
- {
- apr_status_t ret;
- const char *cachecontrol;
- char *str;
- harness_ctx *ctx = f->ctx;
- ap_filter_rec_t *filter = f->frec;
- if (f->r->status != 200) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
- filter_trace(f->c, filter->debug, f->frec->name, bb);
- /* look up a handler function if we haven't already set it */
- if (!ctx->func) {
- #ifndef NO_PROTOCOL
- if (f->r->proxyreq) {
- if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
- if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
- cachecontrol = apr_table_get(f->r->headers_out,
- "Cache-Control");
- if (cachecontrol) {
- str = apr_pstrdup(f->r->pool, cachecontrol);
- ap_str_tolower(str);
- if (strstr(str, "no-transform")) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
- }
- }
- }
- #endif
- if (!filter_lookup(f, filter)) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
- }
- /* call the content filter with its own context, then restore our
- * context
- */
- f->ctx = ctx->fctx;
- ret = ctx->func(f, bb);
- ctx->fctx = f->ctx;
- f->ctx = ctx;
- return ret;
- }
- #ifndef NO_PROTOCOL
- static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
- const char *pname, const char *proto)
- {
- static const char *sep = ";, \t";
- char *arg;
- char *tok = 0;
- unsigned int flags = 0;
- mod_filter_cfg *cfg = CFG;
- ap_filter_provider_t *provider = NULL;
- ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
- APR_HASH_KEY_STRING);
- if (!filter) {
- return "FilterProtocol: No such filter";
- }
- /* Fixup the args: it's really pname that's optional */
- if (proto == NULL) {
- proto = pname;
- pname = NULL;
- }
- else {
- /* Find provider */
- for (provider = filter->providers; provider; provider = provider->next){
- if (!strcasecmp(provider->frec->name, pname)) {
- break;
- }
- }
- if (!provider) {
- return "FilterProtocol: No such provider for this filter";
- }
- }
- /* Now set flags from our args */
- for (arg = apr_strtok(apr_pstrdup(cmd->pool, proto), sep, &tok);
- arg; arg = apr_strtok(NULL, sep, &tok)) {
- if (!strcasecmp(arg, "change=yes")) {
- flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
- }
- else if (!strcasecmp(arg, "change=1:1")) {
- flags |= AP_FILTER_PROTO_CHANGE;
- }
- else if (!strcasecmp(arg, "byteranges=no")) {
- flags |= AP_FILTER_PROTO_NO_BYTERANGE;
- }
- else if (!strcasecmp(arg, "proxy=no")) {
- flags |= AP_FILTER_PROTO_NO_PROXY;
- }
- else if (!strcasecmp(arg, "proxy=transform")) {
- flags |= AP_FILTER_PROTO_TRANSFORM;
- }
- else if (!strcasecmp(arg, "cache=no")) {
- flags |= AP_FILTER_PROTO_NO_CACHE;
- }
- }
- if (pname) {
- provider->frec->proto_flags = flags;
- }
- else {
- filter->proto_flags = flags;
- }
- return NULL;
- }
- #endif
- static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
- const char *place)
- {
- mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
- ap_filter_rec_t *filter;
- filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
- apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
- filter->name = fname;
- filter->filter_init_func = filter_init;
- filter->filter_func.out_func = filter_harness;
- filter->ftype = AP_FTYPE_RESOURCE;
- filter->next = NULL;
- if (place) {
- if (!strcasecmp(place, "CONTENT_SET")) {
- filter->ftype = AP_FTYPE_CONTENT_SET;
- }
- else if (!strcasecmp(place, "PROTOCOL")) {
- filter->ftype = AP_FTYPE_PROTOCOL;
- }
- else if (!strcasecmp(place, "CONNECTION")) {
- filter->ftype = AP_FTYPE_CONNECTION;
- }
- else if (!strcasecmp(place, "NETWORK")) {
- filter->ftype = AP_FTYPE_NETWORK;
- }
- }
- return NULL;
- }
- static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args)
- {
- mod_filter_cfg *cfg = CFG;
- int flags;
- ap_filter_provider_t *provider;
- const char *rxend;
- const char *c;
- char *str;
- const char *eq;
- ap_filter_rec_t* frec;
- ap_filter_rec_t* provider_frec;
- /* insist on exactly four arguments */
- const char *fname = ap_getword_conf(cmd->pool, &args) ;
- const char *pname = ap_getword_conf(cmd->pool, &args) ;
- const char *condition = ap_getword_conf(cmd->pool, &args) ;
- const char *match = ap_getword_conf(cmd->pool, &args) ;
- eq = ap_getword_conf(cmd->pool, &args) ;
- if ( !*fname || !*pname || !*match || !*condition || *eq ) {
- return "usage: FilterProvider filter provider condition match" ;
- }
- /* fname has been declared with DeclareFilter, so we can look it up */
- frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
- /* or if provider is mod_filter itself, we can also look it up */
- if (!frec) {
- c = filter_declare(cmd, CFG, fname, NULL);
- if ( c ) {
- return c;
- }
- frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
- }
- if (!frec) {
- return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
- }
- /* if provider has been registered, we can look it up */
- provider_frec = ap_get_output_filter_handle(pname);
- if (!provider_frec) {
- return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
- }
- provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
- if (*match == '!') {
- provider->not = 1;
- ++match;
- }
- else {
- provider->not = 0;
- }
- switch (*match++) {
- case '<':
- if (*match == '=') {
- provider->match_type = INT_LE;
- ++match;
- }
- else {
- provider->match_type = INT_LT;
- }
- provider->match.number = atoi(match);
- break;
- case '>':
- if (*match == '=') {
- provider->match_type = INT_GE;
- ++match;
- }
- else {
- provider->match_type = INT_GT;
- }
- provider->match.number = atoi(match);
- break;
- case '=':
- provider->match_type = INT_EQ;
- provider->match.number = atoi(match);
- break;
- case '/':
- provider->match_type = REGEX_MATCH;
- rxend = ap_strchr_c(match, '/');
- if (!rxend) {
- return "Bad regexp syntax";
- }
- flags = AP_REG_NOSUB; /* we're not mod_rewrite:-) */
- for (c = rxend+1; *c; ++c) {
- switch (*c) {
- case 'i': flags |= AP_REG_ICASE; break;
- }
- }
- provider->match.regex = ap_pregcomp(cmd->pool,
- apr_pstrndup(cmd->pool,
- match,
- rxend-match),
- flags);
- if (provider->match.regex == NULL) {
- return "Bad regexp";
- }
- break;
- case '*':
- provider->match_type = DEFINED;
- provider->match.number = -1;
- break;
- case '$':
- provider->match_type = STRING_CONTAINS;
- str = apr_pstrdup(cmd->pool, match);
- ap_str_tolower(str);
- provider->match.string = str;
- break;
- default:
- provider->match_type = STRING_MATCH;
- provider->match.string = apr_pstrdup(cmd->pool, match-1);
- break;
- }
- provider->frec = provider_frec;
- provider->next = frec->providers;
- frec->providers = provider;
- /* determine what a filter will dispatch this provider on */
- eq = ap_strchr_c(condition, '=');
- if (eq) {
- str = apr_pstrdup(cmd->pool, eq+1);
- if (!strncasecmp(condition, "env=", 4)) {
- provider->dispatch = SUBPROCESS_ENV;
- }
- else if (!strncasecmp(condition, "req=", 4)) {
- provider->dispatch = REQUEST_HEADERS;
- }
- else if (!strncasecmp(condition, "resp=", 5)) {
- provider->dispatch = RESPONSE_HEADERS;
- }
- else {
- return "FilterProvider: unrecognized dispatch table";
- }
- }
- else {
- if (!strcasecmp(condition, "handler")) {
- provider->dispatch = HANDLER;
- }
- else {
- provider->dispatch = RESPONSE_HEADERS;
- }
- str = apr_pstrdup(cmd->pool, condition);
- ap_str_tolower(str);
- }
- if ( (provider->dispatch == RESPONSE_HEADERS)
- && !strcasecmp(str, "content-type")) {
- provider->dispatch = CONTENT_TYPE;
- }
- provider->value = str;
- return NULL;
- }
- static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
- {
- mod_filter_chain *p;
- mod_filter_chain *q;
- mod_filter_cfg *cfg = CFG;
- switch (arg[0]) {
- case '+': /* add to end of chain */
- p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
- p->fname = arg+1;
- if (cfg->chain) {
- for (q = cfg->chain; q->next; q = q->next);
- q->next = p;
- }
- else {
- cfg->chain = p;
- }
- break;
- case '@': /* add to start of chain */
- p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
- p->fname = arg+1;
- p->next = cfg->chain;
- cfg->chain = p;
- break;
- case '-': /* remove from chain */
- if (cfg->chain) {
- if (strcasecmp(cfg->chain->fname, arg+1)) {
- for (p = cfg->chain; p->next; p = p->next) {
- if (!strcasecmp(p->next->fname, arg+1)) {
- p->next = p->next->next;
- }
- }
- }
- else {
- cfg->chain = cfg->chain->next;
- }
- }
- break;
- case '!': /* Empty the chain */
- /** IG: Add a NULL provider to the beginning so that
- * we can ensure that we'll empty everything before
- * this when doing config merges later */
- p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
- p->fname = NULL;
- cfg->chain = p;
- break;
- case '=': /* initialise chain with this arg */
- /** IG: Prepend a NULL provider to the beginning as above */
- p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
- p->fname = NULL;
- p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
- p->next->fname = arg+1;
- cfg->chain = p;
- break;
- default: /* add to end */
- p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
- p->fname = arg;
- if (cfg->chain) {
- for (q = cfg->chain; q->next; q = q->next);
- q->next = p;
- }
- else {
- cfg->chain = p;
- }
- break;
- }
- return NULL;
- }
- static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
- const char *level)
- {
- mod_filter_cfg *cfg = CFG;
- ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
- APR_HASH_KEY_STRING);
- if (!frec) {
- return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
- }
- frec->debug = atoi(level);
- return NULL;
- }
- static void filter_insert(request_rec *r)
- {
- mod_filter_chain *p;
- ap_filter_rec_t *filter;
- mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
- &filter_module);
- #ifndef NO_PROTOCOL
- int ranges = 1;
- mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
- ap_set_module_config(r->request_config, &filter_module, ctx);
- #endif
- /** IG: Now that we've merged to the final config, go one last time
- * through the chain, and prune out the NULL filters */
- for (p = cfg->chain; p; p = p->next) {
- if (p->fname == NULL)
- cfg->chain = p->next;
- }
- for (p = cfg->chain; p; p = p->next) {
- filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
- if (filter == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
- "Unknown filter %s not added", p->fname);
- continue;
- }
- ap_add_output_filter_handle(filter, NULL, r, r->connection);
- #ifndef NO_PROTOCOL
- if (ranges && (filter->proto_flags
- & (AP_FILTER_PROTO_NO_BYTERANGE
- | AP_FILTER_PROTO_CHANGE_LENGTH))) {
- ctx->range = apr_table_get(r->headers_in, "Range");
- apr_table_unset(r->headers_in, "Range");
- ranges = 0;
- }
- #endif
- }
- return;
- }
- static void filter_hooks(apr_pool_t *pool)
- {
- ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
- }
- static void *filter_config(apr_pool_t *pool, char *x)
- {
- mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
- cfg->live_filters = apr_hash_make(pool);
- cfg->chain = NULL;
- return cfg;
- }
- static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
- {
- mod_filter_cfg *base = BASE;
- mod_filter_cfg *add = ADD;
- mod_filter_chain *savelink = 0;
- mod_filter_chain *newlink;
- mod_filter_chain *p;
- mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
- conf->live_filters = apr_hash_overlay(pool, add->live_filters,
- base->live_filters);
- if (base->chain && add->chain) {
- for (p = base->chain; p; p = p->next) {
- newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
- if (newlink->fname == NULL) {
- conf->chain = savelink = newlink;
- }
- else if (savelink) {
- savelink->next = newlink;
- savelink = newlink;
- }
- else {
- conf->chain = savelink = newlink;
- }
- }
- for (p = add->chain; p; p = p->next) {
- newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
- /** Filter out merged chain resets */
- if (newlink->fname == NULL) {
- conf->chain = savelink = newlink;
- }
- else if (savelink) {
- savelink->next = newlink;
- savelink = newlink;
- }
- else {
- conf->chain = savelink = newlink;
- }
- }
- }
- else if (add->chain) {
- conf->chain = add->chain;
- }
- else {
- conf->chain = base->chain;
- }
- return conf;
- }
- static const command_rec filter_cmds[] = {
- AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
- "filter-name [, filter-type]"),
- /** we don't have a TAKE4, so we have to use RAW_ARGS */
- AP_INIT_RAW_ARGS("FilterProvider", filter_provider, NULL, OR_OPTIONS,
- "filter-name, provider-name, dispatch--criterion, dispatch-match"),
- AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
- "list of filter names with optional [+-=!@]"),
- AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
- "Debug level"),
- #ifndef NO_PROTOCOL
- AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
- "filter-name [provider-name] protocol-args"),
- #endif
- { NULL }
- };
- module AP_MODULE_DECLARE_DATA filter_module = {
- STANDARD20_MODULE_STUFF,
- filter_config,
- filter_merge,
- NULL,
- NULL,
- filter_cmds,
- filter_hooks
- };
|