]> granicus.if.org Git - apache/blob - modules/filters/mod_filter.c
leave a hint while scrolling through inflate() calls
[apache] / modules / filters / mod_filter.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #define APR_WANT_STRFUNC
18 #include "apr_want.h"
19 #include "apr_lib.h"
20 #include "apr_strings.h"
21 #include "apr_hash.h"
22 #include "httpd.h"
23 #include "http_config.h"
24 #include "http_request.h"
25 #include "http_log.h"
26 #include "util_filter.h"
27 #include "ap_expr.h"
28
29 module AP_MODULE_DECLARE_DATA filter_module;
30
31 /**
32  * @brief is a filter provider, as defined and implemented by mod_filter.
33  *
34  * The struct is a linked list, with dispatch criteria
35  * defined for each filter.  The provider implementation itself is a
36  * (2.0-compatible) ap_filter_rec_t* frec.
37  */
38 struct ap_filter_provider_t {
39     ap_expr_info_t *expr;
40     const char **types;
41
42     /** The filter that implements this provider */
43     ap_filter_rec_t *frec;
44
45     /** The next provider in the list */
46     ap_filter_provider_t *next;
47 };
48
49 /** we need provider_ctx to save ctx values set by providers in filter_init */
50 typedef struct provider_ctx provider_ctx;
51 struct provider_ctx {
52     ap_filter_provider_t *provider;
53     void *ctx;
54     provider_ctx *next;
55 };
56 typedef struct {
57     ap_out_filter_func func;
58     void *fctx;
59     provider_ctx *init_ctx;
60 } harness_ctx;
61
62 typedef struct mod_filter_chain {
63     const char *fname;
64     struct mod_filter_chain *next;
65 } mod_filter_chain;
66
67 typedef struct {
68     apr_hash_t *live_filters;
69     mod_filter_chain *chain;
70 } mod_filter_cfg;
71
72 typedef struct {
73     const char* range ;
74 } mod_filter_ctx ;
75
76
77 static void filter_trace(conn_rec *c, int debug, const char *fname,
78                          apr_bucket_brigade *bb)
79 {
80     apr_bucket *b;
81
82     switch (debug) {
83     case 0:        /* normal, operational use */
84         return;
85     case 1:        /* mod_diagnostics level */
86         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01375) "%s", fname);
87         for (b = APR_BRIGADE_FIRST(bb);
88              b != APR_BRIGADE_SENTINEL(bb);
89              b = APR_BUCKET_NEXT(b)) {
90
91             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(01376)
92                           "%s: type: %s, length: %" APR_SIZE_T_FMT,
93                           fname, b->type->name ? b->type->name : "(unknown)",
94                           b->length);
95         }
96         break;
97     }
98 }
99
100 static int filter_init(ap_filter_t *f)
101 {
102     ap_filter_provider_t *p;
103     provider_ctx *pctx;
104     int err;
105     ap_filter_rec_t *filter = f->frec;
106
107     harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
108     for (p = filter->providers; p; p = p->next) {
109         if (p->frec->filter_init_func == filter_init) {
110             ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01377)
111                           "Chaining of FilterProviders not supported");
112             return HTTP_INTERNAL_SERVER_ERROR;
113         }
114         else if (p->frec->filter_init_func) {
115             f->ctx = NULL;
116             if ((err = p->frec->filter_init_func(f)) != OK) {
117                 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c, APLOGNO(01378)
118                               "filter_init for %s failed", p->frec->name);
119                 return err;   /* if anyone errors out here, so do we */
120             }
121             if (f->ctx != NULL) {
122                 /* the filter init function set a ctx - we need to record it */
123                 pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
124                 pctx->provider = p;
125                 pctx->ctx = f->ctx;
126                 pctx->next = fctx->init_ctx;
127                 fctx->init_ctx = pctx;
128             }
129         }
130     }
131     f->ctx = fctx;
132     return OK;
133 }
134
135 static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
136 {
137     ap_filter_provider_t *provider;
138     int match = 0;
139     const char *err = NULL;
140     request_rec *r = f->r;
141     harness_ctx *ctx = f->ctx;
142     provider_ctx *pctx;
143 #ifndef NO_PROTOCOL
144     unsigned int proto_flags;
145     mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
146                                                 &filter_module);
147 #endif
148
149     /* Check registered providers in order */
150     for (provider = filter->providers; provider; provider = provider->next) {
151         if (provider->expr) {
152             match = ap_expr_exec(r, provider->expr, &err);
153             if (err) {
154                 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01379)
155                               "Error evaluating filter dispatch condition: %s",
156                               err);
157                 match = 0;
158             }
159             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
160                           "Expression condition for '%s' %s",
161                           provider->frec->name,
162                           match ? "matched" : "did not match");
163         }
164         else if (r->content_type) {
165             const char **type = provider->types;
166             size_t len = strcspn(r->content_type, "; \t");
167             AP_DEBUG_ASSERT(type != NULL);
168             ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
169                           "Content-Type '%s' ...", r->content_type);
170             while (*type) {
171                 /* Handle 'content-type;charset=...' correctly */
172                 if (strncmp(*type, r->content_type, len) == 0
173                     && (*type)[len] == '\0') {
174                     ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
175                                   "... matched '%s'", *type);
176                     match = 1;
177                     break;
178                 }
179                 else {
180                     ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r,
181                                   "... did not match '%s'", *type);
182                 }
183                 type++;
184             }
185             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
186                           "Content-Type condition for '%s' %s",
187                           provider->frec->name,
188                           match ? "matched" : "did not match");
189         }
190         else {
191             ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r,
192                           "Content-Type condition for '%s' did not match: "
193                           "no Content-Type", provider->frec->name);
194         }
195
196         if (match) {
197             /* condition matches this provider */
198 #ifndef NO_PROTOCOL
199             /* check protocol
200              *
201              * FIXME:
202              * This is a quick hack and almost certainly buggy.
203              * The idea is that by putting this in mod_filter, we relieve
204              * filter implementations of the burden of fixing up HTTP headers
205              * for cases that are routinely affected by filters.
206              *
207              * Default is ALWAYS to do nothing, so as not to tread on the
208              * toes of filters which want to do it themselves.
209              *
210              */
211             proto_flags = provider->frec->proto_flags;
212
213             /* some specific things can't happen in a proxy */
214             if (r->proxyreq) {
215                 if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
216                     /* can't use this provider; try next */
217                     continue;
218                 }
219
220                 if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
221                     const char *str = apr_table_get(r->headers_out,
222                                                     "Cache-Control");
223                     if (str) {
224                         if (ap_strcasestr(str, "no-transform")) {
225                             /* can't use this provider; try next */
226                             continue;
227                         }
228                     }
229                     apr_table_addn(r->headers_out, "Warning",
230                                    apr_psprintf(r->pool,
231                                                 "214 %s Transformation applied",
232                                                 r->hostname));
233                 }
234             }
235
236             /* things that are invalidated if the filter transforms content */
237             if (proto_flags & AP_FILTER_PROTO_CHANGE) {
238                 apr_table_unset(r->headers_out, "Content-MD5");
239                 apr_table_unset(r->headers_out, "ETag");
240                 if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
241                     apr_table_unset(r->headers_out, "Content-Length");
242                 }
243             }
244
245             /* no-cache is for a filter that has different effect per-hit */
246             if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
247                 apr_table_unset(r->headers_out, "Last-Modified");
248                 apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
249             }
250
251             if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
252                 apr_table_setn(r->headers_out, "Accept-Ranges", "none");
253             }
254             else if (rctx && rctx->range) {
255                 /* restore range header we saved earlier */
256                 apr_table_setn(r->headers_in, "Range", rctx->range);
257                 rctx->range = NULL;
258             }
259 #endif
260             for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
261                 if (pctx->provider == provider) {
262                     ctx->fctx = pctx->ctx ;
263                 }
264             }
265             ctx->func = provider->frec->filter_func.out_func;
266             return 1;
267         }
268     }
269
270     /* No provider matched */
271     return 0;
272 }
273
274 static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
275 {
276     apr_status_t ret;
277 #ifndef NO_PROTOCOL
278     const char *cachecontrol;
279 #endif
280     harness_ctx *ctx = f->ctx;
281     ap_filter_rec_t *filter = f->frec;
282
283     if (f->r->status != 200
284         && !apr_table_get(f->r->subprocess_env, "filter-errordocs")) {
285         ap_remove_output_filter(f);
286         return ap_pass_brigade(f->next, bb);
287     }
288
289     filter_trace(f->c, filter->debug, f->frec->name, bb);
290
291     /* look up a handler function if we haven't already set it */
292     if (!ctx->func) {
293 #ifndef NO_PROTOCOL
294         if (f->r->proxyreq) {
295             if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
296                 ap_remove_output_filter(f);
297                 return ap_pass_brigade(f->next, bb);
298             }
299
300             if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
301                 cachecontrol = apr_table_get(f->r->headers_out,
302                                              "Cache-Control");
303                 if (cachecontrol) {
304                     if (ap_strcasestr(cachecontrol, "no-transform")) {
305                         ap_remove_output_filter(f);
306                         return ap_pass_brigade(f->next, bb);
307                     }
308                 }
309             }
310         }
311 #endif
312         if (!filter_lookup(f, filter)) {
313             ap_remove_output_filter(f);
314             return ap_pass_brigade(f->next, bb);
315         }
316         AP_DEBUG_ASSERT(ctx->func != NULL);
317     }
318
319     /* call the content filter with its own context, then restore our
320      * context
321      */
322     f->ctx = ctx->fctx;
323     ret = ctx->func(f, bb);
324     ctx->fctx = f->ctx;
325     f->ctx = ctx;
326
327     return ret;
328 }
329
330 #ifndef NO_PROTOCOL
331 static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
332                                    const char *pname, const char *proto)
333 {
334     static const char *sep = ";, \t";
335     char *arg;
336     char *tok = 0;
337     unsigned int flags = 0;
338     mod_filter_cfg *cfg = CFG;
339     ap_filter_provider_t *provider = NULL;
340     ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
341                                            APR_HASH_KEY_STRING);
342
343     if (!filter) {
344         return "FilterProtocol: No such filter";
345     }
346
347     /* Fixup the args: it's really pname that's optional */
348     if (proto == NULL) {
349         proto = pname;
350         pname = NULL;
351     }
352     else {
353         /* Find provider */
354         for (provider = filter->providers; provider; provider = provider->next){
355             if (!strcasecmp(provider->frec->name, pname)) {
356                 break;
357             }
358         }
359         if (!provider) {
360             return "FilterProtocol: No such provider for this filter";
361         }
362     }
363
364     /* Now set flags from our args */
365     for (arg = apr_strtok(apr_pstrdup(cmd->pool, proto), sep, &tok);
366          arg; arg = apr_strtok(NULL, sep, &tok)) {
367
368         if (!strcasecmp(arg, "change=yes")) {
369             flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
370         }
371         if (!strcasecmp(arg, "change=no")) {
372             flags &= ~(AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH);
373         }
374         else if (!strcasecmp(arg, "change=1:1")) {
375             flags |= AP_FILTER_PROTO_CHANGE;
376         }
377         else if (!strcasecmp(arg, "byteranges=no")) {
378             flags |= AP_FILTER_PROTO_NO_BYTERANGE;
379         }
380         else if (!strcasecmp(arg, "proxy=no")) {
381             flags |= AP_FILTER_PROTO_NO_PROXY;
382         }
383         else if (!strcasecmp(arg, "proxy=transform")) {
384             flags |= AP_FILTER_PROTO_TRANSFORM;
385         }
386         else if (!strcasecmp(arg, "cache=no")) {
387             flags |= AP_FILTER_PROTO_NO_CACHE;
388         }
389     }
390
391     if (pname) {
392         provider->frec->proto_flags = flags;
393     }
394     else {
395         filter->proto_flags = flags;
396     }
397
398     return NULL;
399 }
400 #endif
401
402 static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
403                                   const char *place)
404 {
405     mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
406     ap_filter_rec_t *filter;
407
408     filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
409     apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
410
411     filter->name = fname;
412     filter->filter_init_func = filter_init;
413     filter->filter_func.out_func = filter_harness;
414     filter->ftype = AP_FTYPE_RESOURCE;
415     filter->next = NULL;
416
417     if (place) {
418         if (!strcasecmp(place, "CONTENT_SET")) {
419             filter->ftype = AP_FTYPE_CONTENT_SET;
420         }
421         else if (!strcasecmp(place, "PROTOCOL")) {
422             filter->ftype = AP_FTYPE_PROTOCOL;
423         }
424         else if (!strcasecmp(place, "CONNECTION")) {
425             filter->ftype = AP_FTYPE_CONNECTION;
426         }
427         else if (!strcasecmp(place, "NETWORK")) {
428             filter->ftype = AP_FTYPE_NETWORK;
429         }
430     }
431
432     return NULL;
433 }
434
435 static const char *add_filter(cmd_parms *cmd, void *CFG,
436                               const char *fname, const char *pname,
437                               const char *expr, const char **types)
438 {
439     mod_filter_cfg *cfg = CFG;
440     ap_filter_provider_t *provider;
441     const char *c;
442     ap_filter_rec_t* frec;
443     ap_filter_rec_t* provider_frec;
444     ap_expr_info_t *node;
445     const char *err = NULL;
446
447     /* fname has been declared with DeclareFilter, so we can look it up */
448     frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
449
450     /* or if provider is mod_filter itself, we can also look it up */
451     if (!frec) {
452         c = filter_declare(cmd, CFG, fname, NULL);
453         if ( c ) {
454             return c;
455         }
456         frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
457     }
458
459     if (!frec) {
460         return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
461     }
462
463     /* if provider has been registered, we can look it up */
464     provider_frec = ap_get_output_filter_handle(pname);
465     if (!provider_frec) {
466         return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
467     }
468     provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
469     if (expr) {
470         node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
471         if (err) {
472             return apr_pstrcat(cmd->pool,
473                                "Error parsing FilterProvider expression:", err,
474                                NULL);
475         }
476         provider->expr = node;
477         provider->types = NULL;
478     }
479     else {
480         provider->types = types;
481         provider->expr = NULL;
482     }
483     provider->frec = provider_frec;
484     provider->next = frec->providers;
485     frec->providers = provider;
486     return NULL;
487 }
488
489 static const char *filter_provider(cmd_parms *cmd, void *CFG,
490                                    const char *fname, const char *pname,
491                                    const char *expr)
492 {
493     return add_filter(cmd, CFG, fname, pname, expr, NULL);
494 }
495
496 static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
497 {
498     mod_filter_chain *p;
499     mod_filter_chain *q;
500     mod_filter_cfg *cfg = CFG;
501
502     switch (arg[0]) {
503     case '+':        /* add to end of chain */
504         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
505         p->fname = arg+1;
506         if (cfg->chain) {
507             for (q = cfg->chain; q->next; q = q->next);
508             q->next = p;
509         }
510         else {
511             cfg->chain = p;
512         }
513         break;
514
515     case '@':        /* add to start of chain */
516         p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
517         p->fname = arg+1;
518         p->next = cfg->chain;
519         cfg->chain = p;
520         break;
521
522     case '-':        /* remove from chain */
523         if (cfg->chain) {
524             if (strcasecmp(cfg->chain->fname, arg+1)) {
525                 for (p = cfg->chain; p->next; p = p->next) {
526                     if (!strcasecmp(p->next->fname, arg+1)) {
527                         p->next = p->next->next;
528                     }
529                 }
530             }
531             else {
532                 cfg->chain = cfg->chain->next;
533             }
534         }
535         break;
536
537     case '!':        /* Empty the chain */
538                      /** IG: Add a NULL provider to the beginning so that
539                       *  we can ensure that we'll empty everything before
540                       *  this when doing config merges later */
541         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
542         p->fname = NULL;
543         cfg->chain = p;
544         break;
545
546     case '=':        /* initialise chain with this arg */
547                      /** IG: Prepend a NULL provider to the beginning as above */
548         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
549         p->fname = NULL;
550         p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
551         p->next->fname = arg+1;
552         cfg->chain = p;
553         break;
554
555     default:        /* add to end */
556         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
557         p->fname = arg;
558         if (cfg->chain) {
559             for (q = cfg->chain; q->next; q = q->next);
560             q->next = p;
561         }
562         else {
563             cfg->chain = p;
564         }
565         break;
566     }
567
568     return NULL;
569 }
570
571 static const char *filter_bytype1(cmd_parms *cmd, void *CFG,
572                                   const char *pname, const char **types)
573 {
574     const char *rv;
575     const char *fname;
576     int seen_name = 0;
577     mod_filter_cfg *cfg = CFG;
578
579     /* construct fname from name */
580     fname = apr_pstrcat(cmd->pool, "BYTYPE:", pname, NULL);
581
582     /* check whether this is already registered, in which case
583      * it's already in the filter chain
584      */
585     if (apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING)) {
586         seen_name = 1;
587     }
588
589     rv = add_filter(cmd, CFG, fname, pname, NULL, types);
590
591     /* If it's the first time through, add to filterchain */
592     if (rv == NULL && !seen_name) {
593         rv = filter_chain(cmd, CFG, fname);
594     }
595     return rv;
596 }
597
598 static const char *filter_bytype(cmd_parms *cmd, void *CFG,
599                                  int argc, char *const argv[])
600 {
601     /* back compatibility, need to parse multiple components in filter name */
602     char *pname;
603     char *strtok_state = NULL;
604     char *name;
605     const char **types;
606     const char *rv = NULL;
607     if (argc < 2)
608         return "AddOutputFilterByType requires at least two arguments";
609     name = apr_pstrdup(cmd->temp_pool, argv[0]);
610     types = apr_palloc(cmd->pool, argc * sizeof(char *));
611     memcpy(types, &argv[1], (argc - 1) * sizeof(char *));
612     types[argc-1] = NULL;
613     for (pname = apr_strtok(name, ";", &strtok_state);
614          pname != NULL && rv == NULL;
615          pname = apr_strtok(NULL, ";", &strtok_state)) {
616         rv = filter_bytype1(cmd, CFG, pname, types);
617     }
618     return rv;
619 }
620
621 static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
622                                 const char *level)
623 {
624     mod_filter_cfg *cfg = CFG;
625     ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
626                                          APR_HASH_KEY_STRING);
627     if (!frec) {
628         return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
629     }
630     frec->debug = atoi(level);
631
632     return NULL;
633 }
634
635 static void filter_insert(request_rec *r)
636 {
637     mod_filter_chain *p;
638     ap_filter_rec_t *filter;
639     mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
640                                                &filter_module);
641 #ifndef NO_PROTOCOL
642     int ranges = 1;
643     mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
644     ap_set_module_config(r->request_config, &filter_module, ctx);
645 #endif
646
647     /** IG: Now that we've merged to the final config, go one last time
648      *  through the chain, and prune out the NULL filters */
649
650     for (p = cfg->chain; p; p = p->next) {
651         if (p->fname == NULL)
652             cfg->chain = p->next;
653     }
654
655     for (p = cfg->chain; p; p = p->next) {
656         filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
657         if (filter == NULL) {
658             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01380)
659                           "Unknown filter %s not added", p->fname);
660             continue;
661         }
662         ap_add_output_filter_handle(filter, NULL, r, r->connection);
663 #ifndef NO_PROTOCOL
664         if (ranges && (filter->proto_flags
665                        & (AP_FILTER_PROTO_NO_BYTERANGE
666                           | AP_FILTER_PROTO_CHANGE_LENGTH))) {
667             ctx->range = apr_table_get(r->headers_in, "Range");
668             apr_table_unset(r->headers_in, "Range");
669             ranges = 0;
670         }
671 #endif
672     }
673
674     return;
675 }
676
677 static void filter_hooks(apr_pool_t *pool)
678 {
679     ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
680 }
681
682 static void *filter_config(apr_pool_t *pool, char *x)
683 {
684     mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
685     cfg->live_filters = apr_hash_make(pool);
686     cfg->chain = NULL;
687     return cfg;
688 }
689
690 static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
691 {
692     mod_filter_cfg *base = BASE;
693     mod_filter_cfg *add = ADD;
694     mod_filter_chain *savelink = 0;
695     mod_filter_chain *newlink;
696     mod_filter_chain *p;
697     mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
698
699     conf->live_filters = apr_hash_overlay(pool, add->live_filters,
700                                           base->live_filters);
701     if (base->chain && add->chain) {
702         for (p = base->chain; p; p = p->next) {
703             newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
704             if (newlink->fname == NULL) {
705                 conf->chain = savelink = newlink;
706             }
707             else if (savelink) {
708                 savelink->next = newlink;
709                 savelink = newlink;
710             }
711             else {
712                 conf->chain = savelink = newlink;
713             }
714         }
715
716         for (p = add->chain; p; p = p->next) {
717             newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
718             /** Filter out merged chain resets */
719             if (newlink->fname == NULL) {
720                 conf->chain = savelink = newlink;
721             }
722             else if (savelink) {
723                 savelink->next = newlink;
724                 savelink = newlink;
725             }
726             else {
727                 conf->chain = savelink = newlink;
728             }
729         }
730     }
731     else if (add->chain) {
732         conf->chain = add->chain;
733     }
734     else {
735         conf->chain = base->chain;
736     }
737
738     return conf;
739 }
740
741 static const command_rec filter_cmds[] = {
742     AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
743         "filter-name [filter-type]"),
744     AP_INIT_TAKE3("FilterProvider", filter_provider, NULL, OR_OPTIONS,
745         "filter-name provider-name match-expression"),
746     AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
747         "list of filter names with optional [+-=!@]"),
748     AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
749         "filter-name debug-level"),
750     AP_INIT_TAKE_ARGV("AddOutputFilterByType", filter_bytype, NULL, OR_FILEINFO,
751         "output filter name followed by one or more content-types"),
752 #ifndef NO_PROTOCOL
753     AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
754         "filter-name [provider-name] protocol-args"),
755 #endif
756     { NULL }
757 };
758
759 AP_DECLARE_MODULE(filter) = {
760     STANDARD20_MODULE_STUFF,
761     filter_config,
762     filter_merge,
763     NULL,
764     NULL,
765     filter_cmds,
766     filter_hooks
767 };