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