]> granicus.if.org Git - apache/blob - modules/filters/mod_filter.c
dfb0fde65bcdcafa337fc14f95b5e4d8bb86ca66
[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
28 module AP_MODULE_DECLARE_DATA filter_module;
29
30 /**
31  * @brief is a filter provider, as defined and implemented by mod_filter.
32  *
33  * The struct is a linked list, with dispatch criteria
34  * defined for each filter.  The provider implementation itself is a
35  * (2.0-compatible) ap_filter_rec_t* frec.
36  */
37 struct ap_filter_provider_t {
38     /** How to match this provider to filter dispatch criterion */
39     enum {
40         STRING_MATCH,
41         STRING_CONTAINS,
42         REGEX_MATCH,
43         INT_EQ,
44         INT_LT,
45         INT_LE,
46         INT_GT,
47         INT_GE,
48         DEFINED
49     } match_type;
50
51     /** negation on match_type */
52     int not;
53
54     /** The dispatch match itself - union member depends on match_type */
55     union {
56         const char *string;
57         ap_regex_t *regex;
58         int         number;
59     } match;
60
61     /** The filter that implements this provider */
62     ap_filter_rec_t *frec;
63
64     /** The next provider in the list */
65     ap_filter_provider_t *next;
66
67     /** Dispatch criteria for filter providers */
68     enum {
69         HANDLER,
70         REQUEST_HEADERS,
71         RESPONSE_HEADERS,
72         SUBPROCESS_ENV,
73         CONTENT_TYPE
74     } dispatch;
75
76     /** Match value for filter providers */
77     const char* value;
78 };
79
80 /** we need provider_ctx to save ctx values set by providers in filter_init */
81 typedef struct provider_ctx provider_ctx;
82 struct provider_ctx {
83     ap_filter_provider_t *provider;
84     void *ctx;
85     provider_ctx *next;
86 };
87 typedef struct {
88     ap_out_filter_func func;
89     void *fctx;
90     provider_ctx *init_ctx;
91 } harness_ctx;
92
93 typedef struct mod_filter_chain {
94     const char *fname;
95     struct mod_filter_chain *next;
96 } mod_filter_chain;
97
98 typedef struct {
99     apr_hash_t *live_filters;
100     mod_filter_chain *chain;
101 } mod_filter_cfg;
102
103 typedef struct {
104     const char* range ;
105 } mod_filter_ctx ;
106
107
108 static void filter_trace(conn_rec *c, int debug, const char *fname,
109                          apr_bucket_brigade *bb)
110 {
111     apr_bucket *b;
112
113     switch (debug) {
114     case 0:        /* normal, operational use */
115         return;
116     case 1:        /* mod_diagnostics level */
117         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, "%s", fname);
118         for (b = APR_BRIGADE_FIRST(bb);
119              b != APR_BRIGADE_SENTINEL(bb);
120              b = APR_BUCKET_NEXT(b)) {
121
122             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c,
123                           "%s: type: %s, length: %" APR_SIZE_T_FMT,
124                           fname, b->type->name ? b->type->name : "(unknown)",
125                           b->length);
126         }
127         break;
128     }
129 }
130
131 static int filter_init(ap_filter_t *f)
132 {
133     ap_filter_provider_t *p;
134     provider_ctx *pctx;
135     int err;
136     ap_filter_rec_t *filter = f->frec;
137
138     harness_ctx *fctx = apr_pcalloc(f->r->pool, sizeof(harness_ctx));
139     for (p = filter->providers; p; p = p->next) {
140         if (p->frec->filter_init_func) {
141             f->ctx = NULL;
142             if ((err = p->frec->filter_init_func(f)) != OK) {
143                 ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, f->c,
144                               "filter_init for %s failed", p->frec->name);
145                 return err;   /* if anyone errors out here, so do we */
146             }
147             if (f->ctx != NULL) {
148                 /* the filter init function set a ctx - we need to record it */
149                 pctx = apr_pcalloc(f->r->pool, sizeof(provider_ctx));
150                 pctx->provider = p;
151                 pctx->ctx = f->ctx;
152                 pctx->next = fctx->init_ctx;
153                 fctx->init_ctx = pctx;
154             }
155         }
156     }
157     f->ctx = fctx;
158     return OK;
159 }
160
161 static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
162 {
163     ap_filter_provider_t *provider;
164     const char *str = NULL;
165     char *str1;
166     int match;
167     unsigned int proto_flags;
168     request_rec *r = f->r;
169     harness_ctx *ctx = f->ctx;
170     provider_ctx *pctx;
171     mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
172                                                 &filter_module);
173
174     /* Check registered providers in order */
175     for (provider = filter->providers; provider; provider = provider->next) {
176         match = 1;
177         switch (provider->dispatch) {
178         case REQUEST_HEADERS:
179             str = apr_table_get(r->headers_in, provider->value);
180             break;
181         case RESPONSE_HEADERS:
182             str = apr_table_get(r->headers_out, provider->value);
183             break;
184         case SUBPROCESS_ENV:
185             str = apr_table_get(r->subprocess_env, provider->value);
186             break;
187         case CONTENT_TYPE:
188             str = r->content_type;
189             break;
190         case HANDLER:
191             str = r->handler;
192             break;
193         }
194
195         /* treat nulls so we don't have to check every strcmp individually
196          * Not sure if there's anything better to do with them
197          */
198         if (!str) {
199             if (provider->match_type == DEFINED && provider->match.string) {
200                 match = 0;
201             }
202         }
203         /* we can't check for NULL in provider as that kills integer 0
204          * so we have to test each string/regexp case in the switch
205          */
206         else {
207             switch (provider->match_type) {
208             case STRING_MATCH:
209                 if (strcasecmp(str, provider->match.string)) {
210                     match = 0;
211                 }
212                 break;
213             case STRING_CONTAINS:
214                 str1 = apr_pstrdup(r->pool, str);
215                 ap_str_tolower(str1);
216                 if (!strstr(str1, provider->match.string)) {
217                     match = 0;
218                 }
219                 break;
220             case REGEX_MATCH:
221                 if (ap_regexec(provider->match.regex, str, 0, NULL, 0)
222                     == AP_REG_NOMATCH) {
223                     match = 0;
224                 }
225                 break;
226             case INT_EQ:
227                 if (atoi(str) != provider->match.number) {
228                     match = 0;
229                 }
230                 break;
231             /* Integer comparisons should be [var] OP [match]
232              * We need to set match = 0 if the condition fails
233              */
234             case INT_LT:
235                 if (atoi(str) >= provider->match.number) {
236                     match = 0;
237                 }
238                 break;
239             case INT_LE:
240                 if (atoi(str) > provider->match.number) {
241                     match = 0;
242                 }
243                 break;
244             case INT_GT:
245                 if (atoi(str) <= provider->match.number) {
246                     match = 0;
247                 }
248                 break;
249             case INT_GE:
250                 if (atoi(str) < provider->match.number) {
251                     match = 0;
252                 }
253                 break;
254             case DEFINED:        /* we already handled this:-) */
255                 break;
256             }
257         }
258
259         if (match != provider->not) {
260             /* condition matches this provider */
261 #ifndef NO_PROTOCOL
262             /* check protocol
263              *
264              * FIXME:
265              * This is a quick hack and almost certainly buggy.
266              * The idea is that by putting this in mod_filter, we relieve
267              * filter implementations of the burden of fixing up HTTP headers
268              * for cases that are routinely affected by filters.
269              *
270              * Default is ALWAYS to do nothing, so as not to tread on the
271              * toes of filters which want to do it themselves.
272              *
273              */
274             proto_flags = provider->frec->proto_flags;
275
276             /* some specific things can't happen in a proxy */
277             if (r->proxyreq) {
278                 if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
279                     /* can't use this provider; try next */
280                     continue;
281                 }
282
283                 if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
284                     str = apr_table_get(r->headers_out, "Cache-Control");
285                     if (str) {
286                         str1 = apr_pstrdup(r->pool, str);
287                         ap_str_tolower(str1);
288                         if (strstr(str1, "no-transform")) {
289                             /* can't use this provider; try next */
290                             continue;
291                         }
292                     }
293                     apr_table_addn(r->headers_out, "Warning",
294                                    apr_psprintf(r->pool,
295                                                 "214 %s Transformation applied",
296                                                 r->hostname));
297                 }
298             }
299
300             /* things that are invalidated if the filter transforms content */
301             if (proto_flags & AP_FILTER_PROTO_CHANGE) {
302                 apr_table_unset(r->headers_out, "Content-MD5");
303                 apr_table_unset(r->headers_out, "ETag");
304                 if (proto_flags & AP_FILTER_PROTO_CHANGE_LENGTH) {
305                     apr_table_unset(r->headers_out, "Content-Length");
306                 }
307             }
308
309             /* no-cache is for a filter that has different effect per-hit */
310             if (proto_flags & AP_FILTER_PROTO_NO_CACHE) {
311                 apr_table_unset(r->headers_out, "Last-Modified");
312                 apr_table_addn(r->headers_out, "Cache-Control", "no-cache");
313             }
314
315             if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
316                 apr_table_unset(r->headers_out, "Accept-Ranges");
317             }
318             else if (rctx && rctx->range) {
319                 /* restore range header we saved earlier */
320                 apr_table_setn(r->headers_in, "Range", rctx->range);
321                 rctx->range = NULL;
322             }
323 #endif
324             for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
325                 if (pctx->provider == provider) {
326                     ctx->fctx = pctx->ctx ;
327                 }
328             }
329             ctx->func = provider->frec->filter_func.out_func;
330             return 1;
331         }
332     }
333
334     /* No provider matched */
335     return 0;
336 }
337
338 static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
339 {
340     apr_status_t ret;
341     const char *cachecontrol;
342     char *str;
343     harness_ctx *ctx = f->ctx;
344     ap_filter_rec_t *filter = f->frec;
345
346     if (f->r->status != 200) {
347         ap_remove_output_filter(f);
348         return ap_pass_brigade(f->next, bb);
349     }
350
351     filter_trace(f->c, filter->debug, f->frec->name, bb);
352
353     /* look up a handler function if we haven't already set it */
354     if (!ctx->func) {
355 #ifndef NO_PROTOCOL
356         if (f->r->proxyreq) {
357             if (filter->proto_flags & AP_FILTER_PROTO_NO_PROXY) {
358                 ap_remove_output_filter(f);
359                 return ap_pass_brigade(f->next, bb);
360             }
361
362             if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
363                 cachecontrol = apr_table_get(f->r->headers_out,
364                                              "Cache-Control");
365                 if (cachecontrol) {
366                     str = apr_pstrdup(f->r->pool,  cachecontrol);
367                     ap_str_tolower(str);
368                     if (strstr(str, "no-transform")) {
369                         ap_remove_output_filter(f);
370                         return ap_pass_brigade(f->next, bb);
371                     }
372                 }
373             }
374         }
375 #endif
376         if (!filter_lookup(f, filter)) {
377             ap_remove_output_filter(f);
378             return ap_pass_brigade(f->next, bb);
379         }
380     }
381
382     /* call the content filter with its own context, then restore our
383      * context
384      */
385     f->ctx = ctx->fctx;
386     ret = ctx->func(f, bb);
387     ctx->fctx = f->ctx;
388     f->ctx = ctx;
389
390     return ret;
391 }
392
393 #ifndef NO_PROTOCOL
394 static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
395                                    const char *pname, const char *proto)
396 {
397     static const char *sep = ";, \t";
398     char *arg;
399     char *tok = 0;
400     unsigned int flags = 0;
401     mod_filter_cfg *cfg = CFG;
402     ap_filter_provider_t *provider = NULL;
403     ap_filter_rec_t *filter = apr_hash_get(cfg->live_filters, fname,
404                                            APR_HASH_KEY_STRING);
405
406     if (!filter) {
407         return "FilterProtocol: No such filter";
408     }
409
410     /* Fixup the args: it's really pname that's optional */
411     if (proto == NULL) {
412         proto = pname;
413         pname = NULL;
414     }
415     else {
416         /* Find provider */
417         for (provider = filter->providers; provider; provider = provider->next){
418             if (!strcasecmp(provider->frec->name, pname)) {
419                 break;
420             }
421         }
422         if (!provider) {
423             return "FilterProtocol: No such provider for this filter";
424         }
425     }
426
427     /* Now set flags from our args */
428     for (arg = apr_strtok(apr_pstrdup(cmd->pool, proto), sep, &tok);
429          arg; arg = apr_strtok(NULL, sep, &tok)) {
430
431         if (!strcasecmp(arg, "change=yes")) {
432             flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
433         }
434         else if (!strcasecmp(arg, "change=1:1")) {
435             flags |= AP_FILTER_PROTO_CHANGE;
436         }
437         else if (!strcasecmp(arg, "byteranges=no")) {
438             flags |= AP_FILTER_PROTO_NO_BYTERANGE;
439         }
440         else if (!strcasecmp(arg, "proxy=no")) {
441             flags |= AP_FILTER_PROTO_NO_PROXY;
442         }
443         else if (!strcasecmp(arg, "proxy=transform")) {
444             flags |= AP_FILTER_PROTO_TRANSFORM;
445         }
446         else if (!strcasecmp(arg, "cache=no")) {
447             flags |= AP_FILTER_PROTO_NO_CACHE;
448         }
449     }
450
451     if (pname) {
452         provider->frec->proto_flags = flags;
453     }
454     else {
455         filter->proto_flags = flags;
456     }
457
458     return NULL;
459 }
460 #endif
461
462 static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
463                                   const char *place)
464 {
465     mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
466     ap_filter_rec_t *filter;
467
468     filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
469     apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
470
471     filter->name = fname;
472     filter->filter_init_func = filter_init;
473     filter->filter_func.out_func = filter_harness;
474     filter->ftype = AP_FTYPE_RESOURCE;
475     filter->next = NULL;
476
477     if (place) {
478         if (!strcasecmp(place, "CONTENT_SET")) {
479             filter->ftype = AP_FTYPE_CONTENT_SET;
480         }
481         else if (!strcasecmp(place, "PROTOCOL")) {
482             filter->ftype = AP_FTYPE_PROTOCOL;
483         }
484         else if (!strcasecmp(place, "CONNECTION")) {
485             filter->ftype = AP_FTYPE_CONNECTION;
486         }
487         else if (!strcasecmp(place, "NETWORK")) {
488             filter->ftype = AP_FTYPE_NETWORK;
489         }
490     }
491
492     return NULL;
493 }
494
495 static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args)
496 {
497     mod_filter_cfg *cfg = CFG;
498     int flags;
499     ap_filter_provider_t *provider;
500     const char *rxend;
501     const char *c;
502     char *str;
503     const char *eq;
504     ap_filter_rec_t* frec;
505     ap_filter_rec_t* provider_frec;
506
507     /* insist on exactly four arguments */
508     const char *fname = ap_getword_conf(cmd->pool, &args) ;
509     const char *pname = ap_getword_conf(cmd->pool, &args) ;
510     const char *condition = ap_getword_conf(cmd->pool, &args) ;
511     const char *match = ap_getword_conf(cmd->pool, &args) ;
512     eq = ap_getword_conf(cmd->pool, &args) ;
513     if ( !*fname || !*pname || !*match || !*condition || *eq ) {
514         return "usage: FilterProvider filter provider condition match" ;
515     }
516
517     /* fname has been declared with DeclareFilter, so we can look it up */
518     frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
519
520     /* or if provider is mod_filter itself, we can also look it up */
521     if (!frec) {
522         c = filter_declare(cmd, CFG, fname, NULL);
523         if ( c ) {
524             return c;
525         }
526         frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
527     }
528
529     if (!frec) {
530         return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
531     }
532
533     /* if provider has been registered, we can look it up */
534     provider_frec = ap_get_output_filter_handle(pname);
535     if (!provider_frec) {
536         provider_frec = apr_hash_get(cfg->live_filters, pname,
537                                      APR_HASH_KEY_STRING);
538     }
539     if (!provider_frec) {
540         return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
541     }
542
543     provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
544     if (*match == '!') {
545         provider->not = 1;
546         ++match;
547     }
548     else {
549         provider->not = 0;
550     }
551
552     switch (*match++) {
553     case '<':
554         if (*match == '=') {
555             provider->match_type = INT_LE;
556             ++match;
557         }
558         else {
559             provider->match_type = INT_LT;
560         }
561         provider->match.number = atoi(match);
562         break;
563     case '>':
564         if (*match == '=') {
565             provider->match_type = INT_GE;
566             ++match;
567         }
568         else {
569             provider->match_type = INT_GT;
570         }
571         provider->match.number = atoi(match);
572         break;
573     case '=':
574         provider->match_type = INT_EQ;
575         provider->match.number = atoi(match);
576         break;
577     case '/':
578         provider->match_type = REGEX_MATCH;
579         rxend = ap_strchr_c(match, '/');
580         if (!rxend) {
581               return "Bad regexp syntax";
582         }
583         flags = AP_REG_NOSUB;        /* we're not mod_rewrite:-) */
584         for (c = rxend+1; *c; ++c) {
585             switch (*c) {
586             case 'i': flags |= AP_REG_ICASE; break;
587             }
588         }
589         provider->match.regex = ap_pregcomp(cmd->pool,
590                                             apr_pstrndup(cmd->pool,
591                                                          match,
592                                                          rxend-match),
593                                             flags);
594         if (provider->match.regex == NULL) {
595             return "Bad regexp";
596         }
597         break;
598     case '*':
599         provider->match_type = DEFINED;
600         provider->match.number = -1;
601         break;
602     case '$':
603         provider->match_type = STRING_CONTAINS;
604         str = apr_pstrdup(cmd->pool, match);
605         ap_str_tolower(str);
606         provider->match.string = str;
607         break;
608     default:
609         provider->match_type = STRING_MATCH;
610         provider->match.string = apr_pstrdup(cmd->pool, match-1);
611         break;
612     }
613     provider->frec = provider_frec;
614     provider->next = frec->providers;
615     frec->providers = provider;
616
617     /* determine what a filter will dispatch this provider on */
618     eq = ap_strchr_c(condition, '=');
619     if (eq) {
620         str = apr_pstrdup(cmd->pool, eq+1);
621         if (!strncasecmp(condition, "env=", 4)) {
622             provider->dispatch = SUBPROCESS_ENV;
623         }
624         else if (!strncasecmp(condition, "req=", 4)) {
625             provider->dispatch = REQUEST_HEADERS;
626         }
627         else if (!strncasecmp(condition, "resp=", 5)) {
628             provider->dispatch = RESPONSE_HEADERS;
629         }
630         else {
631             return "FilterProvider: unrecognized dispatch table";
632         }
633     }
634     else {
635         if (!strcasecmp(condition, "handler")) {
636             provider->dispatch = HANDLER;
637         }
638         else {
639             provider->dispatch = RESPONSE_HEADERS;
640         }
641         str = apr_pstrdup(cmd->pool, condition);
642         ap_str_tolower(str);
643     }
644
645     if (   (provider->dispatch == RESPONSE_HEADERS)
646         && !strcasecmp(str, "content-type")) {
647         provider->dispatch = CONTENT_TYPE;
648     }
649     provider->value = str;
650
651     return NULL;
652 }
653
654 static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
655 {
656     mod_filter_chain *p;
657     mod_filter_chain *q;
658     mod_filter_cfg *cfg = CFG;
659
660     switch (arg[0]) {
661     case '+':        /* add to end of chain */
662         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
663         p->fname = arg+1;
664         if (cfg->chain) {
665             for (q = cfg->chain; q->next; q = q->next);
666             q->next = p;
667         }
668         else {
669             cfg->chain = p;
670         }
671         break;
672
673     case '@':        /* add to start of chain */
674         p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
675         p->fname = arg+1;
676         p->next = cfg->chain;
677         cfg->chain = p;
678         break;
679
680     case '-':        /* remove from chain */
681         if (cfg->chain) {
682             if (strcasecmp(cfg->chain->fname, arg+1)) {
683                 for (p = cfg->chain; p->next; p = p->next) {
684                     if (!strcasecmp(p->next->fname, arg+1)) {
685                         p->next = p->next->next;
686                     }
687                 }
688             }
689             else {
690                 cfg->chain = cfg->chain->next;
691             }
692         }
693         break;
694
695     case '!':        /* Empty the chain */
696                      /** IG: Add a NULL provider to the beginning so that 
697                       *  we can ensure that we'll empty everything before 
698                       *  this when doing config merges later */
699         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
700         p->fname = NULL;
701         cfg->chain = p;
702         break;
703
704     case '=':        /* initialise chain with this arg */
705                      /** IG: Prepend a NULL provider to the beginning as above */
706         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
707         p->fname = NULL;
708         p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
709         p->next->fname = arg+1;
710         cfg->chain = p;
711         break;
712
713     default:        /* add to end */
714         p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
715         p->fname = arg;
716         if (cfg->chain) {
717             for (q = cfg->chain; q->next; q = q->next);
718             q->next = p;
719         }
720         else {
721             cfg->chain = p;
722         }
723         break;
724     }
725
726     return NULL;
727 }
728
729 static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
730                                 const char *level)
731 {
732     mod_filter_cfg *cfg = CFG;
733     ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
734                                          APR_HASH_KEY_STRING);
735     if (!frec) {
736         return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
737     }
738     frec->debug = atoi(level);
739
740     return NULL;
741 }
742
743 static void filter_insert(request_rec *r)
744 {
745     mod_filter_chain *p;
746     ap_filter_rec_t *filter;
747     mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
748                                                &filter_module);
749 #ifndef NO_PROTOCOL
750     int ranges = 1;
751     mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
752     ap_set_module_config(r->request_config, &filter_module, ctx);
753 #endif
754
755     /** IG: Now that we've merged to the final config, go one last time
756      *  through the chain, and prune out the NULL filters */
757
758     for (p = cfg->chain; p; p = p->next) {
759         if (p->fname == NULL) 
760             cfg->chain = p->next;
761     }
762
763     for (p = cfg->chain; p; p = p->next) {
764         filter = apr_hash_get(cfg->live_filters, p->fname, APR_HASH_KEY_STRING);
765         if (filter == NULL) {
766             ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
767                           "Unknown filter %s not added", p->fname);
768             continue;
769         }
770         ap_add_output_filter_handle(filter, NULL, r, r->connection);
771 #ifndef NO_PROTOCOL
772         if (ranges && (filter->proto_flags
773                        & (AP_FILTER_PROTO_NO_BYTERANGE
774                           | AP_FILTER_PROTO_CHANGE_LENGTH))) {
775             ctx->range = apr_table_get(r->headers_in, "Range");
776             apr_table_unset(r->headers_in, "Range");
777             ranges = 0;
778         }
779 #endif
780     }
781
782     return;
783 }
784
785 static void filter_hooks(apr_pool_t *pool)
786 {
787     ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
788 }
789
790 static void *filter_config(apr_pool_t *pool, char *x)
791 {
792     mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
793     cfg->live_filters = apr_hash_make(pool);
794     cfg->chain = NULL;
795     return cfg;
796 }
797
798 static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
799 {
800     mod_filter_cfg *base = BASE;
801     mod_filter_cfg *add = ADD;
802     mod_filter_chain *savelink = 0;
803     mod_filter_chain *newlink;
804     mod_filter_chain *p;
805     mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
806
807     conf->live_filters = apr_hash_overlay(pool, add->live_filters,
808                                           base->live_filters);
809     if (base->chain && add->chain) {
810         for (p = base->chain; p; p = p->next) {
811             newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
812             if (newlink->fname == NULL) {
813                 conf->chain = savelink = newlink;
814             }
815             else if (savelink) {
816                 savelink->next = newlink;
817                 savelink = newlink;
818             }
819             else {
820                 conf->chain = savelink = newlink;
821             }
822         }
823
824         for (p = add->chain; p; p = p->next) {
825             newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
826             /** Filter out merged chain resets */
827             if (newlink->fname == NULL) {
828                 conf->chain = savelink = newlink;
829             }
830             else if (savelink) {
831                 savelink->next = newlink;
832                 savelink = newlink;
833             }
834             else {
835                 conf->chain = savelink = newlink;
836             }
837         }
838     }
839     else if (add->chain) {
840         conf->chain = add->chain;
841     }
842     else {
843         conf->chain = base->chain;
844     }
845
846     return conf;
847 }
848
849 static const command_rec filter_cmds[] = {
850     AP_INIT_TAKE12("FilterDeclare", filter_declare, NULL, OR_OPTIONS,
851         "filter-name [filter-type]"),
852     /** we don't have a TAKE4, so we have to use RAW_ARGS */
853     AP_INIT_RAW_ARGS("FilterProvider", filter_provider, NULL, OR_OPTIONS,
854         "filter-name provider-name dispatch-criterion dispatch-match"),
855     AP_INIT_ITERATE("FilterChain", filter_chain, NULL, OR_OPTIONS,
856         "list of filter names with optional [+-=!@]"),
857     AP_INIT_TAKE2("FilterTrace", filter_debug, NULL, RSRC_CONF | ACCESS_CONF,
858         "filter-name debug-level"),
859 #ifndef NO_PROTOCOL
860     AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
861         "filter-name [provider-name] protocol-args"),
862 #endif
863     { NULL }
864 };
865
866 module AP_MODULE_DECLARE_DATA filter_module = {
867     STANDARD20_MODULE_STUFF,
868     filter_config,
869     filter_merge,
870     NULL,
871     NULL,
872     filter_cmds,
873     filter_hooks
874 };