Index: modules/metadata/mod_setenvif.c
===================================================================
--- modules/metadata/mod_setenvif.c	(revision 410296)
+++ modules/metadata/mod_setenvif.c	(working copy)
@@ -102,7 +102,8 @@
     SPECIAL_REQUEST_URI,
     SPECIAL_REQUEST_METHOD,
     SPECIAL_REQUEST_PROTOCOL,
-    SPECIAL_SERVER_ADDR
+    SPECIAL_SERVER_ADDR,
+    SPECIAL_OUTPUT_HEADER
 };
 typedef struct {
     char *name;                 /* header name */
@@ -113,10 +114,12 @@
     apr_table_t *features;      /* env vars to set (or unset) */
     enum special special_type;  /* is it a "special" header ? */
     int icase;                  /* ignoring case? */
+    int iscontent_type;         /* is content type ? */
 } sei_entry;
 
 typedef struct {
     apr_array_header_t *conditionals;
+    int output_filter_on;        /* act as output_filter for headers_out ? */
 } sei_cfg_rec;
 
 module AP_MODULE_DECLARE_DATA setenvif_module;
@@ -135,6 +138,7 @@
     sei_cfg_rec *new = (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec));
 
     new->conditionals = apr_array_make(p, 20, sizeof(sei_entry));
+    new->output_filter_on = 0;
     return (void *) new;
 }
 
@@ -155,6 +159,8 @@
 
     a->conditionals = apr_array_append(p, base->conditionals,
                                        overrides->conditionals);
+    if(base->output_filter_on || overrides->output_filter_on)
+        a->output_filter_on = 1;
     return a;
 }
 
@@ -164,6 +170,7 @@
  */
 #define ICASE_MAGIC	((void *)(&setenvif_module))
 #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag"
+#define SEI_OUTPUT_FILTER_NAME  "setenvif-output-filter"
 
 static int is_header_regex(apr_pool_t *p, const char* name) 
 {
@@ -307,26 +314,7 @@
         new->name = fname;
         new->regex = regex;
         new->icase = icase;
-        if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
-            new->pattern = apr_strmatch_precompile(cmd->pool,
-                                                   simple_pattern, !icase);
-            if (new->pattern == NULL) {
-                return apr_pstrcat(cmd->pool, cmd->cmd->name,
-                                   " pattern could not be compiled.", NULL);
-            }
-            new->preg = NULL;
-        }
-        else {
-            new->preg = ap_pregcomp(cmd->pool, regex,
-                                    (REG_EXTENDED | (icase ? REG_ICASE : 0)));
-            if (new->preg == NULL) {
-                return apr_pstrcat(cmd->pool, cmd->cmd->name,
-                                   " regex could not be compiled.", NULL);
-            }
-            new->pattern = NULL;
-        }
-        new->features = apr_table_make(cmd->pool, 2);
-
+        new->iscontent_type = 0;
         if (!strcasecmp(fname, "remote_addr")) {
             new->special_type = SPECIAL_REMOTE_ADDR;
         }
@@ -345,7 +333,37 @@
         else if (!strcasecmp(fname, "server_addr")) {
             new->special_type = SPECIAL_SERVER_ADDR;
         }
-        else {
+        else if (!strcasecmp(fname, "output_header")) {
+            new->special_type = SPECIAL_OUTPUT_HEADER;
+            sconf->output_filter_on = 1;
+            /*
+             * The regex is the 3rd word here, because we keep the same syntax
+             * as for header string, but we added the output_header key word to
+             * keep backward compatibility.  (to avoid the matching of both
+             * request and response header on a regex).
+             * In this case the 2nd param of the directive is the header
+             * regex/name.
+             */
+            new->name = regex;
+            if (is_header_regex(cmd->pool, new->name)) {
+                new->pnamereg = ap_pregcomp(cmd->pool, new->name, (REG_EXTENDED |
+                REG_NOSUB | (icase ? REG_ICASE : 0)));
+                if (new->pnamereg == NULL)
+                    return apr_pstrcat(cmd->pool, cmd->cmd->name, "Header name regex could not be compiled.", NULL);
+            }
+            else {
+                new->pnamereg = NULL;
+                if(!strcasecmp(new->name, "content-type"))
+                    new->iscontent_type = 1;
+            }
+            regex = ap_getword_conf(cmd->pool, &args);
+            if (!*regex) {
+                return apr_pstrcat(cmd->pool, "Missing regular expression for ",
+                cmd->cmd->name, NULL);
+            }
+            new->regex = regex;
+        }
+         else {
             new->special_type = SPECIAL_NOT;
             /* Handle fname as a regular expression.
              * If fname a simple header string, identify as such
@@ -365,6 +383,30 @@
                 new->pnamereg = NULL;
             }
         }
+        /*
+         * This block must be after the identification of the parameters, as
+         * the regex may be in an other position than the second one depends
+         * of the special type.
+         */
+        if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
+            new->pattern = apr_strmatch_precompile(cmd->pool,
+                                                   simple_pattern, !icase);
+            if (new->pattern == NULL) {
+                return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                                   " pattern could not be compiled.", NULL);
+            }
+            new->preg = NULL;
+        }
+        else {
+            new->preg = ap_pregcomp(cmd->pool, regex,
+                                    (REG_EXTENDED | (icase ? REG_ICASE : 0)));
+            if (new->preg == NULL) {
+                return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                                   " regex could not be compiled.", NULL);
+            }
+            new->pattern = NULL;
+        }
+        new->features = apr_table_make(cmd->pool, 2);
     }
     else {
         new = &entries[i];
@@ -436,6 +478,95 @@
 };
 
 /*
+ * This routine get from a header (or subprocess_env if no match in header),
+ * the value that correspond to a header name or a header matching regexp.
+ * (according to a sei_entry).
+ */
+static void get_header_val(sei_entry *b, apr_table_t *headers, apr_table_t *subprocess_env, char const **val, request_rec *r) {
+    const apr_table_entry_t *elts;
+    /* Matching headers_in against a regex. Iterate through
+     * the headers until we find a match or run out of
+     * headers.
+     */
+    const apr_array_header_t *arr = apr_table_elts(headers);
+    int j;
+
+
+    if (b->pnamereg) {
+        elts = (const apr_table_entry_t *) arr->elts;
+        *val = NULL;
+        for (j = 0; j < arr->nelts; ++j) {
+            if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) { 
+                *val = elts[j].val;
+            }
+        }
+    }
+    else {
+        if(!b->iscontent_type) {
+            /* Not matching against a regex */
+            *val = apr_table_get(headers, b->name);
+            if (*val == NULL) {
+                *val = apr_table_get(subprocess_env, b->name);
+            }
+        }
+        else {
+            *val = r->content_type;
+        }
+    }
+}
+
+
+/*
+ * This routine set environment variable according to val.
+ * Question : why sei_entry's not const ? because ap_regexec is not defined as
+ * well... pcreposix does not define the use of the regex as not modifying it?
+ */
+static void setenvif_val_match(request_rec *r, const char *val, apr_size_t
+        val_len, sei_entry *b) {
+    regmatch_t regm[AP_MAX_REG_MATCH];
+    const apr_table_entry_t *elts;
+    int j;
+
+    /*
+     * A NULL value indicates that the header field or special entity
+     * wasn't present or is undefined.  Represent that as an empty string
+     * so that REs like "^$" will work and allow envariable setting
+     * based on missing or empty field.
+     */
+    if (val == NULL) {
+        val = "";
+        val_len = 0;
+    }
+
+    if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
+            (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
+                                        0))) {
+        const apr_array_header_t *arr = apr_table_elts(b->features);
+        elts = (const apr_table_entry_t *) arr->elts;
+
+        for (j = 0; j < arr->nelts; ++j) {
+            if (*(elts[j].val) == '!') {
+                apr_table_unset(r->subprocess_env, elts[j].key);
+            }
+            else {
+                if (!b->pattern) {
+                    char *replaced = ap_pregsub(r->pool, elts[j].val, val,
+                            AP_MAX_REG_MATCH, regm);
+                    if (replaced) {
+                        apr_table_setn(r->subprocess_env, elts[j].key,
+                                replaced);
+                    }
+                }
+                else {
+                    apr_table_setn(r->subprocess_env, elts[j].key,
+                            elts[j].val);
+                }
+            }
+        }
+    }
+}
+
+/*
  * This routine gets called at two different points in request processing:
  * once before the URI has been translated (during the post-read-request
  * phase) and once after (during the header-parse phase).  We use different
@@ -448,12 +579,10 @@
 {
     sei_cfg_rec *sconf;
     sei_entry *entries;
-    const apr_table_entry_t *elts;
     const char *val;
     apr_size_t val_len = 0;
-    int i, j;
+    int i;
     char *last_name;
-    regmatch_t regm[AP_MAX_REG_MATCH];
 
     if (!ap_get_module_config(r->request_config, &setenvif_module)) {
         ap_set_module_config(r->request_config, &setenvif_module,
@@ -467,10 +596,11 @@
     }
     entries = (sei_entry *) sconf->conditionals->elts;
     last_name = NULL;
-    val = NULL;
+
     for (i = 0; i < sconf->conditionals->nelts; ++i) {
         sei_entry *b = &entries[i];
 
+        val = NULL;
         /* Optimize the case where a bunch of directives in a row use the
          * same header.  Remember we don't need to strcmp the two header
          * names because we made sure the pointers were equal during
@@ -499,79 +629,73 @@
                 val = r->protocol;
                 break;
             case SPECIAL_NOT:
-                if (b->pnamereg) {
-                    /* Matching headers_in against a regex. Iterate through
-                     * the headers_in until we find a match or run out of
-                     * headers.
-                     */
-                    const apr_array_header_t
-                        *arr = apr_table_elts(r->headers_in);
-
-                    elts = (const apr_table_entry_t *) arr->elts;
-                    val = NULL;
-                    for (j = 0; j < arr->nelts; ++j) {
-                        if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) { 
-                            val = elts[j].val;
-                        }
-                    }
-                }
-                else {
-                    /* Not matching against a regex */
-                    val = apr_table_get(r->headers_in, b->name);
-                    if (val == NULL) {
-                        val = apr_table_get(r->subprocess_env, b->name);
-                    }
-                }
+                get_header_val(b, r->headers_in, r->subprocess_env, &val, r);
             }
             val_len = val ? strlen(val) : 0;
         }
 
+        setenvif_val_match(r, val, val_len, b);
+
+    }
+
+    if (sconf->output_filter_on == 1)
+        ap_add_output_filter(SEI_OUTPUT_FILTER_NAME, sconf, r, r->connection);
+    
+    return DECLINED;
+}
+
+/*
+ * This (output) filter process headers_out to find condition to set env.
+ */
+static apr_status_t setenvif_out_filter(ap_filter_t * f, apr_bucket_brigade * bb)
+{
+    sei_cfg_rec *sconf;
+    sei_entry *entries;
+    const char *val;
+    apr_size_t val_len = 0;
+    int i;
+    char *last_name;
+
+    sconf  = (sei_cfg_rec *) f->ctx;
+    entries = (sei_entry *) sconf->conditionals->elts;
+    last_name = NULL;
+    for (i = 0; i < sconf->conditionals->nelts; ++i) {
+        sei_entry *b = &entries[i];
+
+        val = NULL;
         /*
-         * A NULL value indicates that the header field or special entity
-         * wasn't present or is undefined.  Represent that as an empty string
-         * so that REs like "^$" will work and allow envariable setting
-         * based on missing or empty field.
+         * Optimize the case where a bunch of directives in a row use the
+         * same header.  Remember we don't need to strcmp the two header
+         * names because we made sure the pointers were equal during
+         * configuration.
          */
-        if (val == NULL) {
-            val = "";
-            val_len = 0;
-        }
-
-        if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
-            (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
-                                        0))) {
-            const apr_array_header_t *arr = apr_table_elts(b->features);
-            elts = (const apr_table_entry_t *) arr->elts;
-
-            for (j = 0; j < arr->nelts; ++j) {
-                if (*(elts[j].val) == '!') {
-                    apr_table_unset(r->subprocess_env, elts[j].key);
-                }
-                else {
-                    if (!b->pattern) {
-                        char *replaced = ap_pregsub(r->pool, elts[j].val, val,
-                                                    AP_MAX_REG_MATCH, regm);
-                        if (replaced) {
-                            apr_table_setn(r->subprocess_env, elts[j].key,
-                                           replaced);
-                        }
-                    }
-                    else {
-                        apr_table_setn(r->subprocess_env, elts[j].key,
-                                       elts[j].val);
-                    }
-                }
+        if (b->name != last_name) {
+            last_name = b->name;
+            if (b->special_type == SPECIAL_OUTPUT_HEADER) {
+                /*
+                 * As output filter, only headers_out are interresting for us
+                 * so we just process this type of conditions.
+                 */
+                get_header_val(b, f->r->headers_out, f->r->subprocess_env, &val, f->r);
+                val_len = val ? strlen(val) : 0;
+                setenvif_val_match(f->r, val, val_len, b);
             }
         }
     }
 
-    return DECLINED;
+    ap_remove_output_filter(f);
+    return ap_pass_brigade(f->next, bb);
 }
 
 static void register_hooks(apr_pool_t *p)
 {
     ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
     ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
+    /*
+     * ftype is set to AP_FTYPE_CONTENT_SET + 1 to make this filter hooked
+     * before deflate (it's specific for the moment).
+     */
+    ap_register_output_filter(SEI_OUTPUT_FILTER_NAME, setenvif_out_filter, NULL, AP_FTYPE_CONTENT_SET);
 }
 
 module AP_MODULE_DECLARE_DATA setenvif_module =
