~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/filters/mod_filter.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

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