]> granicus.if.org Git - apache/blobdiff - modules/metadata/mod_headers.c
Move the POSIX reg* implementations into the ap_* namespace;
[apache] / modules / metadata / mod_headers.c
index fbcb5041695a1f1a09ea9567a1bdd2190ef2372c..82f29938bf37ebf4919e896ed3c5444cc9f358bb 100644 (file)
@@ -1,4 +1,5 @@
-/* Copyright 1999-2004 The Apache Software Foundation
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -91,11 +92,12 @@ typedef enum {
     hdr_echo = 'e'              /* echo headers from request to response */
 } hdr_actions;
 
-typedef enum {
-    hdr_in = 0,                 /* RequestHeader */
-    hdr_out = 1,                /* Header */
-    hdr_err = 2                 /* ErrorHeader */
-} hdr_inout;
+/*
+ * magic cmd->info values
+ */
+static char hdr_in  = '0';  /* RequestHeader */
+static char hdr_out = '1';  /* Header onsuccess */
+static char hdr_err = '2';  /* Header always */
 
 /*
  * There is an array of struct format_tag per Header/RequestHeader 
@@ -106,14 +108,16 @@ typedef struct {
     char *arg;
 } format_tag;
 
+/* 'Magic' condition_var value to run action in post_read_request */
+static const char* condition_early = "early";
 /*
  * There is one "header_entry" per Header/RequestHeader config directive
  */
 typedef struct {
     hdr_actions action;
-    char *header;
+    const char *header;
     apr_array_header_t *ta;   /* Array of format_tag structs */
-    regex_t *regex;
+    ap_regex_t *regex;
     const char *condition_var;
 } header_entry;
 
@@ -200,7 +204,8 @@ static const char *header_request_ssl_var(request_rec *r, char *name)
 /*
  * Config routines
  */
-static void *create_headers_config(apr_pool_t *p, server_rec *s)
+
+static void *create_headers_dir_config(apr_pool_t *p, char *d)
 {
     headers_conf *conf = apr_pcalloc(p, sizeof(*conf));
 
@@ -211,19 +216,16 @@ static void *create_headers_config(apr_pool_t *p, server_rec *s)
     return conf;
 }
 
-static void *create_headers_dir_config(apr_pool_t *p, char *d)
-{
-    return create_headers_config(p, NULL);
-}
-
 static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
 {
     headers_conf *newconf = apr_pcalloc(p, sizeof(*newconf));
     headers_conf *base = basev;
     headers_conf *overrides = overridesv;
 
-    newconf->fixup_in = apr_array_append(p, base->fixup_in, overrides->fixup_in);
-    newconf->fixup_out = apr_array_append(p, base->fixup_out, overrides->fixup_out);
+    newconf->fixup_in = apr_array_append(p, base->fixup_in,
+                                         overrides->fixup_in);
+    newconf->fixup_out = apr_array_append(p, base->fixup_out,
+                                          overrides->fixup_out);
     newconf->fixup_err = apr_array_append(p, base->fixup_err,
                                           overrides->fixup_err);
 
@@ -268,7 +270,7 @@ static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
                 *d++ = '\n';
                 s++;
                 break;
-            case 't':  
+            case 't':   
                 *d++ = '\t';
                 s++;
                 break;
@@ -300,6 +302,15 @@ static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
         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 == '{') {
@@ -313,8 +324,7 @@ static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
         char dummy[2];
         dummy[0] = s[-1];
         dummy[1] = '\0';
-        return apr_pstrcat(p, "Unrecognized Header or RequestHeader directive %",
-                           dummy, NULL);
+        return apr_pstrcat(p, "Unrecognized header format %", dummy, NULL);
     }
     tag->func = tag_handler;
 
@@ -336,7 +346,7 @@ static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s
 {
     char *res;
 
-    /* No string to parse with unset and copy commands */
+    /* No string to parse with unset and echo commands */
     if (hdr->action == hdr_unset ||
         hdr->action == hdr_echo) {
         return NULL;
@@ -353,38 +363,22 @@ static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s
 }
 
 /* handle RequestHeader and Header directive */
-static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd,
-                                    void *indirconf,
-                                    const char *action, const char *inhdr,
-                                    const char *value, const char* envclause)
+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;
-    char *colon;
-    char *hdr = apr_pstrdup(cmd->pool, inhdr);
+    const char *colon;
     header_entry *new;
-    server_rec *s = cmd->server;
-    headers_conf *serverconf = ap_get_module_config(s->module_config,
-                                                    &headers_module);
-    apr_array_header_t *fixup = dirconf->fixup_out;
-
-    switch (inout) {
-    case hdr_in:
-        fixup = (cmd->path != NULL)
-            ? dirconf->fixup_in
-            : serverconf->fixup_in;
-        break;
-    case hdr_out:
-        fixup = (cmd->path != NULL)
-            ? dirconf->fixup_out
-            : serverconf->fixup_out;
-        break;
-    case hdr_err:
-        fixup = (cmd->path != NULL)
-            ? dirconf->fixup_err
-            : serverconf->fixup_err;
-        break;
-    }
+
+    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);
 
@@ -399,20 +393,33 @@ static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd,
     else if (!strcasecmp(action, "echo"))
         new->action = hdr_echo;
     else
-        return "first argument must be add, set, append, unset or echo.";
+        return "first argument must be 'add', 'set', 'append', 'unset' or "
+               "'echo'.";
 
     if (new->action == hdr_unset) {
-        if (value)
-            return "header unset takes two arguments";
+        if (value) {
+            if (envclause) {
+                return "header unset takes two arguments";
+            }
+            envclause = value;
+            value = NULL;
+        }
     }
     else if (new->action == hdr_echo) {
-        regex_t *regex;
-        if (value)
-            return "Header echo takes two arguments";
-        else if (inout != hdr_out)
-            return "Header echo only valid on Header directive";
+        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, REG_EXTENDED | REG_NOSUB);
+            regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB);
             if (regex == NULL) {
                 return "Header echo regex could not be compiled";
             }
@@ -420,27 +427,29 @@ static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd,
         new->regex = regex;
     }
     else if (!value)
-        return "header requires three arguments";
+        return "Header requires three arguments";
 
     /* Handle the envclause on Header */
     if (envclause != NULL) {
-        if (inout == hdr_in) {
-            return "error: envclause (env=...) only valid on "
-                "Header and ErrorHeader directives";
-        }
-        if (strncasecmp(envclause, "env=", 4) != 0) {
-            return "error: envclause should be in the form env=envar";
+        if (strcasecmp(envclause, "early") == 0) {
+            condition_var = condition_early;
         }
-        if ((envclause[4] == '\0')
-            || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
-            return "error: missing environment variable name. "
-                "envclause should be in the form env=envar ";
+        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;
         }
-        condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
     }
     
-    if ((colon = strchr(hdr, ':')))
-        *colon = '\0';
+    if ((colon = ap_strchr_c(hdr, ':'))) {
+        hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr);
+    }
 
     new->header = hdr;
     new->condition_var = condition_var;
@@ -448,33 +457,35 @@ static const char *header_inout_cmd(hdr_inout inout, cmd_parms *cmd,
     return parse_format_string(cmd->pool, new, value);
 }
 
-/* Handle Header directive */
+/* Handle all (xxx)Header directives */
 static const char *header_cmd(cmd_parms *cmd, void *indirconf,
                               const char *args)
 {
-    const char *s;
     const char *action;
     const char *hdr;
     const char *val;
     const char *envclause;
-    hdr_inout outbl;
 
-    s = apr_pstrdup(cmd->pool, args);
-    action = ap_getword_conf(cmd->pool, &s);
-    hdr = ap_getword_conf(cmd->pool, &s);
-    val = *s ? ap_getword_conf(cmd->pool, &s) : NULL;
-    envclause = *s ? ap_getword_conf(cmd->pool, &s) : NULL;
-    outbl = (cmd->info == NULL) ? hdr_out : hdr_err;
+    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;
 
-    return header_inout_cmd(outbl, cmd, indirconf, action, hdr, val, envclause);
-}
+    if (*args) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           " has too many arguments", NULL);
+    }
 
-/* handle RequestHeader directive */
-static const char *request_header_cmd(cmd_parms *cmd, void *indirconf,
-                              const char *action, const char *inhdr,
-                              const char *value)
-{
-    return header_inout_cmd(hdr_in, cmd, indirconf, action, inhdr, value, NULL);
+    return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause);
 }
 
 /*
@@ -499,7 +510,7 @@ static char* process_tags(header_entry *hdr, request_rec *r)
         else
             str = apr_pstrcat(r->pool, str, s, NULL);
     }
-    return str;
+    return str ? str : "";
 }
 
 static int echo_header(echo_do *v, const char *key, const char *val)
@@ -514,30 +525,25 @@ static int echo_header(echo_do *v, const char *key, const char *val)
     return 1;
 }
 
-static void do_headers_fixup(request_rec *r, hdr_inout inout,
-                             apr_array_header_t *fixup)
+static void do_headers_fixup(request_rec *r, apr_table_t *headers,
+                             apr_array_header_t *fixup, int early)
 {
     int i;
-    apr_table_t *headers = r->headers_out;
-
-    switch (inout) {
-    case hdr_in:
-        headers = r->headers_in;
-        break;
-    case hdr_out:
-        headers = r->headers_out;
-        break;
-    case hdr_err:
-        headers = r->err_headers_out;
-        break;
-    }
 
     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? */
-        if (hdr->condition_var) {
-            const char *envar = hdr->condition_var;
+        else if (envar && !early) {
             if (*envar != '!') {
                 if (apr_table_get(r->subprocess_env, envar) == NULL)
                     continue;
@@ -576,13 +582,10 @@ static void do_headers_fixup(request_rec *r, hdr_inout inout,
 
 static void ap_headers_insert_output_filter(request_rec *r)
 {
-    headers_conf *serverconf = ap_get_module_config(r->server->module_config,
-                                                    &headers_module);
     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
                                                  &headers_module);
 
-    if (serverconf->fixup_out->nelts || dirconf->fixup_out->nelts
-        || serverconf->fixup_err->nelts || dirconf->fixup_err->nelts) {
+    if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
         ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection);
     }
 }
@@ -592,12 +595,10 @@ static void ap_headers_insert_output_filter(request_rec *r)
  */
 static void ap_headers_insert_error_filter(request_rec *r)
 {
-    headers_conf *serverconf = ap_get_module_config(r->server->module_config,
-                                                    &headers_module);
     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
                                                  &headers_module);
 
-    if (serverconf->fixup_err->nelts || dirconf->fixup_err->nelts) {
+    if (dirconf->fixup_err->nelts) {
         ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection);
     }
 }
@@ -605,8 +606,6 @@ static void ap_headers_insert_error_filter(request_rec *r)
 static apr_status_t ap_headers_output_filter(ap_filter_t *f,
                                              apr_bucket_brigade *in)
 {
-    headers_conf *serverconf = ap_get_module_config(f->r->server->module_config,
-                                                    &headers_module);
     headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
                                                  &headers_module);
 
@@ -614,10 +613,8 @@ static apr_status_t ap_headers_output_filter(ap_filter_t *f,
                  "headers: ap_headers_output_filter()");
 
     /* do the fixup */
-    do_headers_fixup(f->r, hdr_err, serverconf->fixup_err);
-    do_headers_fixup(f->r, hdr_out, serverconf->fixup_out);
-    do_headers_fixup(f->r, hdr_err, dirconf->fixup_err);
-    do_headers_fixup(f->r, hdr_out, dirconf->fixup_out);
+    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);
@@ -627,28 +624,24 @@ static apr_status_t ap_headers_output_filter(ap_filter_t *f,
 }
 
 /*
- * Make sure we propagate any ErrorHeader settings on the error
+ * 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 *serverconf;
     headers_conf *dirconf;
 
-    serverconf = ap_get_module_config(f->r->server->module_config,
-                                      &headers_module);
     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 ErrorHeader to r->err_headers_out.
+     * 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, hdr_err, serverconf->fixup_err);
-    do_headers_fixup(f->r, hdr_err, dirconf->fixup_err);
+    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
@@ -664,45 +657,60 @@ static apr_status_t ap_headers_error_filter(ap_filter_t *f,
 
 static apr_status_t ap_headers_fixup(request_rec *r)
 {
-    headers_conf *serverconf = ap_get_module_config(r->server->module_config,
-                                                    &headers_module);
     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
                                                  &headers_module);
 
     /* do the fixup */
-    if (serverconf->fixup_in->nelts || dirconf->fixup_in->nelts) {
-        do_headers_fixup(r, hdr_in, serverconf->fixup_in);
-        do_headers_fixup(r, hdr_in, dirconf->fixup_in);
+    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, NULL, OR_FILEINFO,
-                   "an action, header and value followed by optional env clause"),
-    AP_INIT_RAW_ARGS("ErrorHeader", header_cmd, "", OR_FILEINFO,
-                     "an action, header and value "
+    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_TAKE23("RequestHeader", request_header_cmd, NULL, OR_FILEINFO,
-                   "an action, header and value"),
+    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(apr_pool_t *p, char *tag, void *tag_handler, int def)
+static void register_format_tag_handler(const char *tag,
+                                        const void *tag_handler)
 {
-    const void *h = apr_palloc(p, sizeof(h));
-    h = tag_handler;
-    apr_hash_set(format_tag_hash, tag, 1, h);
+    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(p, "D", (void*) header_request_duration, 0);
-    register_format_tag_handler(p, "t", (void*) header_request_time, 0);
-    register_format_tag_handler(p, "e", (void*) header_request_env_var, 0);
-    register_format_tag_handler(p, "s", (void*) header_request_ssl_var, 0);
+    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;
 }
 
@@ -725,6 +733,7 @@ static void register_hooks(apr_pool_t *p)
     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 =
@@ -732,8 +741,8 @@ 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 */
-    create_headers_config,      /* server config */
-    merge_headers_config,       /* merge server configs */
+    NULL,                       /* server config */
+    NULL,                       /* merge server configs */
     headers_cmds,               /* command apr_table_t */
-    register_hooks             /* register hooks */
+    register_hooks              /* register hooks */
 };