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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
17
#define APR_WANT_STRFUNC
20
#include "apr_strings.h"
23
#include "http_config.h"
24
#include "http_request.h"
26
#include "util_filter.h"
28
module AP_MODULE_DECLARE_DATA filter_module;
31
* @brief is a filter provider, as defined and implemented by mod_filter.
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.
37
struct ap_filter_provider_t {
38
/** How to match this provider to filter dispatch criterion */
51
/** negation on match_type */
54
/** The dispatch match itself - union member depends on match_type */
61
/** The filter that implements this provider */
62
ap_filter_rec_t *frec;
64
/** The next provider in the list */
65
ap_filter_provider_t *next;
67
/** Dispatch criteria for filter providers */
76
/** Match value for filter providers */
80
/** we need provider_ctx to save ctx values set by providers in filter_init */
81
typedef struct provider_ctx provider_ctx;
83
ap_filter_provider_t *provider;
88
ap_out_filter_func func;
90
provider_ctx *init_ctx;
93
typedef struct mod_filter_chain {
95
struct mod_filter_chain *next;
99
apr_hash_t *live_filters;
100
mod_filter_chain *chain;
108
static void filter_trace(conn_rec *c, int debug, const char *fname,
109
apr_bucket_brigade *bb)
114
case 0: /* normal, operational use */
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)) {
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)",
131
static int filter_init(ap_filter_t *f)
133
ap_filter_provider_t *p;
136
ap_filter_rec_t *filter = f->frec;
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) {
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 */
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));
152
pctx->next = fctx->init_ctx;
153
fctx->init_ctx = pctx;
161
static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
163
ap_filter_provider_t *provider;
164
const char *str = NULL;
167
unsigned int proto_flags;
168
request_rec *r = f->r;
169
harness_ctx *ctx = f->ctx;
171
mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
174
/* Check registered providers in order */
175
for (provider = filter->providers; provider; provider = provider->next) {
177
switch (provider->dispatch) {
178
case REQUEST_HEADERS:
179
str = apr_table_get(r->headers_in, provider->value);
181
case RESPONSE_HEADERS:
182
str = apr_table_get(r->headers_out, provider->value);
185
str = apr_table_get(r->subprocess_env, provider->value);
188
str = r->content_type;
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
199
if (provider->match_type == DEFINED && provider->match.string) {
203
else if (!provider->match.string) {
207
/* Now we have no nulls, so we can do string and regexp matching */
208
switch (provider->match_type) {
210
if (strcasecmp(str, provider->match.string)) {
214
case STRING_CONTAINS:
215
str1 = apr_pstrdup(r->pool, str);
216
ap_str_tolower(str1);
217
if (!strstr(str1, provider->match.string)) {
222
if (ap_regexec(provider->match.regex, str, 0, NULL, 0)
228
if (atoi(str) != provider->match.number) {
233
if (atoi(str) < provider->match.number) {
238
if (atoi(str) <= provider->match.number) {
243
if (atoi(str) > provider->match.number) {
248
if (atoi(str) >= provider->match.number) {
252
case DEFINED: /* we already handled this:-) */
257
if (match != provider->not) {
258
/* condition matches this provider */
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.
268
* Default is ALWAYS to do nothing, so as not to tread on the
269
* toes of filters which want to do it themselves.
272
proto_flags = provider->frec->proto_flags;
274
/* some specific things can't happen in a proxy */
276
if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
277
/* can't use this provider; try next */
281
if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
282
str = apr_table_get(r->headers_out, "Cache-Control");
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 */
291
apr_table_addn(r->headers_out, "Warning",
292
apr_psprintf(r->pool,
293
"214 %s Transformation applied",
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");
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");
313
if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
314
apr_table_unset(r->headers_out, "Accept-Ranges");
316
else if (rctx && rctx->range) {
317
/* restore range header we saved earlier */
318
apr_table_setn(r->headers_in, "Range", rctx->range);
322
for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
323
if (pctx->provider == provider) {
324
ctx->fctx = pctx->ctx ;
327
ctx->func = provider->frec->filter_func.out_func;
332
/* No provider matched */
336
static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
339
const char *cachecontrol;
341
harness_ctx *ctx = f->ctx;
342
ap_filter_rec_t *filter = f->frec;
344
if (f->r->status != 200) {
345
ap_remove_output_filter(f);
346
return ap_pass_brigade(f->next, bb);
349
filter_trace(f->c, filter->debug, f->frec->name, bb);
351
/* look up a handler function if we haven't already set it */
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);
360
if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
361
cachecontrol = apr_table_get(f->r->headers_out,
364
str = apr_pstrdup(f->r->pool, cachecontrol);
366
if (strstr(str, "no-transform")) {
367
ap_remove_output_filter(f);
368
return ap_pass_brigade(f->next, bb);
374
if (!filter_lookup(f, filter)) {
375
ap_remove_output_filter(f);
376
return ap_pass_brigade(f->next, bb);
380
/* call the content filter with its own context, then restore our
384
ret = ctx->func(f, bb);
392
static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
393
const char *pname, const char *proto)
395
static const char *sep = ";, \t";
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);
405
return "FilterProtocol: No such filter";
408
/* Fixup the args: it's really pname that's optional */
415
for (provider = filter->providers; provider; provider = provider->next){
416
if (!strcasecmp(provider->frec->name, pname)) {
421
return "FilterProtocol: No such provider for this filter";
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)) {
429
if (!strcasecmp(arg, "change=yes")) {
430
flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
432
else if (!strcasecmp(arg, "change=1:1")) {
433
flags |= AP_FILTER_PROTO_CHANGE;
435
else if (!strcasecmp(arg, "byteranges=no")) {
436
flags |= AP_FILTER_PROTO_NO_BYTERANGE;
438
else if (!strcasecmp(arg, "proxy=no")) {
439
flags |= AP_FILTER_PROTO_NO_PROXY;
441
else if (!strcasecmp(arg, "proxy=transform")) {
442
flags |= AP_FILTER_PROTO_TRANSFORM;
444
else if (!strcasecmp(arg, "cache=no")) {
445
flags |= AP_FILTER_PROTO_NO_CACHE;
450
provider->frec->proto_flags = flags;
453
filter->proto_flags = flags;
460
static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
463
mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
464
ap_filter_rec_t *filter;
466
filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
467
apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
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;
476
if (!strcasecmp(place, "CONTENT_SET")) {
477
filter->ftype = AP_FTYPE_CONTENT_SET;
479
else if (!strcasecmp(place, "PROTOCOL")) {
480
filter->ftype = AP_FTYPE_PROTOCOL;
482
else if (!strcasecmp(place, "CONNECTION")) {
483
filter->ftype = AP_FTYPE_CONNECTION;
485
else if (!strcasecmp(place, "NETWORK")) {
486
filter->ftype = AP_FTYPE_NETWORK;
493
static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args)
495
mod_filter_cfg *cfg = CFG;
497
ap_filter_provider_t *provider;
502
ap_filter_rec_t* frec;
503
ap_filter_rec_t* provider_frec;
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" ;
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);
518
/* or if provider is mod_filter itself, we can also look it up */
520
c = filter_declare(cmd, CFG, fname, NULL);
524
frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
528
return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
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);
537
if (!provider_frec) {
538
return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
541
provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
553
provider->match_type = INT_LE;
557
provider->match_type = INT_LT;
559
provider->match.number = atoi(match);
563
provider->match_type = INT_GE;
567
provider->match_type = INT_GT;
569
provider->match.number = atoi(match);
572
provider->match_type = INT_EQ;
573
provider->match.number = atoi(match);
576
provider->match_type = REGEX_MATCH;
577
rxend = ap_strchr_c(match, '/');
579
return "Bad regexp syntax";
581
flags = AP_REG_NOSUB; /* we're not mod_rewrite:-) */
582
for (c = rxend+1; *c; ++c) {
584
case 'i': flags |= AP_REG_ICASE; break;
587
provider->match.regex = ap_pregcomp(cmd->pool,
588
apr_pstrndup(cmd->pool,
594
provider->match_type = DEFINED;
595
provider->match.number = -1;
598
provider->match_type = STRING_CONTAINS;
599
str = apr_pstrdup(cmd->pool, match);
601
provider->match.string = str;
604
provider->match_type = STRING_MATCH;
605
provider->match.string = apr_pstrdup(cmd->pool, match-1);
608
provider->frec = provider_frec;
609
provider->next = frec->providers;
610
frec->providers = provider;
612
/* determine what a filter will dispatch this provider on */
613
eq = ap_strchr_c(condition, '=');
615
str = apr_pstrdup(cmd->pool, eq+1);
616
if (!strncasecmp(condition, "env=", 4)) {
617
provider->dispatch = SUBPROCESS_ENV;
619
else if (!strncasecmp(condition, "req=", 4)) {
620
provider->dispatch = REQUEST_HEADERS;
622
else if (!strncasecmp(condition, "resp=", 5)) {
623
provider->dispatch = RESPONSE_HEADERS;
626
return "FilterProvider: unrecognized dispatch table";
630
if (!strcasecmp(condition, "handler")) {
631
provider->dispatch = HANDLER;
634
provider->dispatch = RESPONSE_HEADERS;
636
str = apr_pstrdup(cmd->pool, condition);
640
if ( (provider->dispatch == RESPONSE_HEADERS)
641
&& !strcmp(str, "content-type")) {
642
provider->dispatch = CONTENT_TYPE;
644
provider->value = str;
649
static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
653
mod_filter_cfg *cfg = CFG;
656
case '+': /* add to end of chain */
657
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
660
for (q = cfg->chain; q->next; q = q->next);
668
case '@': /* add to start of chain */
669
p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
671
p->next = cfg->chain;
675
case '-': /* remove from 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;
685
cfg->chain = cfg->chain->next;
690
case '!': /* Empty the chain */
694
case '=': /* initialise chain with this arg */
695
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
700
default: /* add to end */
701
p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
704
for (q = cfg->chain; q->next; q = q->next);
716
static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
719
mod_filter_cfg *cfg = CFG;
720
ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
721
APR_HASH_KEY_STRING);
723
return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
725
frec->debug = atoi(level);
730
static void filter_insert(request_rec *r)
733
ap_filter_rec_t *filter;
734
mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
738
mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
739
ap_set_module_config(r->request_config, &filter_module, ctx);
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);
749
ap_add_output_filter_handle(filter, NULL, r, r->connection);
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");
764
static void filter_hooks(apr_pool_t *pool)
766
ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
769
static void *filter_config(apr_pool_t *pool, char *x)
771
mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
772
cfg->live_filters = apr_hash_make(pool);
777
static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
779
mod_filter_cfg *base = BASE;
780
mod_filter_cfg *add = ADD;
781
mod_filter_chain *savelink = 0;
782
mod_filter_chain *newlink;
784
mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
786
conf->live_filters = apr_hash_overlay(pool, add->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));
792
savelink->next = newlink;
796
conf->chain = savelink = newlink;
800
for (p = add->chain; p; p = p->next) {
801
newlink = apr_pmemdup(pool, p, sizeof(mod_filter_chain));
802
savelink->next = newlink;
806
else if (add->chain) {
807
conf->chain = add->chain;
810
conf->chain = base->chain;
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,
827
AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
828
"filter-name [provider-name] protocol-args"),
833
module AP_MODULE_DECLARE_DATA filter_module = {
834
STANDARD20_MODULE_STUFF,