Home > Patch for mod_setenvif >

A patch for module mod_setenvif

You may want to read the related blog entry to understand why this patch has been submit, and how it works.

Download mod_setenvif-2-2-x.patch

mod_setenvif-2-2-x.patch

Read mod_setenvif-2-2-x.patch

  1. Index: modules/metadata/mod_setenvif.c
  2. ===================================================================
  3. --- modules/metadata/mod_setenvif.c     (revision 410287)
  4. +++ modules/metadata/mod_setenvif.c     (working copy)
  5. @@ -102,7 +102,8 @@
  6.      SPECIAL_REQUEST_URI,
  7.      SPECIAL_REQUEST_METHOD,
  8.      SPECIAL_REQUEST_PROTOCOL,
  9. -    SPECIAL_SERVER_ADDR
  10. +    SPECIAL_SERVER_ADDR,
  11. +    SPECIAL_OUTPUT_HEADER
  12.  };
  13.  typedef struct {
  14.      char *name;                 /* header name */
  15. @@ -113,10 +114,12 @@
  16.      apr_table_t *features;      /* env vars to set (or unset) */
  17.      enum special special_type;  /* is it a "special" header ? */
  18.      int icase;                  /* ignoring case? */
  19. +    int iscontent_type;         /* is content type ? */
  20.  } sei_entry;
  21.  
  22.  typedef struct {
  23.      apr_array_header_t *conditionals;
  24. +    int output_filter_on;        /* act as output_filter for headers_out ? */
  25.  } sei_cfg_rec;
  26.  
  27.  module AP_MODULE_DECLARE_DATA setenvif_module;
  28. @@ -135,6 +138,7 @@
  29.      sei_cfg_rec *new = (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec));
  30.  
  31.      new->conditionals = apr_array_make(p, 20, sizeof(sei_entry));
  32. +    new->output_filter_on = 0;
  33.      return (void *) new;
  34.  }
  35.  
  36. @@ -155,6 +159,8 @@
  37.  
  38.      a->conditionals = apr_array_append(p, base->conditionals,
  39.                                         overrides->conditionals);
  40. +    if(base->output_filter_on || overrides->output_filter_on)
  41. +        a->output_filter_on = 1;
  42.      return a;
  43.  }
  44.  
  45. @@ -164,6 +170,7 @@
  46.   */
  47.  #define ICASE_MAGIC  ((void *)(&setenvif_module))
  48.  #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag"
  49. +#define SEI_OUTPUT_FILTER_NAME  "setenvif-output-filter"
  50.  
  51.  static int is_header_regex(apr_pool_t *p, const char* name)
  52.  {
  53. @@ -307,6 +314,7 @@
  54.          new->name = fname;
  55.          new->regex = regex;
  56.          new->icase = icase;
  57. +        new->iscontent_type = 0;
  58.          if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
  59.              new->pattern = apr_strmatch_precompile(cmd->pool,
  60.                                                     simple_pattern, !icase);
  61. @@ -345,7 +353,37 @@
  62.          else if (!strcasecmp(fname, "server_addr")) {
  63.              new->special_type = SPECIAL_SERVER_ADDR;
  64.          }
  65. -        else {
  66. +        else if (!strcasecmp(fname, "output_header")) {
  67. +            new->special_type = SPECIAL_OUTPUT_HEADER;
  68. +            sconf->output_filter_on = 1;
  69. +            /*
  70. +             * The regex is the 3rd word here, because we keep the same syntax
  71. +             * as for header string, but we added the output_header key word to
  72. +             * keep backward compatibility.  (to avoid the matching of both
  73. +             * request and response header on a regex).
  74. +             * In this case the 2nd param of the directive is the header
  75. +             * regex/name.
  76. +             */
  77. +            new->name = regex;
  78. +            if (is_header_regex(cmd->pool, new->name)) {
  79. +                new->pnamereg = ap_pregcomp(cmd->pool, new->name, (AP_REG_EXTENDED |
  80. +                AP_REG_NOSUB | (icase ? AP_REG_ICASE : 0)));
  81. +                if (new->pnamereg == NULL)
  82. +                    return apr_pstrcat(cmd->pool, cmd->cmd->name, "Header name regex could not be compiled.", NULL);
  83. +            }
  84. +            else {
  85. +                new->pnamereg = NULL;
  86. +                if(!strcasecmp(new->name, "content-type"))
  87. +                    new->iscontent_type = 1;
  88. +            }
  89. +            regex = ap_getword_conf(cmd->pool, &args);
  90. +            if (!*regex) {
  91. +                return apr_pstrcat(cmd->pool, "Missing regular expression for ",
  92. +                cmd->cmd->name, NULL);
  93. +            }
  94. +            new->regex = regex;
  95. +        }
  96. +         else {
  97.              new->special_type = SPECIAL_NOT;
  98.              /* Handle fname as a regular expression.
  99.               * If fname a simple header string, identify as such
  100. @@ -365,6 +403,30 @@
  101.                  new->pnamereg = NULL;
  102.              }
  103.          }
  104. +        /*
  105. +         * This block must be after the identification of the parameters, as
  106. +         * the regex may be in an other position than the second one depends
  107. +         * of the special type.
  108. +         */
  109. +        if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
  110. +            new->pattern = apr_strmatch_precompile(cmd->pool,
  111. +                                                   simple_pattern, !icase);
  112. +            if (new->pattern == NULL) {
  113. +                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  114. +                                   " pattern could not be compiled.", NULL);
  115. +            }
  116. +            new->preg = NULL;
  117. +        }
  118. +        else {
  119. +            new->preg = ap_pregcomp(cmd->pool, regex,
  120. +                                    (AP_REG_EXTENDED | (icase ? AP_REG_ICASE : 0)));
  121. +            if (new->preg == NULL) {
  122. +                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  123. +                                   " regex could not be compiled.", NULL);
  124. +            }
  125. +            new->pattern = NULL;
  126. +        }
  127. +        new->features = apr_table_make(cmd->pool, 2);
  128.      }
  129.      else {
  130.          new = &entries[i];
  131. @@ -436,6 +498,95 @@
  132.  };
  133.  
  134.  /*
  135. + * This routine get from a header (or subprocess_env if no match in header),
  136. + * the value that correspond to a header name or a header matching regexp.
  137. + * (according to a sei_entry).
  138. + */
  139. +static void get_header_val(sei_entry *b, apr_table_t *headers, apr_table_t *subprocess_env, char const **val, request_rec *r) {
  140. +    const apr_table_entry_t *elts;
  141. +    /* Matching headers_in against a regex. Iterate through
  142. +     * the headers until we find a match or run out of
  143. +     * headers.
  144. +     */
  145. +    const apr_array_header_t *arr = apr_table_elts(headers);
  146. +    int j;
  147. +
  148. +
  149. +    if (b->pnamereg) {
  150. +        elts = (const apr_table_entry_t *) arr->elts;
  151. +        *val = NULL;
  152. +        for (j = 0; j < arr->nelts; ++j) {
  153. +            if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
  154. +                *val = elts[j].val;
  155. +            }
  156. +        }
  157. +    }
  158. +    else {
  159. +        if(!b->iscontent_type) {
  160. +            /* Not matching against a regex */
  161. +            *val = apr_table_get(headers, b->name);
  162. +            if (*val == NULL) {
  163. +                *val = apr_table_get(subprocess_env, b->name);
  164. +            }
  165. +        }
  166. +        else {
  167. +            *val = r->content_type;
  168. +        }
  169. +    }
  170. +}
  171. +
  172. +
  173. +/*
  174. + * This routine set environment variable according to val.
  175. + * Question : why sei_entry's not const ? because ap_regexec is not defined as
  176. + * well... pcreposix does not define the use of the regex as not modifying it?
  177. + */
  178. +static void setenvif_val_match(request_rec *r, const char *val, apr_size_t
  179. +        val_len, sei_entry *b) {
  180. +    ap_regmatch_t regm[AP_MAX_REG_MATCH];
  181. +    const apr_table_entry_t *elts;
  182. +    int j;
  183. +
  184. +    /*
  185. +     * A NULL value indicates that the header field or special entity
  186. +     * wasn't present or is undefined.  Represent that as an empty string
  187. +     * so that REs like "^$" will work and allow envariable setting
  188. +     * based on missing or empty field.
  189. +     */
  190. +    if (val == NULL) {
  191. +        val = "";
  192. +        val_len = 0;
  193. +    }
  194. +
  195. +    if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
  196. +            (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
  197. +                                        0))) {
  198. +        const apr_array_header_t *arr = apr_table_elts(b->features);
  199. +        elts = (const apr_table_entry_t *) arr->elts;
  200. +
  201. +        for (j = 0; j < arr->nelts; ++j) {
  202. +            if (*(elts[j].val) == '!') {
  203. +                apr_table_unset(r->subprocess_env, elts[j].key);
  204. +            }
  205. +            else {
  206. +                if (!b->pattern) {
  207. +                    char *replaced = ap_pregsub(r->pool, elts[j].val, val,
  208. +                            AP_MAX_REG_MATCH, regm);
  209. +                    if (replaced) {
  210. +                        apr_table_setn(r->subprocess_env, elts[j].key,
  211. +                                replaced);
  212. +                    }
  213. +                }
  214. +                else {
  215. +                    apr_table_setn(r->subprocess_env, elts[j].key,
  216. +                            elts[j].val);
  217. +                }
  218. +            }
  219. +        }
  220. +    }
  221. +}
  222. +
  223. +/*
  224.   * This routine gets called at two different points in request processing:
  225.   * once before the URI has been translated (during the post-read-request
  226.   * phase) and once after (during the header-parse phase).  We use different
  227. @@ -565,13 +716,64 @@
  228.          }
  229.      }
  230.  
  231. +    if (sconf->output_filter_on == 1)
  232. +        ap_add_output_filter(SEI_OUTPUT_FILTER_NAME, sconf, r, r->connection);
  233. +
  234.      return DECLINED;
  235.  }
  236.  
  237. +/*
  238. + * This (output) filter process headers_out to find condition to set env.
  239. + */
  240. +static apr_status_t setenvif_out_filter(ap_filter_t * f, apr_bucket_brigade * bb)
  241. +{
  242. +    sei_cfg_rec *sconf;
  243. +    sei_entry *entries;
  244. +    const char *val;
  245. +    apr_size_t val_len = 0;
  246. +    int i;
  247. +    char *last_name;
  248. +
  249. +    sconf  = (sei_cfg_rec *) f->ctx;
  250. +    entries = (sei_entry *) sconf->conditionals->elts;
  251. +    last_name = NULL;
  252. +    for (i = 0; i < sconf->conditionals->nelts; ++i) {
  253. +        sei_entry *b = &entries[i];
  254. +
  255. +        val = NULL;
  256. +        /*
  257. +         * Optimize the case where a bunch of directives in a row use the
  258. +         * same header.  Remember we don't need to strcmp the two header
  259. +         * names because we made sure the pointers were equal during
  260. +         * configuration.
  261. +         */
  262. +        if (b->name != last_name) {
  263. +            last_name = b->name;
  264. +            if (b->special_type == SPECIAL_OUTPUT_HEADER) {
  265. +                /*
  266. +                 * As output filter, only headers_out are interresting for us
  267. +                 * so we just process this type of conditions.
  268. +                 */
  269. +                get_header_val(b, f->r->headers_out, f->r->subprocess_env, &val, f->r);
  270. +                val_len = val ? strlen(val) : 0;
  271. +                setenvif_val_match(f->r, val, val_len, b);
  272. +            }
  273. +        }
  274. +    }
  275. +
  276. +    ap_remove_output_filter(f);
  277. +    return ap_pass_brigade(f->next, bb);
  278. +}
  279. +
  280.  static void register_hooks(apr_pool_t *p)
  281.  {
  282.      ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
  283.      ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
  284. +    /*
  285. +     * ftype is set to AP_FTYPE_CONTENT_SET + 1 to make this filter hooked
  286. +     * before deflate (it's specific for the moment).
  287. +     */
  288. +    ap_register_output_filter(SEI_OUTPUT_FILTER_NAME, setenvif_out_filter, NULL, AP_FTYPE_CONTENT_SET);
  289.  }
  290.  
  291.  module AP_MODULE_DECLARE_DATA setenvif_module =
  292.  

Download mod_setenvif-2-0-x.patch

mod_setenvif-2-0-x.patch

Read mod_setenvif-2-0-x.patch

  1. Index: modules/metadata/mod_setenvif.c
  2. ===================================================================
  3. --- modules/metadata/mod_setenvif.c     (revision 410296)
  4. +++ modules/metadata/mod_setenvif.c     (working copy)
  5. @@ -102,7 +102,8 @@
  6.      SPECIAL_REQUEST_URI,
  7.      SPECIAL_REQUEST_METHOD,
  8.      SPECIAL_REQUEST_PROTOCOL,
  9. -    SPECIAL_SERVER_ADDR
  10. +    SPECIAL_SERVER_ADDR,
  11. +    SPECIAL_OUTPUT_HEADER
  12.  };
  13.  typedef struct {
  14.      char *name;                 /* header name */
  15. @@ -113,10 +114,12 @@
  16.      apr_table_t *features;      /* env vars to set (or unset) */
  17.      enum special special_type;  /* is it a "special" header ? */
  18.      int icase;                  /* ignoring case? */
  19. +    int iscontent_type;         /* is content type ? */
  20.  } sei_entry;
  21.  
  22.  typedef struct {
  23.      apr_array_header_t *conditionals;
  24. +    int output_filter_on;        /* act as output_filter for headers_out ? */
  25.  } sei_cfg_rec;
  26.  
  27.  module AP_MODULE_DECLARE_DATA setenvif_module;
  28. @@ -135,6 +138,7 @@
  29.      sei_cfg_rec *new = (sei_cfg_rec *) apr_palloc(p, sizeof(sei_cfg_rec));
  30.  
  31.      new->conditionals = apr_array_make(p, 20, sizeof(sei_entry));
  32. +    new->output_filter_on = 0;
  33.      return (void *) new;
  34.  }
  35.  
  36. @@ -155,6 +159,8 @@
  37.  
  38.      a->conditionals = apr_array_append(p, base->conditionals,
  39.                                         overrides->conditionals);
  40. +    if(base->output_filter_on || overrides->output_filter_on)
  41. +        a->output_filter_on = 1;
  42.      return a;
  43.  }
  44.  
  45. @@ -164,6 +170,7 @@
  46.   */
  47.  #define ICASE_MAGIC    ((void *)(&setenvif_module))
  48.  #define SEI_MAGIC_HEIRLOOM "setenvif-phase-flag"
  49. +#define SEI_OUTPUT_FILTER_NAME  "setenvif-output-filter"
  50.  
  51.  static int is_header_regex(apr_pool_t *p, const char* name)
  52.  {
  53. @@ -307,26 +314,7 @@
  54.          new->name = fname;
  55.          new->regex = regex;
  56.          new->icase = icase;
  57. -        if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
  58. -            new->pattern = apr_strmatch_precompile(cmd->pool,
  59. -                                                   simple_pattern, !icase);
  60. -            if (new->pattern == NULL) {
  61. -                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  62. -                                   " pattern could not be compiled.", NULL);
  63. -            }
  64. -            new->preg = NULL;
  65. -        }
  66. -        else {
  67. -            new->preg = ap_pregcomp(cmd->pool, regex,
  68. -                                    (REG_EXTENDED | (icase ? REG_ICASE : 0)));
  69. -            if (new->preg == NULL) {
  70. -                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  71. -                                   " regex could not be compiled.", NULL);
  72. -            }
  73. -            new->pattern = NULL;
  74. -        }
  75. -        new->features = apr_table_make(cmd->pool, 2);
  76. -
  77. +        new->iscontent_type = 0;
  78.          if (!strcasecmp(fname, "remote_addr")) {
  79.              new->special_type = SPECIAL_REMOTE_ADDR;
  80.          }
  81. @@ -345,7 +333,37 @@
  82.          else if (!strcasecmp(fname, "server_addr")) {
  83.              new->special_type = SPECIAL_SERVER_ADDR;
  84.          }
  85. -        else {
  86. +        else if (!strcasecmp(fname, "output_header")) {
  87. +            new->special_type = SPECIAL_OUTPUT_HEADER;
  88. +            sconf->output_filter_on = 1;
  89. +            /*
  90. +             * The regex is the 3rd word here, because we keep the same syntax
  91. +             * as for header string, but we added the output_header key word to
  92. +             * keep backward compatibility.  (to avoid the matching of both
  93. +             * request and response header on a regex).
  94. +             * In this case the 2nd param of the directive is the header
  95. +             * regex/name.
  96. +             */
  97. +            new->name = regex;
  98. +            if (is_header_regex(cmd->pool, new->name)) {
  99. +                new->pnamereg = ap_pregcomp(cmd->pool, new->name, (REG_EXTENDED |
  100. +                REG_NOSUB | (icase ? REG_ICASE : 0)));
  101. +                if (new->pnamereg == NULL)
  102. +                    return apr_pstrcat(cmd->pool, cmd->cmd->name, "Header name regex could not be compiled.", NULL);
  103. +            }
  104. +            else {
  105. +                new->pnamereg = NULL;
  106. +                if(!strcasecmp(new->name, "content-type"))
  107. +                    new->iscontent_type = 1;
  108. +            }
  109. +            regex = ap_getword_conf(cmd->pool, &args);
  110. +            if (!*regex) {
  111. +                return apr_pstrcat(cmd->pool, "Missing regular expression for ",
  112. +                cmd->cmd->name, NULL);
  113. +            }
  114. +            new->regex = regex;
  115. +        }
  116. +         else {
  117.              new->special_type = SPECIAL_NOT;
  118.              /* Handle fname as a regular expression.
  119.               * If fname a simple header string, identify as such
  120. @@ -365,6 +383,30 @@
  121.                  new->pnamereg = NULL;
  122.              }
  123.          }
  124. +        /*
  125. +         * This block must be after the identification of the parameters, as
  126. +         * the regex may be in an other position than the second one depends
  127. +         * of the special type.
  128. +         */
  129. +        if ((simple_pattern = non_regex_pattern(cmd->pool, regex))) {
  130. +            new->pattern = apr_strmatch_precompile(cmd->pool,
  131. +                                                   simple_pattern, !icase);
  132. +            if (new->pattern == NULL) {
  133. +                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  134. +                                   " pattern could not be compiled.", NULL);
  135. +            }
  136. +            new->preg = NULL;
  137. +        }
  138. +        else {
  139. +            new->preg = ap_pregcomp(cmd->pool, regex,
  140. +                                    (REG_EXTENDED | (icase ? REG_ICASE : 0)));
  141. +            if (new->preg == NULL) {
  142. +                return apr_pstrcat(cmd->pool, cmd->cmd->name,
  143. +                                   " regex could not be compiled.", NULL);
  144. +            }
  145. +            new->pattern = NULL;
  146. +        }
  147. +        new->features = apr_table_make(cmd->pool, 2);
  148.      }
  149.      else {
  150.          new = &entries[i];
  151. @@ -436,6 +478,95 @@
  152.  };
  153.  
  154.  /*
  155. + * This routine get from a header (or subprocess_env if no match in header),
  156. + * the value that correspond to a header name or a header matching regexp.
  157. + * (according to a sei_entry).
  158. + */
  159. +static void get_header_val(sei_entry *b, apr_table_t *headers, apr_table_t *subprocess_env, char const **val, request_rec *r) {
  160. +    const apr_table_entry_t *elts;
  161. +    /* Matching headers_in against a regex. Iterate through
  162. +     * the headers until we find a match or run out of
  163. +     * headers.
  164. +     */
  165. +    const apr_array_header_t *arr = apr_table_elts(headers);
  166. +    int j;
  167. +
  168. +
  169. +    if (b->pnamereg) {
  170. +        elts = (const apr_table_entry_t *) arr->elts;
  171. +        *val = NULL;
  172. +        for (j = 0; j < arr->nelts; ++j) {
  173. +            if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
  174. +                *val = elts[j].val;
  175. +            }
  176. +        }
  177. +    }
  178. +    else {
  179. +        if(!b->iscontent_type) {
  180. +            /* Not matching against a regex */
  181. +            *val = apr_table_get(headers, b->name);
  182. +            if (*val == NULL) {
  183. +                *val = apr_table_get(subprocess_env, b->name);
  184. +            }
  185. +        }
  186. +        else {
  187. +            *val = r->content_type;
  188. +        }
  189. +    }
  190. +}
  191. +
  192. +
  193. +/*
  194. + * This routine set environment variable according to val.
  195. + * Question : why sei_entry's not const ? because ap_regexec is not defined as
  196. + * well... pcreposix does not define the use of the regex as not modifying it?
  197. + */
  198. +static void setenvif_val_match(request_rec *r, const char *val, apr_size_t
  199. +        val_len, sei_entry *b) {
  200. +    regmatch_t regm[AP_MAX_REG_MATCH];
  201. +    const apr_table_entry_t *elts;
  202. +    int j;
  203. +
  204. +    /*
  205. +     * A NULL value indicates that the header field or special entity
  206. +     * wasn't present or is undefined.  Represent that as an empty string
  207. +     * so that REs like "^$" will work and allow envariable setting
  208. +     * based on missing or empty field.
  209. +     */
  210. +    if (val == NULL) {
  211. +        val = "";
  212. +        val_len = 0;
  213. +    }
  214. +
  215. +    if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
  216. +            (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
  217. +                                        0))) {
  218. +        const apr_array_header_t *arr = apr_table_elts(b->features);
  219. +        elts = (const apr_table_entry_t *) arr->elts;
  220. +
  221. +        for (j = 0; j < arr->nelts; ++j) {
  222. +            if (*(elts[j].val) == '!') {
  223. +                apr_table_unset(r->subprocess_env, elts[j].key);
  224. +            }
  225. +            else {
  226. +                if (!b->pattern) {
  227. +                    char *replaced = ap_pregsub(r->pool, elts[j].val, val,
  228. +                            AP_MAX_REG_MATCH, regm);
  229. +                    if (replaced) {
  230. +                        apr_table_setn(r->subprocess_env, elts[j].key,
  231. +                                replaced);
  232. +                    }
  233. +                }
  234. +                else {
  235. +                    apr_table_setn(r->subprocess_env, elts[j].key,
  236. +                            elts[j].val);
  237. +                }
  238. +            }
  239. +        }
  240. +    }
  241. +}
  242. +
  243. +/*
  244.   * This routine gets called at two different points in request processing:
  245.   * once before the URI has been translated (during the post-read-request
  246.   * phase) and once after (during the header-parse phase).  We use different
  247. @@ -448,12 +579,10 @@
  248.  {
  249.      sei_cfg_rec *sconf;
  250.      sei_entry *entries;
  251. -    const apr_table_entry_t *elts;
  252.      const char *val;
  253.      apr_size_t val_len = 0;
  254. -    int i, j;
  255. +    int i;
  256.      char *last_name;
  257. -    regmatch_t regm[AP_MAX_REG_MATCH];
  258.  
  259.      if (!ap_get_module_config(r->request_config, &setenvif_module)) {
  260.          ap_set_module_config(r->request_config, &setenvif_module,
  261. @@ -467,10 +596,11 @@
  262.      }
  263.      entries = (sei_entry *) sconf->conditionals->elts;
  264.      last_name = NULL;
  265. -    val = NULL;
  266. +
  267.      for (i = 0; i < sconf->conditionals->nelts; ++i) {
  268.          sei_entry *b = &entries[i];
  269.  
  270. +        val = NULL;
  271.          /* Optimize the case where a bunch of directives in a row use the
  272.           * same header.  Remember we don't need to strcmp the two header
  273.           * names because we made sure the pointers were equal during
  274. @@ -499,79 +629,73 @@
  275.                  val = r->protocol;
  276.                  break;
  277.              case SPECIAL_NOT:
  278. -                if (b->pnamereg) {
  279. -                    /* Matching headers_in against a regex. Iterate through
  280. -                     * the headers_in until we find a match or run out of
  281. -                     * headers.
  282. -                     */
  283. -                    const apr_array_header_t
  284. -                        *arr = apr_table_elts(r->headers_in);
  285. -
  286. -                    elts = (const apr_table_entry_t *) arr->elts;
  287. -                    val = NULL;
  288. -                    for (j = 0; j < arr->nelts; ++j) {
  289. -                        if (!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
  290. -                            val = elts[j].val;
  291. -                        }
  292. -                    }
  293. -                }
  294. -                else {
  295. -                    /* Not matching against a regex */
  296. -                    val = apr_table_get(r->headers_in, b->name);
  297. -                    if (val == NULL) {
  298. -                        val = apr_table_get(r->subprocess_env, b->name);
  299. -                    }
  300. -                }
  301. +                get_header_val(b, r->headers_in, r->subprocess_env, &val, r);
  302.              }
  303.              val_len = val ? strlen(val) : 0;
  304.          }
  305.  
  306. +        setenvif_val_match(r, val, val_len, b);
  307. +
  308. +    }
  309. +
  310. +    if (sconf->output_filter_on == 1)
  311. +        ap_add_output_filter(SEI_OUTPUT_FILTER_NAME, sconf, r, r->connection);
  312. +    
  313. +    return DECLINED;
  314. +}
  315. +
  316. +/*
  317. + * This (output) filter process headers_out to find condition to set env.
  318. + */
  319. +static apr_status_t setenvif_out_filter(ap_filter_t * f, apr_bucket_brigade * bb)
  320. +{
  321. +    sei_cfg_rec *sconf;
  322. +    sei_entry *entries;
  323. +    const char *val;
  324. +    apr_size_t val_len = 0;
  325. +    int i;
  326. +    char *last_name;
  327. +
  328. +    sconf  = (sei_cfg_rec *) f->ctx;
  329. +    entries = (sei_entry *) sconf->conditionals->elts;
  330. +    last_name = NULL;
  331. +    for (i = 0; i < sconf->conditionals->nelts; ++i) {
  332. +        sei_entry *b = &entries[i];
  333. +
  334. +        val = NULL;
  335.          /*
  336. -         * A NULL value indicates that the header field or special entity
  337. -         * wasn't present or is undefined.  Represent that as an empty string
  338. -         * so that REs like "^$" will work and allow envariable setting
  339. -         * based on missing or empty field.
  340. +         * Optimize the case where a bunch of directives in a row use the
  341. +         * same header.  Remember we don't need to strcmp the two header
  342. +         * names because we made sure the pointers were equal during
  343. +         * configuration.
  344.           */
  345. -        if (val == NULL) {
  346. -            val = "";
  347. -            val_len = 0;
  348. -        }
  349. -
  350. -        if ((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
  351. -            (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm,
  352. -                                        0))) {
  353. -            const apr_array_header_t *arr = apr_table_elts(b->features);
  354. -            elts = (const apr_table_entry_t *) arr->elts;
  355. -
  356. -            for (j = 0; j < arr->nelts; ++j) {
  357. -                if (*(elts[j].val) == '!') {
  358. -                    apr_table_unset(r->subprocess_env, elts[j].key);
  359. -                }
  360. -                else {
  361. -                    if (!b->pattern) {
  362. -                        char *replaced = ap_pregsub(r->pool, elts[j].val, val,
  363. -                                                    AP_MAX_REG_MATCH, regm);
  364. -                        if (replaced) {
  365. -                            apr_table_setn(r->subprocess_env, elts[j].key,
  366. -                                           replaced);
  367. -                        }
  368. -                    }
  369. -                    else {
  370. -                        apr_table_setn(r->subprocess_env, elts[j].key,
  371. -                                       elts[j].val);
  372. -                    }
  373. -                }
  374. +        if (b->name != last_name) {
  375. +            last_name = b->name;
  376. +            if (b->special_type == SPECIAL_OUTPUT_HEADER) {
  377. +                /*
  378. +                 * As output filter, only headers_out are interresting for us
  379. +                 * so we just process this type of conditions.
  380. +                 */
  381. +                get_header_val(b, f->r->headers_out, f->r->subprocess_env, &val, f->r);
  382. +                val_len = val ? strlen(val) : 0;
  383. +                setenvif_val_match(f->r, val, val_len, b);
  384.              }
  385.          }
  386.      }
  387.  
  388. -    return DECLINED;
  389. +    ap_remove_output_filter(f);
  390. +    return ap_pass_brigade(f->next, bb);
  391.  }
  392.  
  393.  static void register_hooks(apr_pool_t *p)
  394.  {
  395.      ap_hook_header_parser(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
  396.      ap_hook_post_read_request(match_headers, NULL, NULL, APR_HOOK_MIDDLE);
  397. +    /*
  398. +     * ftype is set to AP_FTYPE_CONTENT_SET + 1 to make this filter hooked
  399. +     * before deflate (it's specific for the moment).
  400. +     */
  401. +    ap_register_output_filter(SEI_OUTPUT_FILTER_NAME, setenvif_out_filter, NULL, AP_FTYPE_CONTENT_SET);
  402.  }
  403.  
  404.  module AP_MODULE_DECLARE_DATA setenvif_module =
  405.