mod_headers.c revision ef5650b61a8e35f3cc93ec07e73efc17ea329894
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh * applicable.
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd *
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * Licensed under the Apache License, Version 2.0 (the "License");
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd * you may not use this file except in compliance with the License.
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh * You may obtain a copy of the License at
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh *
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh * http://www.apache.org/licenses/LICENSE-2.0
5a58787efeb02a1c3f06569d019ad81fd2efa06end *
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletc * Unless required by applicable law or agreed to in writing, software
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * distributed under the License is distributed on an "AS IS" BASIS,
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen * See the License for the specific language governing permissions and
2e545ce2450a9953665f701bb05350f0d3f26275nd * limitations under the License.
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen */
d29d9ab4614ff992b0e8de6e2b88d52b6f1f153erbowen
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim/*
5a58787efeb02a1c3f06569d019ad81fd2efa06end * mod_headers.c: Add/append/remove HTTP response headers
af33a4994ae2ff15bc67d19ff1a7feb906745bf8rbowen * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
3f08db06526d6901aa08c110b5bc7dde6bc39905nd *
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * The Header directive can be used to add/replace/remove HTTP headers
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * within the response message. The RequestHeader directive can be used
5a58787efeb02a1c3f06569d019ad81fd2efa06end * to add/replace/remove HTTP headers before a request message is processed.
3f08db06526d6901aa08c110b5bc7dde6bc39905nd * Valid in both per-server and per-dir configurations.
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd *
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * Syntax is:
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim *
00b49f91367894cf867206991ff1373cfeabb759gryzor * Header action header value
7f5b59ccc63c0c0e3e678a168f09ee6a2f51f9d0nd * RequestHeader action header value
e609c337f729875bc20e01096c7e610f45356f54nilgun *
f086b4b402fa9a2fefc7dda85de2a3cc1cd0a654rjung * Where action is one of:
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd * set - set this header, replacing any old value
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd * add - add this header, possible resulting in two or more
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd * headers with the same name
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * append - append this text onto any existing header of this same
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * unset - remove this header
c68aa7f213d409d464eaa6b963afb28678548f4frbowen *
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf * Where action is unset, the third argument (value) should not be given.
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * The header name can include the colon, or not.
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen *
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * The Header and RequestHeader directives can only be used where allowed
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * by the FileInfo override.
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen *
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * When the request is processed, the header directives are processed in
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * this order: firstly, the main server, then the virtual server handling
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * this request (if any), then any <Directory> sections (working downwards
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * from the root dir), then an <Location> sections (working down from
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * shortest URL component), the any <File> sections. This order is
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * important if any 'set' or 'unset' actions are used. For example,
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * the following two directives have different effect if applied in
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * the reverse order:
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd *
5a58787efeb02a1c3f06569d019ad81fd2efa06end * Header append Author "John P. Doe"
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * Header unset Author
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen *
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * Examples:
a56ff98d3082c853f69e8de5c3e8bcab2734c0earbowen *
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh * To set the "Author" header, use
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim * Header add Author "John P. Doe"
5a58787efeb02a1c3f06569d019ad81fd2efa06end *
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * To remove a header:
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd * Header unset Author
c68aa7f213d409d464eaa6b963afb28678548f4frbowen *
c68aa7f213d409d464eaa6b963afb28678548f4frbowen */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf#include "apr.h"
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen#include "apr_lib.h"
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd#include "apr_strings.h"
4aa603e6448b99f9371397d439795c91a93637eand#include "apr_buckets.h"
c3c006c28c5b03892ccaef6e4d2cbb15a13a2072rbowen
c3c006c28c5b03892ccaef6e4d2cbb15a13a2072rbowen#include "apr_hash.h"
c3c006c28c5b03892ccaef6e4d2cbb15a13a2072rbowen#define APR_WANT_STRFUNC
4aa603e6448b99f9371397d439795c91a93637eand#include "apr_want.h"
c3c006c28c5b03892ccaef6e4d2cbb15a13a2072rbowen
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd#include "httpd.h"
c68aa7f213d409d464eaa6b963afb28678548f4frbowen#include "http_config.h"
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd#include "http_request.h"
20f499565e77defe9dab24dd85c02f38a1175855nd#include "http_log.h"
c3c006c28c5b03892ccaef6e4d2cbb15a13a2072rbowen#include "util_filter.h"
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd#include "http_protocol.h"
4126704c4950bfd46d32ad54e3b106ac6d868a73sf
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd#include "mod_ssl.h" /* for the ssl_var_lookup optional function defn */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
4126704c4950bfd46d32ad54e3b106ac6d868a73sf/* format_tag_hash is initialized during pre-config */
4126704c4950bfd46d32ad54e3b106ac6d868a73sfstatic apr_hash_t *format_tag_hash;
4126704c4950bfd46d32ad54e3b106ac6d868a73sf
4126704c4950bfd46d32ad54e3b106ac6d868a73sftypedef enum {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen hdr_add = 'a', /* add header (could mean multiple hdrs) */
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd hdr_set = 's', /* set (replace old value) */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen hdr_append = 'm', /* append (merge into any old value) */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen hdr_unset = 'u', /* unset header */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen hdr_echo = 'e' /* echo headers from request to response */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen} hdr_actions;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * magic cmd->info values
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd */
00b49f91367894cf867206991ff1373cfeabb759gryzorstatic char hdr_in = '0'; /* RequestHeader */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic char hdr_out = '1'; /* Header onsuccess */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic char hdr_err = '2'; /* Header always */
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * There is an array of struct format_tag per Header/RequestHeader
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * config directive
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowentypedef struct {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen const char* (*func)(request_rec *r,char *arg);
157312a2bcbad225c12462fc6d74b1aa3f32dceehumbedooh char *arg;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen} format_tag;
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh/* 'Magic' condition_var value to run action in post_read_request */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic const char* condition_early = "early";
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * There is one "header_entry" per Header/RequestHeader config directive
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh */
545f1a3ee91056d6de32adab10c2eab26db89f27dpejeshtypedef struct {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen hdr_actions action;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen const char *header;
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh apr_array_header_t *ta; /* Array of format_tag structs */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen ap_regex_t *regex;
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh const char *condition_var;
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh} header_entry;
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/* echo_do is used for Header echo to iterate through the request headers*/
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowentypedef struct {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen request_rec *r;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen header_entry *hdr;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen} echo_do;
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * headers_conf is our per-module configuration. This is used as both
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * a per-dir and per-server config
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowentypedef struct {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen apr_array_header_t *fixup_in;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen apr_array_header_t *fixup_out;
00b49f91367894cf867206991ff1373cfeabb759gryzor apr_array_header_t *fixup_err;
00b49f91367894cf867206991ff1373cfeabb759gryzor} headers_conf;
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh
7add1372edb1ee95a2c4d1314df4c7567bda7c62jimmodule AP_MODULE_DECLARE_DATA headers_module;
5a58787efeb02a1c3f06569d019ad81fd2efa06end
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/* Pointer to ssl_var_lookup, if available. */
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4ndstatic APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * Tag formatting functions
157312a2bcbad225c12462fc6d74b1aa3f32dceehumbedooh */
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic const char *constant_item(request_rec *r, char *stuff)
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd{
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh return stuff;
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh}
545f1a3ee91056d6de32adab10c2eab26db89f27dpejeshstatic const char *header_request_duration(request_rec *r, char *a)
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh{
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT,
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh (apr_time_now() - r->request_time));
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd}
4aa603e6448b99f9371397d439795c91a93637eandstatic const char *header_request_time(request_rec *r, char *a)
bbcc277fef0330ac4c1f937cb0dea78248225c0ahumbedooh{
bbcc277fef0330ac4c1f937cb0dea78248225c0ahumbedooh return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time);
4aa603e6448b99f9371397d439795c91a93637eand}
9e213c30f8f58680f0350736a2a6221baad94131humbedooh
aaf7b7f4cc1be050310c3d7f48bce0ec67e174e4nd/* unwrap_header returns HDR with any newlines converted into
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh * whitespace if necessary. */
db99fa79ac42b9cc42b63386eb289aecb0f3cb9cndstatic const char *unwrap_header(apr_pool_t *p, const char *hdr)
545f1a3ee91056d6de32adab10c2eab26db89f27dpejesh{
bf082801b4063fe22a99661889cbd9a7701dae9fnd if (ap_strchr_c(hdr, APR_ASCII_LF) || ap_strchr_c(hdr, APR_ASCII_CR)) {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen char *ptr;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf hdr = ptr = apr_pstrdup(p, hdr);
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen do {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR)
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen *ptr = APR_ASCII_BLANK;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen } while (*ptr++);
a56ff98d3082c853f69e8de5c3e8bcab2734c0earbowen }
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen return hdr;
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen}
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic const char *header_request_env_var(request_rec *r, char *a)
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen{
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen const char *s = apr_table_get(r->subprocess_env,a);
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen if (s)
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen return unwrap_header(r->pool, s);
e9425c93ba098a7844e138a61e1be5f46d2aa2ddnd else
4aa603e6448b99f9371397d439795c91a93637eand return "(null)";
9e213c30f8f58680f0350736a2a6221baad94131humbedooh}
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowenstatic const char *header_request_ssl_var(request_rec *r, char *name)
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen{
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen if (header_ssl_lookup) {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen const char *val = header_ssl_lookup(r->pool, r->server,
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen r->connection, r, name);
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen if (val && val[0])
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen return unwrap_header(r->pool, val);
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen else
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen return "(null)";
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen }
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen else {
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen return "(null)";
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen }
9a58dc6a2b26ec128b1270cf48810e705f1a90dbsf}
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen/*
c35acdcbd4d173d3c536cf0be1295fa6c510cf8drbowen * Config routines
3b3b7fc78d1f5bfc2769903375050048ff41ff26nd */
7add1372edb1ee95a2c4d1314df4c7567bda7c62jim
7add1372edb1ee95a2c4d1314df4c7567bda7c62jimstatic void *create_headers_dir_config(apr_pool_t *p, char *d)
00b49f91367894cf867206991ff1373cfeabb759gryzor{
7f5b59ccc63c0c0e3e678a168f09ee6a2f51f9d0nd headers_conf *conf = apr_pcalloc(p, sizeof(*conf));
e609c337f729875bc20e01096c7e610f45356f54nilgun
f086b4b402fa9a2fefc7dda85de2a3cc1cd0a654rjung conf->fixup_in = apr_array_make(p, 2, sizeof(header_entry));
727872d18412fc021f03969b8641810d8896820bhumbedooh conf->fixup_out = apr_array_make(p, 2, sizeof(header_entry));
0d0ba3a410038e179b695446bb149cce6264e0abnd conf->fixup_err = apr_array_make(p, 2, sizeof(header_entry));
727872d18412fc021f03969b8641810d8896820bhumbedooh
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh return conf;
0d0ba3a410038e179b695446bb149cce6264e0abnd}
cc7e1025de9ac63bd4db6fe7f71c158b2cf09fe4humbedooh
727872d18412fc021f03969b8641810d8896820bhumbedoohstatic void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
0d0ba3a410038e179b695446bb149cce6264e0abnd{
0d0ba3a410038e179b695446bb149cce6264e0abnd headers_conf *newconf = apr_pcalloc(p, sizeof(*newconf));
0d0ba3a410038e179b695446bb149cce6264e0abnd headers_conf *base = basev;
ac082aefa89416cbdc9a1836eaf3bed9698201c8humbedooh headers_conf *overrides = overridesv;
0d0ba3a410038e179b695446bb149cce6264e0abnd
0d0ba3a410038e179b695446bb149cce6264e0abnd newconf->fixup_in = apr_array_append(p, base->fixup_in,
0d0ba3a410038e179b695446bb149cce6264e0abnd overrides->fixup_in);
727872d18412fc021f03969b8641810d8896820bhumbedooh newconf->fixup_out = apr_array_append(p, base->fixup_out,
0d0ba3a410038e179b695446bb149cce6264e0abnd overrides->fixup_out);
0d0ba3a410038e179b695446bb149cce6264e0abnd newconf->fixup_err = apr_array_append(p, base->fixup_err,
30471a4650391f57975f60bbb6e4a90be7b284bfhumbedooh overrides->fixup_err);
205f749042ed530040a4f0080dbcb47ceae8a374rjung
af33a4994ae2ff15bc67d19ff1a7feb906745bf8rbowen return newconf;
0d0ba3a410038e179b695446bb149cce6264e0abnd}
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd
7fec19672a491661b2fe4b29f685bc7f4efa64d4ndstatic char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
7fec19672a491661b2fe4b29f685bc7f4efa64d4nd{
5a58787efeb02a1c3f06569d019ad81fd2efa06end const char *s;
char *d;
tag->func = constant_item;
s = *sa;
while (*s && *s != '%') {
s++;
}
/*
* This might allocate a few chars extra if there's a backslash
* escape in the format string.
*/
tag->arg = apr_palloc(p, s - *sa + 1);
d = tag->arg;
s = *sa;
while (*s && *s != '%') {
if (*s != '\\') {
*d++ = *s++;
}
else {
s++;
switch (*s) {
case '\\':
*d++ = '\\';
s++;
break;
case 'r':
*d++ = '\r';
s++;
break;
case 'n':
*d++ = '\n';
s++;
break;
case 't':
*d++ = '\t';
s++;
break;
default:
/* copy verbatim */
*d++ = '\\';
/*
* Allow the loop to deal with this *s in the normal
* fashion so that it handles end of string etc.
* properly.
*/
break;
}
}
}
*d = '\0';
*sa = s;
return NULL;
}
static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
{
const char *s = *sa;
const char * (*tag_handler)(request_rec *,char *);
/* Handle string literal/conditionals */
if (*s != '%') {
return parse_misc_string(p, tag, sa);
}
s++; /* skip the % */
/* Pass through %% as % */
if (*s == '%') {
tag->func = constant_item;
tag->arg = "%";
*sa = ++s;
return NULL;
}
tag->arg = '\0';
/* grab the argument if there is one */
if (*s == '{') {
++s;
tag->arg = ap_getword(p,&s,'}');
}
tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1);
if (!tag_handler) {
char dummy[2];
dummy[0] = s[-1];
dummy[1] = '\0';
return apr_pstrcat(p, "Unrecognized header format %", dummy, NULL);
}
tag->func = tag_handler;
*sa = s;
return NULL;
}
/*
* A format string consists of white space, text and optional format
* tags in any order. E.g.,
*
* Header add MyHeader "Free form text %D %t more text"
*
* Decompose the format string into its tags. Each tag (struct format_tag)
* contains a pointer to the function used to format the tag. Then save each
* tag in the tag array anchored in the header_entry.
*/
static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s)
{
char *res;
/* No string to parse with unset and echo commands */
if (hdr->action == hdr_unset ||
hdr->action == hdr_echo) {
return NULL;
}
hdr->ta = apr_array_make(p, 10, sizeof(format_tag));
while (*s) {
if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
return res;
}
}
return NULL;
}
/* handle RequestHeader and Header directive */
static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd,
void *indirconf,
const char *action,
const char *hdr,
const char *value,
const char* envclause)
{
headers_conf *dirconf = indirconf;
const char *condition_var = NULL;
const char *colon;
header_entry *new;
apr_array_header_t *fixup = (cmd->info == &hdr_in)
? dirconf->fixup_in : (cmd->info == &hdr_err)
? dirconf->fixup_err
: dirconf->fixup_out;
new = (header_entry *) apr_array_push(fixup);
if (!strcasecmp(action, "set"))
new->action = hdr_set;
else if (!strcasecmp(action, "add"))
new->action = hdr_add;
else if (!strcasecmp(action, "append"))
new->action = hdr_append;
else if (!strcasecmp(action, "unset"))
new->action = hdr_unset;
else if (!strcasecmp(action, "echo"))
new->action = hdr_echo;
else
return "first argument must be 'add', 'set', 'append', 'unset' or "
"'echo'.";
if (new->action == hdr_unset) {
if (value) {
if (envclause) {
return "header unset takes two arguments";
}
envclause = value;
value = NULL;
}
}
else if (new->action == hdr_echo) {
ap_regex_t *regex;
if (value) {
if (envclause) {
return "Header echo takes two arguments";
}
envclause = value;
value = NULL;
}
if (cmd->info != &hdr_out && cmd->info != &hdr_err)
return "Header echo only valid on Header "
"directives";
else {
regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB);
if (regex == NULL) {
return "Header echo regex could not be compiled";
}
}
new->regex = regex;
}
else if (!value)
return "Header requires three arguments";
/* Handle the envclause on Header */
if (envclause != NULL) {
if (strcasecmp(envclause, "early") == 0) {
condition_var = condition_early;
}
else {
if (strncasecmp(envclause, "env=", 4) != 0) {
return "error: envclause should be in the form env=envar";
}
if ((envclause[4] == '\0')
|| ((envclause[4] == '!') && (envclause[5] == '\0'))) {
return "error: missing environment variable name. "
"envclause should be in the form env=envar ";
}
condition_var = envclause + 4;
}
}
if ((colon = ap_strchr_c(hdr, ':'))) {
hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr);
}
new->header = hdr;
new->condition_var = condition_var;
return parse_format_string(cmd->pool, new, value);
}
/* Handle all (xxx)Header directives */
static const char *header_cmd(cmd_parms *cmd, void *indirconf,
const char *args)
{
const char *action;
const char *hdr;
const char *val;
const char *envclause;
action = ap_getword_conf(cmd->pool, &args);
if (cmd->info == &hdr_out) {
if (!strcasecmp(action, "always")) {
cmd->info = &hdr_err;
action = ap_getword_conf(cmd->pool, &args);
}
else if (!strcasecmp(action, "onsuccess")) {
action = ap_getword_conf(cmd->pool, &args);
}
}
hdr = ap_getword_conf(cmd->pool, &args);
val = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
if (*args) {
return apr_pstrcat(cmd->pool, cmd->cmd->name,
" has too many arguments", NULL);
}
return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause);
}
/*
* Process the tags in the format string. Tags may be format specifiers
* (%D, %t, etc.), whitespace or text strings. For each tag, run the handler
* (formatter) specific to the tag. Handlers return text strings.
* Concatenate the return from each handler into one string that is
* returned from this call.
*/
static char* process_tags(header_entry *hdr, request_rec *r)
{
int i;
const char *s;
char *str = NULL;
format_tag *tag = (format_tag*) hdr->ta->elts;
for (i = 0; i < hdr->ta->nelts; i++) {
s = tag[i].func(r, tag[i].arg);
if (str == NULL)
str = apr_pstrdup(r->pool, s);
else
str = apr_pstrcat(r->pool, str, s, NULL);
}
return str ? str : "";
}
static int echo_header(echo_do *v, const char *key, const char *val)
{
/* If the input header (key) matches the regex, echo it intact to
* r->headers_out.
*/
if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
apr_table_add(v->r->headers_out, key, val);
}
return 1;
}
static void do_headers_fixup(request_rec *r, apr_table_t *headers,
apr_array_header_t *fixup, int early)
{
int i;
for (i = 0; i < fixup->nelts; ++i) {
header_entry *hdr = &((header_entry *) (fixup->elts))[i];
const char *envar = hdr->condition_var;
/* ignore early headers in late calls */
if (!early && (envar == condition_early)) {
continue;
}
/* ignore late headers in early calls */
else if (early && (envar != condition_early)) {
continue;
}
/* Have any conditional envar-controlled Header processing to do? */
else if (envar && !early) {
if (*envar != '!') {
if (apr_table_get(r->subprocess_env, envar) == NULL)
continue;
}
else {
if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
continue;
}
}
switch (hdr->action) {
case hdr_add:
apr_table_addn(headers, hdr->header, process_tags(hdr, r));
break;
case hdr_append:
apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
break;
case hdr_set:
apr_table_setn(headers, hdr->header, process_tags(hdr, r));
break;
case hdr_unset:
apr_table_unset(headers, hdr->header);
break;
case hdr_echo:
{
echo_do v;
v.r = r;
v.hdr = hdr;
apr_table_do((int (*) (void *, const char *, const char *))
echo_header, (void *) &v, r->headers_in, NULL);
break;
}
}
}
}
static void ap_headers_insert_output_filter(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection);
}
}
/*
* Make sure our error-path filter is in place.
*/
static void ap_headers_insert_error_filter(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
if (dirconf->fixup_err->nelts) {
ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection);
}
}
static apr_status_t ap_headers_output_filter(ap_filter_t *f,
apr_bucket_brigade *in)
{
headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
&headers_module);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
"headers: ap_headers_output_filter()");
/* do the fixup */
do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out, 0);
/* remove ourselves from the filter chain */
ap_remove_output_filter(f);
/* send the data up the stack */
return ap_pass_brigade(f->next,in);
}
/*
* Make sure we propagate any "Header always" settings on the error
* path through http_protocol.c.
*/
static apr_status_t ap_headers_error_filter(ap_filter_t *f,
apr_bucket_brigade *in)
{
headers_conf *dirconf;
dirconf = ap_get_module_config(f->r->per_dir_config,
&headers_module);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
"headers: ap_headers_error_filter()");
/*
* Add any header fields defined by "Header always" to r->err_headers_out.
* Server-wide first, then per-directory to allow overriding.
*/
do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
/*
* We've done our bit; remove ourself from the filter chain so there's
* no possibility we'll be called again.
*/
ap_remove_output_filter(f);
/*
* Pass the buck. (euro?)
*/
return ap_pass_brigade(f->next, in);
}
static apr_status_t ap_headers_fixup(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
/* do the fixup */
if (dirconf->fixup_in->nelts) {
do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0);
}
return DECLINED;
}
static apr_status_t ap_headers_early(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
/* do the fixup */
if (dirconf->fixup_in->nelts) {
do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 1);
}
if (dirconf->fixup_err->nelts) {
do_headers_fixup(r, r->err_headers_out, dirconf->fixup_err, 1);
}
if (dirconf->fixup_out->nelts) {
do_headers_fixup(r, r->headers_out, dirconf->fixup_out, 1);
}
return DECLINED;
}
static const command_rec headers_cmds[] =
{
AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out, OR_FILEINFO,
"an optional condition, an action, header and value "
"followed by optional env clause"),
AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO,
"an action, header and value followed by optional env "
"clause"),
{NULL}
};
static void register_format_tag_handler(const char *tag,
const void *tag_handler)
{
apr_hash_set(format_tag_hash, tag, 1, tag_handler);
}
static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
format_tag_hash = apr_hash_make(p);
register_format_tag_handler("D", (const void *)header_request_duration);
register_format_tag_handler("t", (const void *)header_request_time);
register_format_tag_handler("e", (const void *)header_request_env_var);
register_format_tag_handler("s", (const void *)header_request_ssl_var);
return OK;
}
static int header_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
return OK;
}
static void register_hooks(apr_pool_t *p)
{
ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_post_config(header_post_config,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
ap_hook_insert_error_filter(ap_headers_insert_error_filter,
NULL, NULL, APR_HOOK_LAST);
ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
ap_hook_post_read_request(ap_headers_early, NULL, NULL, APR_HOOK_FIRST);
}
module AP_MODULE_DECLARE_DATA headers_module =
{
STANDARD20_MODULE_STUFF,
create_headers_dir_config, /* dir config creater */
merge_headers_config, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
headers_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};