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