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 == 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;
145 else if (p->frec->filter_init_func) {
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 */
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));
157 pctx->next = fctx->init_ctx;
158 fctx->init_ctx = pctx;
166 static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter)
168 ap_filter_provider_t *provider;
169 const char *str = NULL;
172 unsigned int proto_flags;
173 request_rec *r = f->r;
174 harness_ctx *ctx = f->ctx;
176 mod_filter_ctx *rctx = ap_get_module_config(r->request_config,
179 /* Check registered providers in order */
180 for (provider = filter->providers; provider; provider = provider->next) {
182 switch (provider->dispatch) {
183 case REQUEST_HEADERS:
184 str = apr_table_get(r->headers_in, provider->value);
186 case RESPONSE_HEADERS:
187 str = apr_table_get(r->headers_out, provider->value);
190 str = apr_table_get(r->subprocess_env, provider->value);
193 str = r->content_type;
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
204 if (provider->match_type == DEFINED && provider->match.string) {
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
212 switch (provider->match_type) {
214 if (strcasecmp(str, provider->match.string)) {
218 case STRING_CONTAINS:
219 str1 = apr_pstrdup(r->pool, str);
220 ap_str_tolower(str1);
221 if (!strstr(str1, provider->match.string)) {
226 if (ap_regexec(provider->match.regex, str, 0, NULL, 0)
232 if (atoi(str) != provider->match.number) {
236 /* Integer comparisons should be [var] OP [match]
237 * We need to set match = 0 if the condition fails
240 if (atoi(str) >= provider->match.number) {
245 if (atoi(str) > provider->match.number) {
250 if (atoi(str) <= provider->match.number) {
255 if (atoi(str) < provider->match.number) {
259 case DEFINED: /* we already handled this:-) */
264 if (match != provider->not) {
265 /* condition matches this provider */
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.
275 * Default is ALWAYS to do nothing, so as not to tread on the
276 * toes of filters which want to do it themselves.
279 proto_flags = provider->frec->proto_flags;
281 /* some specific things can't happen in a proxy */
283 if (proto_flags & AP_FILTER_PROTO_NO_PROXY) {
284 /* can't use this provider; try next */
288 if (proto_flags & AP_FILTER_PROTO_TRANSFORM) {
289 str = apr_table_get(r->headers_out, "Cache-Control");
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 */
298 apr_table_addn(r->headers_out, "Warning",
299 apr_psprintf(r->pool,
300 "214 %s Transformation applied",
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");
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");
320 if (proto_flags & AP_FILTER_PROTO_NO_BYTERANGE) {
321 apr_table_unset(r->headers_out, "Accept-Ranges");
323 else if (rctx && rctx->range) {
324 /* restore range header we saved earlier */
325 apr_table_setn(r->headers_in, "Range", rctx->range);
329 for (pctx = ctx->init_ctx; pctx; pctx = pctx->next) {
330 if (pctx->provider == provider) {
331 ctx->fctx = pctx->ctx ;
334 ctx->func = provider->frec->filter_func.out_func;
339 /* No provider matched */
343 static apr_status_t filter_harness(ap_filter_t *f, apr_bucket_brigade *bb)
346 const char *cachecontrol;
348 harness_ctx *ctx = f->ctx;
349 ap_filter_rec_t *filter = f->frec;
351 if (f->r->status != 200) {
352 ap_remove_output_filter(f);
353 return ap_pass_brigade(f->next, bb);
356 filter_trace(f->c, filter->debug, f->frec->name, bb);
358 /* look up a handler function if we haven't already set it */
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);
367 if (filter->proto_flags & AP_FILTER_PROTO_TRANSFORM) {
368 cachecontrol = apr_table_get(f->r->headers_out,
371 str = apr_pstrdup(f->r->pool, cachecontrol);
373 if (strstr(str, "no-transform")) {
374 ap_remove_output_filter(f);
375 return ap_pass_brigade(f->next, bb);
381 if (!filter_lookup(f, filter)) {
382 ap_remove_output_filter(f);
383 return ap_pass_brigade(f->next, bb);
387 /* call the content filter with its own context, then restore our
391 ret = ctx->func(f, bb);
399 static const char *filter_protocol(cmd_parms *cmd, void *CFG, const char *fname,
400 const char *pname, const char *proto)
402 static const char *sep = ";, \t";
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);
412 return "FilterProtocol: No such filter";
415 /* Fixup the args: it's really pname that's optional */
422 for (provider = filter->providers; provider; provider = provider->next){
423 if (!strcasecmp(provider->frec->name, pname)) {
428 return "FilterProtocol: No such provider for this filter";
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)) {
436 if (!strcasecmp(arg, "change=yes")) {
437 flags |= AP_FILTER_PROTO_CHANGE | AP_FILTER_PROTO_CHANGE_LENGTH;
439 else if (!strcasecmp(arg, "change=1:1")) {
440 flags |= AP_FILTER_PROTO_CHANGE;
442 else if (!strcasecmp(arg, "byteranges=no")) {
443 flags |= AP_FILTER_PROTO_NO_BYTERANGE;
445 else if (!strcasecmp(arg, "proxy=no")) {
446 flags |= AP_FILTER_PROTO_NO_PROXY;
448 else if (!strcasecmp(arg, "proxy=transform")) {
449 flags |= AP_FILTER_PROTO_TRANSFORM;
451 else if (!strcasecmp(arg, "cache=no")) {
452 flags |= AP_FILTER_PROTO_NO_CACHE;
457 provider->frec->proto_flags = flags;
460 filter->proto_flags = flags;
467 static const char *filter_declare(cmd_parms *cmd, void *CFG, const char *fname,
470 mod_filter_cfg *cfg = (mod_filter_cfg *)CFG;
471 ap_filter_rec_t *filter;
473 filter = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
474 apr_hash_set(cfg->live_filters, fname, APR_HASH_KEY_STRING, filter);
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;
483 if (!strcasecmp(place, "CONTENT_SET")) {
484 filter->ftype = AP_FTYPE_CONTENT_SET;
486 else if (!strcasecmp(place, "PROTOCOL")) {
487 filter->ftype = AP_FTYPE_PROTOCOL;
489 else if (!strcasecmp(place, "CONNECTION")) {
490 filter->ftype = AP_FTYPE_CONNECTION;
492 else if (!strcasecmp(place, "NETWORK")) {
493 filter->ftype = AP_FTYPE_NETWORK;
500 static const char *filter_provider(cmd_parms *cmd, void *CFG, const char *args)
502 mod_filter_cfg *cfg = CFG;
504 ap_filter_provider_t *provider;
509 ap_filter_rec_t* frec;
510 ap_filter_rec_t* provider_frec;
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" ;
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);
525 /* or if provider is mod_filter itself, we can also look it up */
527 c = filter_declare(cmd, CFG, fname, NULL);
531 frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
535 return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
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);
544 if (!provider_frec) {
545 return apr_psprintf(cmd->pool, "Unknown filter provider %s", pname);
548 provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
560 provider->match_type = INT_LE;
564 provider->match_type = INT_LT;
566 provider->match.number = atoi(match);
570 provider->match_type = INT_GE;
574 provider->match_type = INT_GT;
576 provider->match.number = atoi(match);
579 provider->match_type = INT_EQ;
580 provider->match.number = atoi(match);
583 provider->match_type = REGEX_MATCH;
584 rxend = ap_strchr_c(match, '/');
586 return "Bad regexp syntax";
588 flags = AP_REG_NOSUB; /* we're not mod_rewrite:-) */
589 for (c = rxend+1; *c; ++c) {
591 case 'i': flags |= AP_REG_ICASE; break;
594 provider->match.regex = ap_pregcomp(cmd->pool,
595 apr_pstrndup(cmd->pool,
599 if (provider->match.regex == NULL) {
604 provider->match_type = DEFINED;
605 provider->match.number = -1;
608 provider->match_type = STRING_CONTAINS;
609 str = apr_pstrdup(cmd->pool, match);
611 provider->match.string = str;
614 provider->match_type = STRING_MATCH;
615 provider->match.string = apr_pstrdup(cmd->pool, match-1);
618 provider->frec = provider_frec;
619 provider->next = frec->providers;
620 frec->providers = provider;
622 /* determine what a filter will dispatch this provider on */
623 eq = ap_strchr_c(condition, '=');
625 str = apr_pstrdup(cmd->pool, eq+1);
626 if (!strncasecmp(condition, "env=", 4)) {
627 provider->dispatch = SUBPROCESS_ENV;
629 else if (!strncasecmp(condition, "req=", 4)) {
630 provider->dispatch = REQUEST_HEADERS;
632 else if (!strncasecmp(condition, "resp=", 5)) {
633 provider->dispatch = RESPONSE_HEADERS;
636 return "FilterProvider: unrecognized dispatch table";
640 if (!strcasecmp(condition, "handler")) {
641 provider->dispatch = HANDLER;
644 provider->dispatch = RESPONSE_HEADERS;
646 str = apr_pstrdup(cmd->pool, condition);
650 if ( (provider->dispatch == RESPONSE_HEADERS)
651 && !strcasecmp(str, "content-type")) {
652 provider->dispatch = CONTENT_TYPE;
654 provider->value = str;
659 static const char *filter_chain(cmd_parms *cmd, void *CFG, const char *arg)
663 mod_filter_cfg *cfg = CFG;
666 case '+': /* add to end of chain */
667 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
670 for (q = cfg->chain; q->next; q = q->next);
678 case '@': /* add to start of chain */
679 p = apr_palloc(cmd->pool, sizeof(mod_filter_chain));
681 p->next = cfg->chain;
685 case '-': /* remove from 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;
695 cfg->chain = cfg->chain->next;
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));
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));
713 p->next = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
714 p->next->fname = arg+1;
718 default: /* add to end */
719 p = apr_pcalloc(cmd->pool, sizeof(mod_filter_chain));
722 for (q = cfg->chain; q->next; q = q->next);
734 static const char *filter_debug(cmd_parms *cmd, void *CFG, const char *fname,
737 mod_filter_cfg *cfg = CFG;
738 ap_filter_rec_t *frec = apr_hash_get(cfg->live_filters, fname,
739 APR_HASH_KEY_STRING);
741 return apr_psprintf(cmd->pool, "Undeclared smart filter %s", fname);
743 frec->debug = atoi(level);
748 static void filter_insert(request_rec *r)
751 ap_filter_rec_t *filter;
752 mod_filter_cfg *cfg = ap_get_module_config(r->per_dir_config,
756 mod_filter_ctx *ctx = apr_pcalloc(r->pool, sizeof(mod_filter_ctx));
757 ap_set_module_config(r->request_config, &filter_module, ctx);
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 */
763 for (p = cfg->chain; p; p = p->next) {
764 if (p->fname == NULL)
765 cfg->chain = p->next;
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);
775 ap_add_output_filter_handle(filter, NULL, r, r->connection);
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");
790 static void filter_hooks(apr_pool_t *pool)
792 ap_hook_insert_filter(filter_insert, NULL, NULL, APR_HOOK_MIDDLE);
795 static void *filter_config(apr_pool_t *pool, char *x)
797 mod_filter_cfg *cfg = apr_palloc(pool, sizeof(mod_filter_cfg));
798 cfg->live_filters = apr_hash_make(pool);
803 static void *filter_merge(apr_pool_t *pool, void *BASE, void *ADD)
805 mod_filter_cfg *base = BASE;
806 mod_filter_cfg *add = ADD;
807 mod_filter_chain *savelink = 0;
808 mod_filter_chain *newlink;
810 mod_filter_cfg *conf = apr_palloc(pool, sizeof(mod_filter_cfg));
812 conf->live_filters = apr_hash_overlay(pool, add->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;
821 savelink->next = newlink;
825 conf->chain = savelink = newlink;
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;
836 savelink->next = newlink;
840 conf->chain = savelink = newlink;
844 else if (add->chain) {
845 conf->chain = add->chain;
848 conf->chain = base->chain;
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"),
865 AP_INIT_TAKE23("FilterProtocol", filter_protocol, NULL, OR_OPTIONS,
866 "filter-name [provider-name] protocol-args"),
871 module AP_MODULE_DECLARE_DATA filter_module = {
872 STANDARD20_MODULE_STUFF,