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/*
18 * mod_headers.c: Add/append/remove HTTP response headers
19 *     Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
20 *
21 * The Header directive can be used to add/replace/remove HTTP headers
22 * within the response message.  The RequestHeader directive can be used
23 * to add/replace/remove HTTP headers before a request message is processed.
24 * Valid in both per-server and per-dir configurations.
25 *
26 * Syntax is:
27 *
28 *   Header action header value
29 *   RequestHeader action header value
30 *
31 * Where action is one of:
32 *     set    - set this header, replacing any old value
33 *     add    - add this header, possible resulting in two or more
34 *              headers with the same name
35 *     append - append this text onto any existing header of this same
36 *     merge  - merge this text onto any existing header of this same,
37 *              avoiding duplicate values
38 *     unset  - remove this header
39 *      edit  - transform the header value according to a regexp
40 *
41 * Where action is unset, the third argument (value) should not be given.
42 * The header name can include the colon, or not.
43 *
44 * The Header and RequestHeader directives can only be used where allowed
45 * by the FileInfo override.
46 *
47 * When the request is processed, the header directives are processed in
48 * this order: firstly, the main server, then the virtual server handling
49 * this request (if any), then any <Directory> sections (working downwards
50 * from the root dir), then an <Location> sections (working down from
51 * shortest URL component), the any <File> sections. This order is
52 * important if any 'set' or 'unset' actions are used. For example,
53 * the following two directives have different effect if applied in
54 * the reverse order:
55 *
56 *   Header append Author "John P. Doe"
57 *   Header unset Author
58 *
59 * Examples:
60 *
61 *  To set the "Author" header, use
62 *     Header add Author "John P. Doe"
63 *
64 *  To remove a header:
65 *     Header unset Author
66 *
67 */
68
69#include "apr.h"
70#include "apr_lib.h"
71#include "apr_strings.h"
72#include "apr_buckets.h"
73
74#include "apr_hash.h"
75#define APR_WANT_STRFUNC
76#include "apr_want.h"
77
78#include "httpd.h"
79#include "http_config.h"
80#include "http_request.h"
81#include "http_log.h"
82#include "util_filter.h"
83#include "http_protocol.h"
84#include "ap_expr.h"
85
86#include "mod_ssl.h" /* for the ssl_var_lookup optional function defn */
87
88/* format_tag_hash is initialized during pre-config */
89static apr_hash_t *format_tag_hash;
90
91typedef enum {
92    hdr_add = 'a',              /* add header (could mean multiple hdrs) */
93    hdr_set = 's',              /* set (replace old value) */
94    hdr_append = 'm',           /* append (merge into any old value) */
95    hdr_merge = 'g',            /* merge (merge, but avoid duplicates) */
96    hdr_unset = 'u',            /* unset header */
97    hdr_echo = 'e',             /* echo headers from request to response */
98    hdr_edit = 'r',             /* change value by regexp, match once */
99    hdr_edit_r = 'R',           /* change value by regexp, everymatch */
100    hdr_setifempty = 'i',       /* set value if header not already present*/
101    hdr_note = 'n'              /* set value of header in a note */
102} hdr_actions;
103
104/*
105 * magic cmd->info values
106 */
107static char hdr_in  = '0';  /* RequestHeader */
108static char hdr_out_onsuccess = '1';  /* Header onsuccess */
109static char hdr_out_always = '2';  /* Header always */
110
111/* Callback function type. */
112typedef const char *format_tag_fn(request_rec *r, char *a);
113
114/*
115 * There is an array of struct format_tag per Header/RequestHeader
116 * config directive
117 */
118typedef struct {
119    format_tag_fn *func;
120    char *arg;
121} format_tag;
122
123/* 'Magic' condition_var value to run action in post_read_request */
124static const char* condition_early = "early";
125/*
126 * There is one "header_entry" per Header/RequestHeader config directive
127 */
128typedef struct {
129    hdr_actions action;
130    const char *header;
131    apr_array_header_t *ta;   /* Array of format_tag structs */
132    ap_regex_t *regex;
133    const char *condition_var;
134    const char *subs;
135    ap_expr_info_t *expr;
136} header_entry;
137
138/* echo_do is used for Header echo to iterate through the request headers*/
139typedef struct {
140    request_rec *r;
141    header_entry *hdr;
142} echo_do;
143
144/* edit_do is used for Header edit to iterate through the request headers */
145typedef struct {
146    request_rec *r;
147    header_entry *hdr;
148    apr_table_t *t;
149} edit_do;
150
151/*
152 * headers_conf is our per-module configuration. This is used as both
153 * a per-dir and per-server config
154 */
155typedef struct {
156    apr_array_header_t *fixup_in;
157    apr_array_header_t *fixup_out;
158    apr_array_header_t *fixup_err;
159} headers_conf;
160
161module AP_MODULE_DECLARE_DATA headers_module;
162
163/* Pointer to ssl_var_lookup, if available. */
164static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL;
165
166/*
167 * Tag formatting functions
168 */
169static const char *constant_item(request_rec *r, char *stuff)
170{
171    return stuff;
172}
173static const char *header_request_duration(request_rec *r, char *a)
174{
175    return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT,
176                        (apr_time_now() - r->request_time));
177}
178static const char *header_request_time(request_rec *r, char *a)
179{
180    return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time);
181}
182
183/* unwrap_header returns HDR with any newlines converted into
184 * whitespace if necessary. */
185static const char *unwrap_header(apr_pool_t *p, const char *hdr)
186{
187    if (ap_strchr_c(hdr, APR_ASCII_LF) || ap_strchr_c(hdr, APR_ASCII_CR)) {
188        char *ptr;
189
190        hdr = ptr = apr_pstrdup(p, hdr);
191
192        do {
193            if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR)
194                *ptr = APR_ASCII_BLANK;
195        } while (*ptr++);
196    }
197    return hdr;
198}
199
200static const char *header_request_env_var(request_rec *r, char *a)
201{
202    const char *s = apr_table_get(r->subprocess_env,a);
203
204    if (s)
205        return unwrap_header(r->pool, s);
206    else
207        return "(null)";
208}
209
210static const char *header_request_ssl_var(request_rec *r, char *name)
211{
212    if (header_ssl_lookup) {
213        const char *val = header_ssl_lookup(r->pool, r->server,
214                                            r->connection, r, name);
215        if (val && val[0])
216            return unwrap_header(r->pool, val);
217        else
218            return "(null)";
219    }
220    else {
221        return "(null)";
222    }
223}
224
225static const char *header_request_loadavg(request_rec *r, char *a)
226{
227    ap_loadavg_t t;
228    ap_get_loadavg(&t);
229    return apr_psprintf(r->pool, "l=%.2f/%.2f/%.2f", t.loadavg,
230                        t.loadavg5, t.loadavg15);
231}
232
233static const char *header_request_idle(request_rec *r, char *a)
234{
235    ap_sload_t t;
236    ap_get_sload(&t);
237    return apr_psprintf(r->pool, "i=%d", t.idle);
238}
239
240static const char *header_request_busy(request_rec *r, char *a)
241{
242    ap_sload_t t;
243    ap_get_sload(&t);
244    return apr_psprintf(r->pool, "b=%d", t.busy);
245}
246
247/*
248 * Config routines
249 */
250
251static void *create_headers_dir_config(apr_pool_t *p, char *d)
252{
253    headers_conf *conf = apr_pcalloc(p, sizeof(*conf));
254
255    conf->fixup_in = apr_array_make(p, 2, sizeof(header_entry));
256    conf->fixup_out = apr_array_make(p, 2, sizeof(header_entry));
257    conf->fixup_err = apr_array_make(p, 2, sizeof(header_entry));
258
259    return conf;
260}
261
262static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
263{
264    headers_conf *newconf = apr_pcalloc(p, sizeof(*newconf));
265    headers_conf *base = basev;
266    headers_conf *overrides = overridesv;
267
268    newconf->fixup_in = apr_array_append(p, base->fixup_in,
269                                         overrides->fixup_in);
270    newconf->fixup_out = apr_array_append(p, base->fixup_out,
271                                          overrides->fixup_out);
272    newconf->fixup_err = apr_array_append(p, base->fixup_err,
273                                          overrides->fixup_err);
274
275    return newconf;
276}
277
278static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
279{
280    const char *s;
281    char *d;
282
283    tag->func = constant_item;
284
285    s = *sa;
286    while (*s && *s != '%') {
287        s++;
288    }
289    /*
290     * This might allocate a few chars extra if there's a backslash
291     * escape in the format string.
292     */
293    tag->arg = apr_palloc(p, s - *sa + 1);
294
295    d = tag->arg;
296    s = *sa;
297    while (*s && *s != '%') {
298        if (*s != '\\') {
299            *d++ = *s++;
300        }
301        else {
302            s++;
303            switch (*s) {
304            case '\\':
305                *d++ = '\\';
306                s++;
307                break;
308            case 'r':
309                *d++ = '\r';
310                s++;
311                break;
312            case 'n':
313                *d++ = '\n';
314                s++;
315                break;
316            case 't':
317                *d++ = '\t';
318                s++;
319                break;
320            default:
321                /* copy verbatim */
322                *d++ = '\\';
323                /*
324                 * Allow the loop to deal with this *s in the normal
325                 * fashion so that it handles end of string etc.
326                 * properly.
327                 */
328                break;
329            }
330        }
331    }
332    *d = '\0';
333
334    *sa = s;
335    return NULL;
336}
337
338static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
339{
340    const char *s = *sa;
341    const char * (*tag_handler)(request_rec *,char *);
342
343    /* Handle string literal/conditionals */
344    if (*s != '%') {
345        return parse_misc_string(p, tag, sa);
346    }
347    s++; /* skip the % */
348
349    /* Pass through %% or % at end of string as % */
350    if ((*s == '%') || (*s == '\0')) {
351        tag->func = constant_item;
352        tag->arg = "%";
353        if (*s)
354            s++;
355        *sa = s;
356        return NULL;
357    }
358
359    tag->arg = "\0";
360    /* grab the argument if there is one */
361    if (*s == '{') {
362        ++s;
363        tag->arg = ap_getword(p,&s,'}');
364    }
365
366    tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1);
367
368    if (!tag_handler) {
369        char dummy[2];
370        dummy[0] = s[-1];
371        dummy[1] = '\0';
372        return apr_pstrcat(p, "Unrecognized header format %", dummy, NULL);
373    }
374    tag->func = tag_handler;
375
376    *sa = s;
377    return NULL;
378}
379
380/*
381 * A format string consists of white space, text and optional format
382 * tags in any order. E.g.,
383 *
384 * Header add MyHeader "Free form text %D %t more text"
385 *
386 * Decompose the format string into its tags. Each tag (struct format_tag)
387 * contains a pointer to the function used to format the tag. Then save each
388 * tag in the tag array anchored in the header_entry.
389 */
390static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s)
391{
392    char *res;
393
394    /* No string to parse with unset and echo commands */
395    if (hdr->action == hdr_unset || hdr->action == hdr_echo) {
396        return NULL;
397    }
398    /* Tags are in the replacement value for edit */
399    else if (hdr->action == hdr_edit || hdr->action == hdr_edit_r ) {
400        s = hdr->subs;
401    }
402
403    hdr->ta = apr_array_make(p, 10, sizeof(format_tag));
404
405    while (*s) {
406        if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
407            return res;
408        }
409    }
410    return NULL;
411}
412
413/* handle RequestHeader and Header directive */
414static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd,
415                                               void *indirconf,
416                                               const char *action,
417                                               const char *hdr,
418                                               const char *value,
419                                               const char *subs,
420                                               const char *envclause)
421{
422    headers_conf *dirconf = indirconf;
423    const char *condition_var = NULL;
424    const char *colon;
425    header_entry *new;
426    ap_expr_info_t *expr = NULL;
427
428    apr_array_header_t *fixup = (cmd->info == &hdr_in)
429        ? dirconf->fixup_in   : (cmd->info == &hdr_out_always)
430        ? dirconf->fixup_err
431        : dirconf->fixup_out;
432
433    new = (header_entry *) apr_array_push(fixup);
434
435    if (!strcasecmp(action, "set"))
436        new->action = hdr_set;
437    else if (!strcasecmp(action, "setifempty"))
438        new->action = hdr_setifempty;
439    else if (!strcasecmp(action, "add"))
440        new->action = hdr_add;
441    else if (!strcasecmp(action, "append"))
442        new->action = hdr_append;
443    else if (!strcasecmp(action, "merge"))
444        new->action = hdr_merge;
445    else if (!strcasecmp(action, "unset"))
446        new->action = hdr_unset;
447    else if (!strcasecmp(action, "echo"))
448        new->action = hdr_echo;
449    else if (!strcasecmp(action, "edit"))
450        new->action = hdr_edit;
451    else if (!strcasecmp(action, "edit*"))
452        new->action = hdr_edit_r;
453    else if (!strcasecmp(action, "note"))
454        new->action = hdr_note;
455    else
456        return "first argument must be 'add', 'set', 'setifempty', 'append', 'merge', "
457               "'unset', 'echo', 'note', 'edit', or 'edit*'.";
458
459    if (new->action == hdr_edit || new->action == hdr_edit_r) {
460        if (subs == NULL) {
461            return "Header edit requires a match and a substitution";
462        }
463        new->regex = ap_pregcomp(cmd->pool, value, AP_REG_EXTENDED);
464        if (new->regex == NULL) {
465            return "Header edit regex could not be compiled";
466        }
467        new->subs = subs;
468    }
469    else {
470        /* there's no subs, so envclause is really that argument */
471        if (envclause != NULL) {
472            return "Too many arguments to directive";
473        }
474        envclause = subs;
475    }
476    if (new->action == hdr_unset) {
477        if (value) {
478            if (envclause) {
479                return "header unset takes two arguments";
480            }
481            envclause = value;
482            value = NULL;
483        }
484    }
485    else if (new->action == hdr_echo) {
486        ap_regex_t *regex;
487
488        if (value) {
489            if (envclause) {
490                return "Header echo takes two arguments";
491            }
492            envclause = value;
493            value = NULL;
494        }
495        if (cmd->info != &hdr_out_onsuccess && cmd->info != &hdr_out_always)
496            return "Header echo only valid on Header "
497                   "directives";
498        else {
499            regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB);
500            if (regex == NULL) {
501                return "Header echo regex could not be compiled";
502            }
503        }
504        new->regex = regex;
505    }
506    else if (!value)
507        return "Header requires three arguments";
508
509    /* Handle the envclause on Header */
510    if (envclause != NULL) {
511        if (strcasecmp(envclause, "early") == 0) {
512            condition_var = condition_early;
513        }
514        else if (strncasecmp(envclause, "env=", 4) == 0) {
515            if ((envclause[4] == '\0')
516                || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
517                return "error: missing environment variable name. "
518                    "envclause should be in the form env=envar ";
519            }
520            condition_var = envclause + 4;
521        }
522        else if (strncasecmp(envclause, "expr=", 5) == 0) {
523            const char *err = NULL;
524            expr = ap_expr_parse_cmd(cmd, envclause + 5, 0, &err, NULL);
525            if (err) {
526                return apr_pstrcat(cmd->pool,
527                                   "Can't parse envclause/expression: ", err,
528                                   NULL);
529            }
530        }
531        else {
532            return apr_pstrcat(cmd->pool, "Unknown parameter: ", envclause,
533                               NULL);
534        }
535    }
536
537    if ((colon = ap_strchr_c(hdr, ':'))) {
538        hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr);
539    }
540
541    new->header = hdr;
542    new->condition_var = condition_var;
543    new->expr = expr;
544
545    return parse_format_string(cmd->pool, new, value);
546}
547
548/* Handle all (xxx)Header directives */
549static const char *header_cmd(cmd_parms *cmd, void *indirconf,
550                              const char *args)
551{
552    const char *action;
553    const char *hdr;
554    const char *val;
555    const char *envclause;
556    const char *subs;
557
558    action = ap_getword_conf(cmd->temp_pool, &args);
559    if (cmd->info == &hdr_out_onsuccess) {
560        if (!strcasecmp(action, "always")) {
561            cmd->info = &hdr_out_always;
562            action = ap_getword_conf(cmd->temp_pool, &args);
563        }
564        else if (!strcasecmp(action, "onsuccess")) {
565            action = ap_getword_conf(cmd->temp_pool, &args);
566        }
567    }
568    hdr = ap_getword_conf(cmd->pool, &args);
569    val = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
570    subs = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
571    envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
572
573    if (*args) {
574        return apr_pstrcat(cmd->pool, cmd->cmd->name,
575                           " has too many arguments", NULL);
576    }
577
578    return header_inout_cmd(cmd, indirconf, action, hdr, val, subs, envclause);
579}
580
581/*
582 * Process the tags in the format string. Tags may be format specifiers
583 * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler
584 * (formatter) specific to the tag. Handlers return text strings.
585 * Concatenate the return from each handler into one string that is
586 * returned from this call.
587 */
588static char* process_tags(header_entry *hdr, request_rec *r)
589{
590    int i;
591    const char *s;
592    char *str = NULL;
593
594    format_tag *tag = (format_tag*) hdr->ta->elts;
595
596    for (i = 0; i < hdr->ta->nelts; i++) {
597        s = tag[i].func(r, tag[i].arg);
598        if (str == NULL)
599            str = apr_pstrdup(r->pool, s);
600        else
601            str = apr_pstrcat(r->pool, str, s, NULL);
602    }
603    return str ? str : "";
604}
605static const char *process_regexp(header_entry *hdr, const char *value,
606                                  request_rec *r)
607{
608    ap_regmatch_t pmatch[AP_MAX_REG_MATCH];
609    const char *subs;
610    const char *remainder;
611    char *ret;
612    int diffsz;
613    if (ap_regexec(hdr->regex, value, AP_MAX_REG_MATCH, pmatch, 0)) {
614        /* no match, nothing to do */
615        return value;
616    }
617    /* Process tags in the input string rather than the resulting
618       * substitution to avoid surprises
619       */
620    subs = ap_pregsub(r->pool, process_tags(hdr, r), value, AP_MAX_REG_MATCH, pmatch);
621    if (subs == NULL)
622        return NULL;
623    diffsz = strlen(subs) - (pmatch[0].rm_eo - pmatch[0].rm_so);
624    if (hdr->action == hdr_edit) {
625        remainder = value + pmatch[0].rm_eo;
626    }
627    else { /* recurse to edit multiple matches if applicable */
628        remainder = process_regexp(hdr, value + pmatch[0].rm_eo, r);
629        if (remainder == NULL)
630            return NULL;
631        diffsz += strlen(remainder) - strlen(value + pmatch[0].rm_eo);
632    }
633    ret = apr_palloc(r->pool, strlen(value) + 1 + diffsz);
634    memcpy(ret, value, pmatch[0].rm_so);
635    strcpy(ret + pmatch[0].rm_so, subs);
636    strcat(ret, remainder);
637    return ret;
638}
639
640static int echo_header(echo_do *v, const char *key, const char *val)
641{
642    /* If the input header (key) matches the regex, echo it intact to
643     * r->headers_out.
644     */
645    if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
646        apr_table_add(v->r->headers_out, key, val);
647    }
648
649    return 1;
650}
651
652static int edit_header(void *v, const char *key, const char *val)
653{
654    edit_do *ed = (edit_do *)v;
655    const char *repl = process_regexp(ed->hdr, val, ed->r);
656    if (repl == NULL)
657        return 0;
658
659    apr_table_addn(ed->t, key, repl);
660    return 1;
661}
662
663static int add_them_all(void *v, const char *key, const char *val)
664{
665    apr_table_t *headers = (apr_table_t *)v;
666
667    apr_table_addn(headers, key, val);
668    return 1;
669}
670
671static int do_headers_fixup(request_rec *r, apr_table_t *headers,
672                             apr_array_header_t *fixup, int early)
673{
674    echo_do v;
675    int i;
676    const char *val;
677
678    for (i = 0; i < fixup->nelts; ++i) {
679        header_entry *hdr = &((header_entry *) (fixup->elts))[i];
680        const char *envar = hdr->condition_var;
681
682        /* ignore early headers in late calls */
683        if (!early && (envar == condition_early)) {
684            continue;
685        }
686        /* ignore late headers in early calls */
687        else if (early && (envar != condition_early)) {
688            continue;
689        }
690        /* Do we have an expression to evaluate? */
691        else if (hdr->expr != NULL) {
692            const char *err = NULL;
693            int eval = ap_expr_exec(r, hdr->expr, &err);
694            if (err) {
695                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01501)
696                              "Failed to evaluate expression (%s) - ignoring",
697                              err);
698            }
699            else if (!eval) {
700                continue;
701            }
702        }
703        /* Have any conditional envar-controlled Header processing to do? */
704        else if (envar && !early) {
705            if (*envar != '!') {
706                if (apr_table_get(r->subprocess_env, envar) == NULL)
707                    continue;
708            }
709            else {
710                if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
711                    continue;
712            }
713        }
714
715        switch (hdr->action) {
716        case hdr_add:
717            apr_table_addn(headers, hdr->header, process_tags(hdr, r));
718            break;
719        case hdr_append:
720            apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
721            break;
722        case hdr_merge:
723            val = apr_table_get(headers, hdr->header);
724            if (val == NULL) {
725                apr_table_addn(headers, hdr->header, process_tags(hdr, r));
726            } else {
727                char *new_val = process_tags(hdr, r);
728                apr_size_t new_val_len = strlen(new_val);
729                int tok_found = 0;
730
731                /* modified version of logic in ap_get_token() */
732                while (*val) {
733                    const char *tok_start;
734
735                    while (apr_isspace(*val))
736                        ++val;
737
738                    tok_start = val;
739
740                    while (*val && *val != ',') {
741                        if (*val++ == '"')
742                            while (*val)
743                                if (*val++ == '"')
744                                    break;
745                    }
746
747                    if (new_val_len == (apr_size_t)(val - tok_start)
748                        && !strncmp(tok_start, new_val, new_val_len)) {
749                        tok_found = 1;
750                        break;
751                    }
752
753                    if (*val)
754                        ++val;
755                }
756
757                if (!tok_found) {
758                    apr_table_mergen(headers, hdr->header, new_val);
759                }
760            }
761            break;
762        case hdr_set:
763            if (!strcasecmp(hdr->header, "Content-Type")) {
764                 ap_set_content_type(r, process_tags(hdr, r));
765            }
766            apr_table_setn(headers, hdr->header, process_tags(hdr, r));
767            break;
768        case hdr_setifempty:
769            if (NULL == apr_table_get(headers, hdr->header)) {
770                if (!strcasecmp(hdr->header, "Content-Type")) {
771                    ap_set_content_type(r, process_tags(hdr, r));
772                }
773                apr_table_setn(headers, hdr->header, process_tags(hdr, r));
774            }
775            break;
776        case hdr_unset:
777            apr_table_unset(headers, hdr->header);
778            break;
779        case hdr_echo:
780            v.r = r;
781            v.hdr = hdr;
782            apr_table_do((int (*) (void *, const char *, const char *))
783                         echo_header, (void *) &v, r->headers_in, NULL);
784            break;
785        case hdr_edit:
786        case hdr_edit_r:
787            if (!strcasecmp(hdr->header, "Content-Type") && r->content_type) {
788                const char *repl = process_regexp(hdr, r->content_type, r);
789                if (repl == NULL)
790                    return 0;
791                ap_set_content_type(r, repl);
792            }
793            if (apr_table_get(headers, hdr->header)) {
794                edit_do ed;
795
796                ed.r = r;
797                ed.hdr = hdr;
798                ed.t = apr_table_make(r->pool, 5);
799                if (!apr_table_do(edit_header, (void *) &ed, headers,
800                                  hdr->header, NULL))
801                    return 0;
802                apr_table_unset(headers, hdr->header);
803                apr_table_do(add_them_all, (void *) headers, ed.t, NULL);
804            }
805            break;
806        case hdr_note:
807            apr_table_setn(r->notes, process_tags(hdr, r), apr_table_get(headers, hdr->header));
808            break;
809
810        }
811    }
812    return 1;
813}
814
815static void ap_headers_insert_output_filter(request_rec *r)
816{
817    headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
818                                                 &headers_module);
819
820    if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
821        ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection);
822    }
823}
824
825/*
826 * Make sure our error-path filter is in place.
827 */
828static void ap_headers_insert_error_filter(request_rec *r)
829{
830    headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
831                                                 &headers_module);
832
833    if (dirconf->fixup_err->nelts) {
834        ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection);
835    }
836}
837
838static apr_status_t ap_headers_output_filter(ap_filter_t *f,
839                                             apr_bucket_brigade *in)
840{
841    headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
842                                                 &headers_module);
843
844    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server, APLOGNO(01502)
845                 "headers: ap_headers_output_filter()");
846
847    /* do the fixup */
848    do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
849    do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out, 0);
850
851    /* remove ourselves from the filter chain */
852    ap_remove_output_filter(f);
853
854    /* send the data up the stack */
855    return ap_pass_brigade(f->next,in);
856}
857
858/*
859 * Make sure we propagate any "Header always" settings on the error
860 * path through http_protocol.c.
861 */
862static apr_status_t ap_headers_error_filter(ap_filter_t *f,
863                                            apr_bucket_brigade *in)
864{
865    headers_conf *dirconf;
866
867    dirconf = ap_get_module_config(f->r->per_dir_config,
868                                    &headers_module);
869    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server, APLOGNO(01503)
870                 "headers: ap_headers_error_filter()");
871
872    /*
873     * Add any header fields defined by "Header always" to r->err_headers_out.
874     * Server-wide first, then per-directory to allow overriding.
875     */
876    do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
877
878    /*
879     * We've done our bit; remove ourself from the filter chain so there's
880     * no possibility we'll be called again.
881     */
882    ap_remove_output_filter(f);
883
884    /*
885     * Pass the buck.  (euro?)
886     */
887    return ap_pass_brigade(f->next, in);
888}
889
890static apr_status_t ap_headers_fixup(request_rec *r)
891{
892    headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
893                                                 &headers_module);
894
895    /* do the fixup */
896    if (dirconf->fixup_in->nelts) {
897        do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0);
898    }
899
900    return DECLINED;
901}
902static apr_status_t ap_headers_early(request_rec *r)
903{
904    headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
905                                                 &headers_module);
906
907    /* do the fixup */
908    if (dirconf->fixup_in->nelts) {
909        if (!do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 1))
910            goto err;
911    }
912    if (dirconf->fixup_err->nelts) {
913        if (!do_headers_fixup(r, r->err_headers_out, dirconf->fixup_err, 1))
914            goto err;
915    }
916    if (dirconf->fixup_out->nelts) {
917        if (!do_headers_fixup(r, r->headers_out, dirconf->fixup_out, 1))
918            goto err;
919    }
920
921    return DECLINED;
922err:
923    ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01504)
924                  "Regular expression replacement failed (replacement too long?)");
925    return HTTP_INTERNAL_SERVER_ERROR;
926}
927
928static const command_rec headers_cmds[] =
929{
930    AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out_onsuccess, OR_FILEINFO,
931                     "an optional condition, an action, header and value "
932                     "followed by optional env clause"),
933    AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO,
934                     "an action, header and value followed by optional env "
935                     "clause"),
936    {NULL}
937};
938
939static void register_format_tag_handler(const char *tag,
940                                        format_tag_fn *tag_handler)
941{
942    apr_hash_set(format_tag_hash, tag, 1, tag_handler);
943}
944
945static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
946{
947    format_tag_hash = apr_hash_make(p);
948    register_format_tag_handler("D", header_request_duration);
949    register_format_tag_handler("t", header_request_time);
950    register_format_tag_handler("e", header_request_env_var);
951    register_format_tag_handler("s", header_request_ssl_var);
952    register_format_tag_handler("l", header_request_loadavg);
953    register_format_tag_handler("i", header_request_idle);
954    register_format_tag_handler("b", header_request_busy);
955
956    return OK;
957}
958
959static int header_post_config(apr_pool_t *pconf, apr_pool_t *plog,
960                              apr_pool_t *ptemp, server_rec *s)
961{
962    header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
963    return OK;
964}
965
966static void register_hooks(apr_pool_t *p)
967{
968    ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter,
969                              NULL, AP_FTYPE_CONTENT_SET);
970    ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter,
971                              NULL, AP_FTYPE_CONTENT_SET);
972    ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE);
973    ap_hook_post_config(header_post_config,NULL,NULL,APR_HOOK_MIDDLE);
974    ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
975    ap_hook_insert_error_filter(ap_headers_insert_error_filter,
976                                NULL, NULL, APR_HOOK_LAST);
977    ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
978    ap_hook_post_read_request(ap_headers_early, NULL, NULL, APR_HOOK_FIRST);
979}
980
981AP_DECLARE_MODULE(headers) =
982{
983    STANDARD20_MODULE_STUFF,
984    create_headers_dir_config,  /* dir config creater */
985    merge_headers_config,       /* dir merger --- default is to override */
986    NULL,                       /* server config */
987    NULL,                       /* merge server configs */
988    headers_cmds,               /* command apr_table_t */
989    register_hooks              /* register hooks */
990};
991