"Fossies" - the Fresh Open Source Software Archive

Member "mod_setenvifplus-0.40/apache2/mod_setenvifplus.c" (16 Jul 2020, 87970 Bytes) of package /linux/www/apache_httpd_modules/mod_setenvifplus-0.40-src.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mod_setenvifplus.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.39_vs_0.40.

    1 /* -*-mode: c; indent-tabs-mode: nil; c-basic-offset: 2; -*-
    2  */
    3 
    4 /**
    5  * mod_setenvifplus.c: Apache httpd module to set environment variables
    6  * See http://modsetenvifplus.sourceforge.net/ for further
    7  * details about mod_setenvifplus.
    8  *
    9  * Copyright (C) 2020 Pascal Buchbinder
   10  *
   11  * Licensed to the Apache Software Foundation (ASF) under one or more
   12  * contributor license agreements.  See the NOTICE file distributed with
   13  * this work for additional information regarding copyright ownership.
   14  * The ASF licenses this file to You under the Apache License, Version 2.0
   15  * (the "License"); you may not use this file except in compliance with
   16  * the License.  You may obtain a copy of the License at
   17  *
   18  *     http://www.apache.org/licenses/LICENSE-2.0
   19  *
   20  * Unless required by applicable law or agreed to in writing, software
   21  * distributed under the License is distributed on an "AS IS" BASIS,
   22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   23  * See the License for the specific language governing permissions and
   24  * limitations under the License.
   25  */
   26 
   27 /************************************************************************
   28  * Version
   29  ***********************************************************************/
   30 static const char revision[] = "$Id: mod_setenvifplus.c 177 2020-07-01 05:12:41Z pbuchbinder $";
   31 static const char g_revision[] = "0.40";
   32 
   33 /************************************************************************
   34  * Includes
   35  ***********************************************************************/
   36 
   37 #include <arpa/inet.h>
   38 
   39 /* openssl */
   40 #include <openssl/crypto.h>
   41 #include <openssl/dh.h>
   42 #include <openssl/bn.h>
   43 #include <openssl/rand.h>
   44 #include <openssl/evp.h>
   45 #include <openssl/err.h>
   46 
   47 /* apr */
   48 #include "apr.h"
   49 #include "apr_strings.h"
   50 #include "apr_strmatch.h"
   51 #include "apr_lib.h"
   52 #include "apr_base64.h"
   53 
   54 #define APR_WANT_STRFUNC
   55 #include "apr_want.h"
   56 
   57 /* apache */
   58 //#include "ap_config.h"
   59 #include "httpd.h"
   60 #include "http_config.h"
   61 #include "http_core.h"
   62 #include "http_log.h"
   63 #include "http_protocol.h"
   64 #include "http_request.h"
   65 #include <util_filter.h>
   66 #include "util_md5.h"
   67 
   68 /************************************************************************
   69  * defines
   70  ***********************************************************************/
   71 #define SP_LOG_PFX(id)  "mod_setenvifplus("#id"): "
   72 #define SP_COOKIE_KEY "SP_COOKIE_KEY"
   73 #define SP_KEY "SP_KEY"
   74 #define SP_RAND_SIZE 18
   75 
   76 enum sp_special {
   77   SP_SPECIAL_NOT,
   78   SP_SPECIAL_REMOTE_ADDR,
   79   SP_SPECIAL_REMOTE_NET,
   80   SP_SPECIAL_REMOTE_HOST,
   81   SP_SPECIAL_REQUEST_URI,
   82   SP_SPECIAL_REQUEST_QUERY,
   83   SP_SPECIAL_REQUEST_USER,
   84   SP_SPECIAL_REQUEST_METHOD,
   85   SP_SPECIAL_REQUEST_PROTOCOL,
   86   SP_SPECIAL_SERVER_ADDR,
   87   SP_SPECIAL_SERVER_PORT,
   88   SP_SPECIAL_RESPONSE_STATUS
   89 };
   90 
   91 enum sp_action {
   92   SP_ACTION_SET,
   93   SP_ACTION_UNSET,
   94   SP_ACTION_CHANGE,
   95   SP_ACTION_ADD
   96 };
   97 
   98 enum sp_cmp {
   99   SP_CMP_EQ,
  100   SP_CMP_NE,
  101   SP_CMP_GT,
  102   SP_CMP_LT
  103 };
  104 
  105 // Apache 2.4 compat
  106 #if (AP_SERVER_MINORVERSION_NUMBER == 4)
  107 #define SF_CONN_REMOTEIP(r) r->connection->client_ip
  108 #define SF_CONN_REMOTE_ADDR(r) r->connection->client_addr
  109 #else
  110 #define SF_CONN_REMOTEIP(r) r->connection->remote_ip
  111 #define SF_CONN_REMOTE_ADDR(r) r->connection->remote_addr
  112 #endif
  113 
  114 /************************************************************************
  115  * structures
  116  ***********************************************************************/
  117 typedef struct {
  118   const char *name;             /* attribute name */
  119   ap_regex_t *pnamereg;         /* compiled attribute name (optional regex) */
  120   const char *regex;            /* regex to match against */
  121   ap_regex_t *preg;             /* compiled regex */
  122   const apr_strmatch_pattern *pattern; /* non-regex pattern to match */
  123   const char *variable;         /* env var name to set (or unset) */
  124   const char *value;            /* env var value to set */ 
  125   int valueSet;                 /* indicates, that a value has been configured by the user*/
  126   enum sp_special special_type; /* is it a "special" attribute ? */
  127   int icase;                    /* ignoring case? */
  128 } sp_std_entry_t;
  129 
  130 typedef struct {
  131   const char *name;
  132   ap_regex_t *preg;             /* compiled regex (for SP_ACTION_CHANGE only) */
  133   const char *value;
  134   const char *condition;
  135   enum sp_action action;
  136 } sp_hdr_entry_t;
  137 
  138 typedef struct {
  139   enum sp_cmp cmp;
  140   const char *left;
  141   const char *right;
  142   const char *variable;
  143   const char *value;
  144 } sp_cmp_entry_t;
  145 
  146 typedef struct {
  147   const char *header;
  148   ap_regex_t *preg;
  149   const char *value;
  150 } sp_usr_entry_t;
  151 
  152 typedef struct {
  153   int code;
  154   const char *var;
  155 } sp_status_entry_t;
  156 
  157 typedef struct {
  158   const char *header;
  159   const char *var;
  160 } sp_hash_entry_t;
  161 
  162 typedef struct {
  163   const char *filtername;
  164   const char *var;
  165   int notset;
  166 } sp_filter_entry_t;
  167 
  168 typedef struct {
  169   char *path;
  170   apr_array_header_t *std_conditionals;
  171   apr_array_header_t *std_late_conditionals;
  172   apr_array_header_t *std_req_query;
  173   apr_array_header_t *std_req_changequery;
  174   apr_array_header_t *std_res_conditionals;
  175   apr_array_header_t *std_req_header;
  176   apr_array_header_t *std_late_req_header;
  177   apr_array_header_t *std_res_header;
  178   apr_array_header_t *user;
  179   apr_array_header_t *std_removepattern_req_header;
  180   apr_array_header_t *status_var;
  181   apr_array_header_t *outfilter;
  182   apr_array_header_t *cookie_names;
  183   apr_array_header_t *header_hash;
  184   apr_array_header_t* cmp;
  185   apr_array_header_t* cmp_late;
  186 } sp_config_t;
  187 
  188 /* functions to replace patterns */
  189 typedef struct {
  190   char *identifier;                     /** name of the function inc. opening
  191                                             bracket, e.g. "e64(" */
  192   int len;
  193   char close;                           /** end marker, e.g. ')' */
  194   char *(*func)(request_rec *, char *); /** pointer to the function */
  195 } sp_function_t;
  196 
  197 typedef struct sp_url_escape_seq {
  198   char c;
  199   char *esc;
  200   apr_size_t len;
  201 } sp_url_escape_seq_t;
  202 
  203 /************************************************************************
  204  * globals
  205  ***********************************************************************/
  206 module AP_MODULE_DECLARE_DATA setenvifplus_module;
  207 #define SP_ICASE_MAGIC  ((void *)(&setenvifplus_module))
  208 
  209 #ifdef SP_VAR_PFX_24
  210 #define SP_VAR_PFX "%{"
  211 #else
  212 #define SP_VAR_PFX "${"
  213 #endif
  214 static char *m_sp_var_pfx = SP_VAR_PFX;
  215 
  216 /************************************************************************
  217  * private
  218  ***********************************************************************/
  219 
  220 /**
  221  * Returns the client's network (IPv4/24 or IPv6/64) number.
  222  */
  223 static char *sp_get_remote_net(request_rec *r) {
  224   apr_sockaddr_t *remote_addr = SF_CONN_REMOTE_ADDR(r);
  225   if(remote_addr->family == AF_INET6) {
  226     char *dst = apr_pcalloc(r->pool, INET6_ADDRSTRLEN);
  227     char *ret;
  228     apr_uint32_t *addr = (apr_uint32_t *)remote_addr->ipaddr_ptr;
  229     apr_uint32_t dest[4];
  230     if(IN6_IS_ADDR_V4MAPPED((struct in6_addr *)remote_addr->ipaddr_ptr)) {
  231       dest[0] = addr[0];
  232       dest[1] = addr[1];
  233       dest[2] = addr[2];
  234       dest[3] = addr[3] & 0x00ffffff;
  235     } else {
  236       dest[0] = addr[0];
  237       dest[1] = addr[1];
  238       dest[2] = 0;
  239       dest[3] = 0;
  240     }
  241     ret = (char *)inet_ntop(AF_INET6, dest, dst, INET6_ADDRSTRLEN);
  242     return ret;
  243   } else {
  244     char *dst = apr_pcalloc(r->pool, INET_ADDRSTRLEN);
  245     char *ret;
  246     apr_uint32_t *addr = (apr_uint32_t *)remote_addr->ipaddr_ptr;
  247     apr_uint32_t dest[1];
  248     dest[0] = addr[0] & 0x00ffffff;
  249     ret = (char *)inet_ntop(AF_INET, dest, dst, INET_ADDRSTRLEN);
  250     return ret;
  251   }
  252 }
  253 
  254 /**
  255  * Decryptes encrypted and base64 encoded string. See sp_enc64().
  256  *
  257  * @param r
  258  * @param str String to decrypt
  259  * @return Decrypted string or NULL on error
  260  */
  261 static char *sp_dec64(request_rec *r, const char *str, unsigned char *key) {
  262 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  263   EVP_CIPHER_CTX cipher_ctx;
  264   EVP_CIPHER_CTX *cipher_ctx_p = &cipher_ctx;
  265 #else
  266   EVP_CIPHER_CTX *cipher_ctx_p;
  267 #endif
  268   unsigned char *hashIn;
  269   unsigned char hash[APR_MD5_DIGESTSIZE];
  270   apr_md5_ctx_t md5;
  271   int len = 0;
  272   int buf_len = 0;
  273   unsigned char *buf;
  274   char *dec = (char *)apr_palloc(r->pool, 1 + apr_base64_decode_len(str));
  275   int dec_len = apr_base64_decode(dec, str);
  276   buf = apr_pcalloc(r->pool, dec_len);
  277 
  278 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  279   EVP_CIPHER_CTX_init(cipher_ctx_p);
  280 #else
  281   cipher_ctx_p = EVP_CIPHER_CTX_new();
  282 #endif
  283   EVP_DecryptInit(cipher_ctx_p, EVP_des_ede3_cbc(), key, NULL);
  284   if(!EVP_DecryptUpdate(cipher_ctx_p, (unsigned char *)&buf[buf_len], &len,
  285                         (const unsigned char *)dec, dec_len)) {
  286     goto failed;
  287   }
  288   buf_len+=len;
  289   if(!EVP_DecryptFinal(cipher_ctx_p, (unsigned char *)&buf[buf_len], &len)) {
  290     goto failed;
  291   }
  292   buf_len+=len;
  293   
  294   if(buf_len < (SP_RAND_SIZE + APR_MD5_DIGESTSIZE)) {
  295     goto failed;
  296   }
  297   if(buf[SP_RAND_SIZE-1] != 'A' || buf[SP_RAND_SIZE-2] != 'z') {
  298     goto failed;
  299   }
  300 
  301   hashIn = &buf[SP_RAND_SIZE];
  302   buf = &buf[SP_RAND_SIZE + APR_MD5_DIGESTSIZE];
  303   buf_len = buf_len - SP_RAND_SIZE - APR_MD5_DIGESTSIZE;
  304 
  305   apr_md5_init(&md5);
  306   apr_md5_update(&md5, buf, buf_len);
  307   apr_md5_final(hash, &md5);
  308 
  309   if(memcmp(hash, hashIn, APR_MD5_DIGESTSIZE) != 0) {
  310     goto failed;
  311   }
  312 
  313 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  314   EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
  315 #else
  316   EVP_CIPHER_CTX_free(cipher_ctx_p);
  317 #endif
  318 
  319   return apr_pstrndup(r->pool, (char *)buf, buf_len);
  320      
  321  failed:
  322 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  323   EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
  324 #else
  325   EVP_CIPHER_CTX_free(cipher_ctx_p);
  326 #endif
  327   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
  328                 SP_LOG_PFX(010)"Failed to decrypt data. (%s)", str ? str : "");
  329   return NULL;
  330 }
  331 
  332 /**
  333  * Encrypts and base64 encodes a string. See sp_dec64().
  334  *
  335  * @param r
  336  * @param str String to encrypt
  337  * @return Encrypted string.
  338  */
  339 static char *sp_enc64(request_rec *r, const char *str, unsigned char *key) {
  340   char *e;
  341 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  342   EVP_CIPHER_CTX cipher_ctx;
  343   EVP_CIPHER_CTX *cipher_ctx_p = &cipher_ctx;
  344 #else
  345   EVP_CIPHER_CTX *cipher_ctx_p;
  346 #endif
  347   unsigned char hash[APR_MD5_DIGESTSIZE];
  348   apr_md5_ctx_t md5;
  349   int len = 0;
  350   int buf_len = 0;
  351   int str_len = strlen(str);
  352   int max_buf_len = SP_RAND_SIZE + 
  353     str_len + 
  354     EVP_CIPHER_block_size(EVP_des_ede3_cbc()) +
  355     APR_MD5_DIGESTSIZE;
  356   unsigned char *buf = apr_pcalloc(r->pool, max_buf_len);
  357   unsigned char *rand = apr_pcalloc(r->pool, SP_RAND_SIZE);
  358 #if APR_HAS_RANDOM
  359   if(apr_generate_random_bytes(rand, SP_RAND_SIZE) != APR_SUCCESS) {
  360     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  361                   SP_LOG_PFX(030)"Can't generate random data.");
  362   }
  363 #else
  364   if(!RAND_bytes(rand, SP_RAND_SIZE)) {
  365     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  366                   SP_LOG_PFX(030)"Can't generate random data.");
  367   }
  368 #endif
  369   apr_md5_init(&md5);
  370   apr_md5_update(&md5, str, str_len);
  371   apr_md5_final(hash, &md5);
  372   rand[SP_RAND_SIZE-1] = 'A';
  373   rand[SP_RAND_SIZE-2] = 'z';
  374 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  375   EVP_CIPHER_CTX_init(cipher_ctx_p);
  376 #else
  377   cipher_ctx_p = EVP_CIPHER_CTX_new();
  378 #endif
  379   EVP_EncryptInit(cipher_ctx_p, EVP_des_ede3_cbc(), key, NULL);
  380   if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
  381                         rand, SP_RAND_SIZE)) {
  382     goto failed;
  383   }
  384   buf_len+=len;
  385   if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
  386                         hash, APR_MD5_DIGESTSIZE)) {
  387     goto failed;
  388   }
  389   buf_len+=len;
  390   if(!EVP_EncryptUpdate(cipher_ctx_p, &buf[buf_len], &len,
  391                         (const unsigned char *)str, str_len)) {
  392     goto failed;
  393   }
  394   buf_len+=len;
  395   if(!EVP_EncryptFinal(cipher_ctx_p, &buf[buf_len], &len)) {
  396     goto failed;
  397   }
  398   buf_len+=len;
  399 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  400   EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
  401 #else
  402   EVP_CIPHER_CTX_free(cipher_ctx_p);
  403 #endif
  404 
  405   e = (char *)apr_pcalloc(r->pool, 1 + apr_base64_encode_len(buf_len));
  406   len = apr_base64_encode(e, (const char *)buf, buf_len);
  407   e[len] = '\0';
  408   return e;
  409 
  410  failed:
  411 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  412   EVP_CIPHER_CTX_cleanup(cipher_ctx_p);
  413 #else
  414   EVP_CIPHER_CTX_free(cipher_ctx_p);
  415 #endif
  416   ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, r,
  417                 SP_LOG_PFX(011)"Failed to encrypt data.");
  418   return "";
  419 }
  420 
  421 /**
  422  * Similar to strstr but restricting the length of s1 (if not null terminated).
  423  *
  424  * @param s1 String to search in
  425  * @param s2 Pattern to ind
  426  * @param len Length of s1
  427  * @return pointer to the beginning of the substring s2 within s1, or NULL
  428  *         if the substring is not found
  429  */
  430 static char *sp_strnstr(const char *s1, const char *s2, int len) {
  431   const char *e1 = &s1[len-1];
  432   const char *p, *q;
  433   for (; *s1 && (s1 <= e1); s1++) {
  434     p = s1, q = s2;
  435     while(*q && *p && (q <= e1)) {
  436       if (*q != *p) {
  437         break;
  438       }
  439       p++, q++;
  440     }
  441     if(q > e1) {
  442       return NULL;
  443     }
  444     if(*q == 0) {
  445       return (char *)s1;
  446     }
  447   }
  448   return 0;
  449 }
  450 
  451 static int sp_isnum(const char *x) {
  452   const char *p = x;
  453   if(x == NULL || x[0] == 0) {
  454     return 0;
  455   }
  456   while(p && p[0]) {
  457     if(!apr_isdigit(p[0])) {
  458       return 0;
  459     }
  460     p++;
  461   }
  462   return 1;
  463 }
  464 /**
  465  * Retuns the hex value of the character
  466  *
  467  * @param x
  468  * @return hex value
  469  */
  470 static int sp_hex2c(const char *x) {
  471   int i, ch;
  472   ch = x[0];
  473   if (apr_isdigit(ch)) {
  474     i = ch - '0';
  475   }else if (apr_isupper(ch)) {
  476     i = ch - ('A' - 10);
  477   } else {
  478     i = ch - ('a' - 10);
  479   }
  480   i <<= 4;
  481   
  482   ch = x[1];
  483   if (apr_isdigit(ch)) {
  484     i += ch - '0';
  485   } else if (apr_isupper(ch)) {
  486     i += ch - ('A' - 10);
  487   } else {
  488     i += ch - ('a' - 10);
  489   }
  490   return i;
  491 }
  492 
  493 #define SP_ISHEX(x) (((x >= '0') && (x <= '9')) ||  \
  494                      ((x >= 'a') && (x <= 'f')) ||  \
  495                      ((x >= 'A') && (x <= 'F')))
  496 
  497 
  498 /**
  499  * URL escaping/encoding (%xx, '+')
  500  *
  501  * @param pool To allocate encoded string from.
  502  * @param x String to encode
  503  * @retrun encoded string
  504  */
  505 static char *sp_url_encoding(apr_pool_t *pool, const char *x) {
  506   int i = 0;
  507   int k = 0;
  508   int len = strlen(x);
  509   char *c = apr_pcalloc(pool, (3 * len) + 1);
  510   unsigned char *in = (unsigned char *)x;
  511   while(in[i]) {
  512     if(strchr("?\n /;%=\"'.:@\\&+", in[i]) != NULL) {
  513       sprintf(&c[k], "%%%02x", in[i]);
  514       k+=3;
  515     } else {
  516       c[k] = in[i];
  517       k++;
  518     }
  519     i++;
  520   }
  521   c[k] = '\0';
  522   return c;
  523 }
  524 
  525 /**
  526  * URL unescaping/decoding (%xx, '+')
  527  *
  528  * @param x String to decode
  529  * @param error Error message if decoding fails
  530  * @return Length of the decoded string
  531  */
  532 static int sp_url_decoding(char *x, int *error) {
  533   int i, j, ch;
  534   if(x == 0) {
  535     return 0;
  536   }
  537   if(x[0] == '\0') {
  538     return 0;
  539   }
  540   for(i = 0, j = 0; x[i] != '\0'; i++, j++) {
  541     ch = x[i];
  542     if(ch == '%') {
  543       if(SP_ISHEX(x[i + 1]) && SP_ISHEX(x[i + 2])) {
  544         ch = sp_hex2c(&x[i + 1]);
  545         i += 2;
  546       } else {
  547         (*error)++;
  548       }
  549     } else if(ch == '+') {
  550       ch = ' ';
  551     }
  552     x[j] = ch;
  553   }
  554   x[j] = '\0';
  555   return j;
  556 }
  557 
  558 /**
  559  * If a Header name contains characters other than:
  560  *    -,_,[A-Z\, [a-z] and [0-9].
  561  * assume the header name is a regular expression.
  562  *
  563  * @param p
  564  * @param name
  565  * @return 1 if it is a regular expression or 0 if it can be treaten as literal string 
  566  */
  567 static int sp_is_header_regex(apr_pool_t *p, const char* name) {
  568   ap_regex_t *preg = ap_pregcomp(p, "^[-A-Za-z0-9_]*$", (AP_REG_EXTENDED|AP_REG_NOSUB));
  569   ap_assert(preg != NULL);
  570   if(ap_regexec(preg, name, 0, NULL, 0)) {
  571     return 1;
  572   }
  573   return 0;
  574 }
  575 
  576 /**
  577  * If the input string does not take advantage of regular
  578  * expression metacharacters, return a pointer to an equivalent
  579  * string that can be searched using apr_strmatch().  (The
  580  * returned string will often be the input string.  But if
  581  * the input string contains escaped characters, the returned
  582  * string will be a copy with the escapes removed.)
  583  */
  584 static const char *sp_non_regex_pattern(apr_pool_t *p, const char *s) {
  585   const char *src = s;
  586   int escapes_found = 0;
  587   int in_escape = 0;
  588   
  589   while (*src) {
  590     switch (*src) {
  591     case '^':
  592     case '.':
  593     case '$':
  594     case '|':
  595     case '(':
  596     case ')':
  597     case '[':
  598     case ']':
  599     case '*':
  600     case '+':
  601     case '?':
  602     case '{':
  603     case '}':
  604       if(!in_escape) {
  605         return NULL;
  606       }
  607       in_escape = 0;
  608       break;
  609     case '\\':
  610       if(!in_escape) {
  611         in_escape = 1;
  612         escapes_found = 1;
  613       } else {
  614         in_escape = 0;
  615       }
  616       break;
  617     default:
  618       if(in_escape) {
  619         return NULL;
  620       }
  621       break;
  622     }
  623     src++;
  624   }
  625   if(!escapes_found) {
  626     return s;
  627   } else {
  628     char *unescaped = (char *)apr_palloc(p, src - s + 1);
  629     char *dst = unescaped;
  630     src = s;
  631     do {
  632       if(*src == '\\') {
  633         src++;
  634       }
  635     } while((*dst++ = *src++));
  636     return unescaped;
  637   }
  638 }
  639 
  640 /**
  641  * Replaces <start>var<end> (e.g. ${var}) entries by the value of the
  642  * variable within the vars table.
  643  *
  644  * @param r
  645  * @param start_marker Defines thow the vairable beginns, e,g "${" or "b64("
  646  * @param start_marker_len Lenth of the marker string
  647  * @param end_marker Counterpart to the start marker, e.g. "}" or ")"
  648  * @param string String to respove variables
  649  * @param func Function to replace the placeholder
  650  * @param envvar Name of the variable to replace the value for (itself, optional)
  651  * @returns 1 (success) if all start markers could be replaced
  652  */
  653 static int sp_reslove_variable(request_rec *r,
  654                                const char *start_marker,
  655                                int start_marker_len,
  656                                const char end_marker,
  657                                char **string,
  658                                char *(*func)(request_rec *, char *),
  659                                const char *envvar) {
  660   char startmarker = '\0';
  661   char endmarker = '\0';
  662   int i;
  663   int start;
  664   int line_end;
  665   char *var_name;
  666   char *new_line = *string;
  667   char *line = *string;
  668   const char *val;
  669   
  670   /* functions may enclose strings which contains brackets
  671        var=Mozilla/5.0 (Windows NT 5.1; rv:2.0.1) Gecko/20110000 Firefox/3.6
  672                        ^                        ^
  673        header=base64(${var})
  674      => if we found another start marker and that we should skip the next end marker */
  675   if(sp_strnstr(start_marker, "(", start_marker_len)) {
  676     startmarker = '(';
  677     endmarker = ')';
  678   }
  679  once_again:
  680   i = 0;
  681   while(line[i] != 0) {
  682     int found_nested = 0;
  683     if(strncmp(&line[i], start_marker, start_marker_len) == 0) {
  684       line_end = i;
  685       i+=start_marker_len;
  686       start = i;
  687       // search the end_marker
  688       while((line[i] != 0) &&
  689             !((line[i] == end_marker) && (found_nested == 0))) {
  690         if(startmarker) {
  691           if(line[i] == startmarker) {
  692             found_nested++;
  693           }
  694           if((line[i] == endmarker) && found_nested) {
  695             found_nested--;
  696           }
  697         }
  698         i++;
  699       }
  700       // did we miss the end? because of more start than end markers?
  701       if(startmarker) {
  702         if(line[i] == '\0' && line[i-1] == end_marker) {
  703           i--;
  704         }
  705       }
  706       if(line[i] != end_marker) {
  707         /* no end found */
  708         break;
  709       } else {
  710         var_name = apr_pstrndup(r->pool, &line[start], i - start);
  711         val = func(r, var_name); 
  712         if(!val && envvar && (strcmp(var_name, envvar) == 0)) {
  713           /* resolving the variable itself may fail (use might want to 
  714              concatenate variables)
  715              => replace it by an empty value */
  716           val = apr_pstrdup(r->pool, "");
  717         }
  718         if(val) {
  719           line[line_end] = 0;
  720           i++;
  721           new_line = apr_pstrcat(r->pool, line, val, &line[i], NULL);
  722           line = new_line;
  723           goto once_again;
  724         }      
  725       }
  726     }
  727     i++;
  728   }
  729   if(!new_line[0] || strstr(new_line, start_marker)) {
  730     return 0;
  731   }
  732   *string = new_line;
  733   return 1;
  734 }
  735 
  736 /**
  737  * Function: replaces the env variable by its value.
  738  *
  739  * @param r
  740  * @param variable
  741  * @return
  742  */
  743 static char *sp_func_variable(request_rec *r, char *variable) {
  744   return apr_pstrdup(r->pool, apr_table_get(r->subprocess_env, variable));
  745   
  746 }
  747 
  748 /**
  749  * Function: URL encoding
  750  *
  751  * @param r
  752  * @param string String to encode
  753  * @return Encoded string
  754  */
  755 static char *sp_func_eURL(request_rec *r, char *string) {
  756   return sp_url_encoding(r->pool, string);
  757 }
  758 
  759 /**
  760  * Function: URL decoding
  761  *
  762  * @param r
  763  * @param string String to decode
  764  * @return Decoded string
  765  */
  766 static char *sp_func_dURL(request_rec *r, char *string) {
  767   int error;
  768   char *c = apr_pstrdup(r->pool, string);
  769   sp_url_decoding(c, &error);
  770   return c;
  771 }
  772 
  773 /**
  774  * Function: encryption
  775  *
  776  * @param r
  777  * @param string String to encode
  778  * @return Encoded string
  779  */
  780 static char *sp_func_enc(request_rec *r, char *string) {
  781   const char *keyStr = apr_table_get(r->subprocess_env, SP_KEY);
  782   unsigned char key[EVP_MAX_KEY_LENGTH];
  783   if(keyStr == NULL) {
  784     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  785                   SP_LOG_PFX(031)"$enc() can't encrypt string, no key available."
  786                   " "SP_KEY" variable is missing!");
  787     return "";
  788   }
  789   EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
  790                  strlen(keyStr), 1, key, NULL);
  791   
  792   return sp_enc64(r, string, key);
  793 }
  794 
  795 /**
  796  * Function: decryption
  797  *
  798  * @param r
  799  * @param string String to decrypt
  800  * @return Decrypted string
  801  */
  802 static char *sp_func_dec(request_rec *r, char *string) {
  803   const char *keyStr = apr_table_get(r->subprocess_env, SP_KEY);
  804   unsigned char key[EVP_MAX_KEY_LENGTH];
  805   if(keyStr == NULL) {
  806     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  807                   SP_LOG_PFX(031)"$enc() can't encrypt string, no key available."
  808                   " "SP_KEY" variable is missing!");
  809     return "";
  810   }
  811   EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
  812                  strlen(keyStr), 1, key, NULL);
  813   return sp_dec64(r, string, key);
  814 }
  815 
  816 
  817 /**
  818  * Function: base64 encoding
  819  *
  820  * @param r
  821  * @param string String to encode
  822  * @return Encoded string
  823  */
  824 static char *sp_func_e64(request_rec *r, char *string) {
  825   return ap_pbase64encode(r->pool, string);
  826 }
  827 
  828 /**
  829  * Function: base64 decoding
  830  *
  831  * @param r
  832  * @param string String to decode
  833  * @return Decoded string
  834  */
  835 static char *sp_func_d64(request_rec *r, char *string) {
  836   return ap_pbase64decode(r->pool, string);
  837 }
  838 
  839 /**
  840  * Function: creates radom characters
  841  *
  842  * @param r
  843  * @param num Number of bytes to return
  844  * @return Random string
  845  */
  846 static char *sp_func_RND(request_rec *r, char *num) {
  847   char *buf;
  848   apr_int64_t len = apr_atoi64(num);
  849   unsigned char *secret = apr_pcalloc(r->pool, len);
  850 #if APR_HAS_RANDOM
  851   if(apr_generate_random_bytes(secret, len) != APR_SUCCESS) {
  852     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  853                   SP_LOG_PFX(030)"$RND() can't generate random data.");
  854   }
  855 #else
  856   if(!RAND_bytes(secret, len)) {
  857     ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
  858                   SP_LOG_PFX(030)"$RND() can't generate random data.");
  859   }
  860 #endif
  861   buf = (char *)apr_pcalloc(r->pool, 1 + apr_base64_encode_len(len));
  862   apr_base64_encode(buf, (const char *)secret, len);
  863   buf[len] = '\0';
  864   return buf;
  865 }
  866 
  867 /**
  868  * List of registered functions
  869  */
  870 static sp_function_t sp_function_list[]= {
  871   { "$e64(",  5, ')', &sp_func_e64 },
  872   { "$d64(",  5, ')', &sp_func_d64 },
  873   { "$dURL(", 6, ')', &sp_func_dURL },
  874   { "$eURL(", 6, ')', &sp_func_eURL },
  875   { "$enc(",  5, ')', &sp_func_enc },
  876   { "$dec(",  5, ')', &sp_func_dec },
  877   { "$RND(",  5, ')', &sp_func_RND },
  878   { NULL,     0, 0,   NULL }
  879 };
  880 
  881 /**
  882  * Implements the kown functions, e.g. base64 encoding
  883  *
  884  * @param r
  885  * @param replaced
  886  * @return Resolved string
  887  */
  888 static char *sp_process_functions(request_rec *r, const char *replaced) {
  889   sp_function_t *e = sp_function_list;
  890   char *string = apr_pstrdup(r->pool, replaced);
  891   while(e && e->identifier) {
  892     sp_reslove_variable(r, e->identifier, e->len, e->close, &string, e->func, NULL);
  893     e++;
  894   }
  895   return string;
  896 }
  897 
  898 /**
  899  * Resolves the variable value and (un-)sets it.
  900  *
  901  * @param t
  902  * @param b Rule entry
  903  * @param val String value to process / resolve. This value is added
  904  *        as the environment variable as configured by the rule entry b.
  905  */
  906 static void sp_match_var(request_rec *r, sp_std_entry_t *b, const char *val) {
  907   if(val) {
  908     ap_regmatch_t regm[AP_MAX_REG_MATCH];
  909     apr_size_t val_len = strlen(val);
  910     if((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
  911        (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm, 0))) {
  912       if(b->variable[0] == '!') {
  913         apr_table_unset(r->subprocess_env, &b->variable[1]);
  914       } else {
  915         char *replaced = NULL;
  916         if(!b->pattern) {
  917           /* 1) respolve sub regex */
  918           replaced = ap_pregsub(r->pool, b->value, val, AP_MAX_REG_MATCH, regm);
  919         } else {
  920           /* literal string (no $1 .. $9) */
  921           replaced = apr_pstrdup(r->pool, b->value);
  922         }
  923         if(replaced) {
  924           /* 2) sp_reslove_variable() resolves all ${xxx} variables */
  925           if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced,
  926                                  &sp_func_variable, b->variable)) {
  927             /* 3) process functions */
  928             replaced = sp_process_functions(r, replaced);
  929             apr_table_set(r->subprocess_env, b->variable, replaced);
  930           }
  931         }
  932       }
  933     }
  934   }
  935   return;
  936 }
  937 
  938 /**
  939  * Same as sp_match_var() but this method adds a request query parameter
  940  */
  941 static void sp_match_query(request_rec *r, sp_std_entry_t *b, const char *val) {
  942   if(val) {
  943     ap_regmatch_t regm[AP_MAX_REG_MATCH];
  944     apr_size_t val_len = strlen(val);
  945     if((b->pattern && apr_strmatch(b->pattern, val, val_len)) ||
  946        (!b->pattern && !ap_regexec(b->preg, val, AP_MAX_REG_MATCH, regm, 0))) {
  947       char *replaced = NULL;
  948       char *query = apr_pstrdup(r->pool, b->variable);
  949       if(b->valueSet) {
  950         if(!b->pattern) {
  951           /* 1) respolve sub regex */
  952           replaced = ap_pregsub(r->pool, b->value, val, AP_MAX_REG_MATCH, regm);
  953         } else {
  954           /* literal string (no $1 .. $9) */
  955           replaced = apr_pstrdup(r->pool, b->value);
  956         }
  957       }
  958       if(replaced) {
  959         /* 2) sp_reslove_variable() resolves all ${xxx} variables */
  960         if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced,
  961                                &sp_func_variable, b->variable)) {
  962           /* 3) process functions */
  963           replaced = sp_process_functions(r, replaced);
  964         }
  965         query = apr_pstrcat(r->pool, query, "=", replaced, NULL);
  966       }
  967       if(r->args) {
  968         r->args = apr_pstrcat(r->pool, r->args, "&", query, NULL);
  969         r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "&", query, NULL);
  970       } else {
  971         r->args = query;
  972         r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "?", query, NULL);
  973       }
  974       if(r->parsed_uri.query) {
  975         r->parsed_uri.query = apr_pstrcat(r->pool, r->parsed_uri.query, "&", query, NULL);
  976       } else {
  977         r->parsed_uri.query = query;
  978       }
  979     }
  980   }
  981   return;
  982 }
  983 
  984 /**
  985  * Changes the request line query if the defined variable
  986  * has been set.
  987  *
  988  * @param r
  989  * @param vars Array of variable names
  990  */
  991 static void sp_query(request_rec *r, apr_array_header_t *vars) {
  992   if(r->unparsed_uri == NULL) {
  993     return;
  994   }
  995   int i;
  996   char **entries = (char **)vars->elts;
  997   for(i = 0; i < vars->nelts; ++i) {
  998     char *variable = entries[i];
  999     const char *newquery = apr_table_get(r->subprocess_env, variable);
 1000     if(newquery) {
 1001       if(r->args) {
 1002         // search the query within unparsed_uri
 1003         char *q = r->unparsed_uri;
 1004         while(q && q[0]) {
 1005           q = strchr(q, '?');
 1006           if(q) {
 1007             if(q > r->unparsed_uri) {
 1008               if(q[-1] != '\\') {
 1009                 q[0] = '\0';
 1010               } else {
 1011                 // escaped, search next
 1012                 q++;
 1013               }
 1014             } else {
 1015               q[0] = '\0';
 1016             }
 1017           }
 1018         }
 1019       }
 1020       r->unparsed_uri = apr_pstrcat(r->pool, r->unparsed_uri, "?", newquery, NULL);
 1021       r->args = apr_pstrdup(r->pool, newquery);
 1022       r->parsed_uri.query = apr_pstrdup(r->pool, newquery);
 1023     }
 1024   }
 1025   return;
 1026 }
 1027 
 1028 /**
 1029  * Hashes the configured variable and stores it as a request header
 1030  */
 1031 static void sp_hash(request_rec *r, apr_array_header_t *header_hash) {
 1032   int i;
 1033   sp_hash_entry_t *entries = (sp_hash_entry_t *)header_hash->elts;
 1034   for(i = 0; i < header_hash->nelts; ++i) {
 1035     sp_hash_entry_t *b = &entries[i];
 1036     const char *var = apr_table_get(r->subprocess_env, b->var);
 1037     if(var) {
 1038       char *md = ap_md5_binary(r->pool, (unsigned char *)var, strlen(var));
 1039       char *hash = apr_pcalloc(r->pool, 64);
 1040       char *d = hash;
 1041       int c = 0;
 1042       while(md[0]) {
 1043         d[0] = md[0];
 1044         d++;
 1045         c++;
 1046         md++;
 1047         if(c == 4 && md[0]) {
 1048           c=0;
 1049           d[0] = ':';
 1050           d++;
 1051         }
 1052       }
 1053       d[0] = '\0';
 1054       apr_table_set(r->headers_in, b->header, hash);
 1055     }
 1056   }
 1057 }
 1058 
 1059 /**
 1060  * Compares the variables defined by SetEnvIfCmpPlus and
 1061  * adds the specified variable.
 1062  *
 1063  * @param r
 1064  * @param cmps Rules to process
 1065  */
 1066 static void sp_cmp(request_rec *r, apr_array_header_t * cmps) {
 1067   int i;
 1068   sp_cmp_entry_t *entries = (sp_cmp_entry_t *)cmps->elts;
 1069   for(i = 0; i < cmps->nelts; ++i) {
 1070     sp_cmp_entry_t *b = &entries[i];
 1071     const char *leftStr = apr_table_get(r->subprocess_env, b->left);
 1072     const char *rightStr = apr_table_get(r->subprocess_env, b->right);
 1073     if(leftStr != NULL && rightStr != NULL) {
 1074       int set = 0;
 1075       if(sp_isnum(leftStr) && sp_isnum(rightStr)) {
 1076         int left = atoi(leftStr);
 1077         int right = atoi(rightStr);
 1078         switch (b->cmp) {
 1079         case SP_CMP_EQ:
 1080           if(left == right) {
 1081             set = 1;
 1082           }
 1083           break;
 1084         case SP_CMP_NE:
 1085           if(left != right) {
 1086             set = 1;
 1087           }
 1088           break;
 1089         case SP_CMP_GT:
 1090           if(left > right) {
 1091             set = 1;
 1092           }
 1093           break;
 1094         case SP_CMP_LT:
 1095           if(left < right) {
 1096             set = 1;
 1097           }
 1098           break;
 1099         }
 1100       } else {
 1101         int c = strcasecmp(leftStr, rightStr);
 1102         switch (b->cmp) {
 1103         case SP_CMP_EQ:
 1104           if(c == 0) {
 1105             set = 1;
 1106           }
 1107           break;
 1108         case SP_CMP_NE:
 1109           if(c != 0) {
 1110             set = 1;
 1111           }
 1112           break;
 1113         case SP_CMP_GT:
 1114           if(c < 0) {
 1115             set = 1;
 1116           }
 1117           break;
 1118         case SP_CMP_LT:
 1119           if(c > 0) {
 1120             set = 1;
 1121           }
 1122           break;
 1123         }
 1124       }
 1125       if(set) {
 1126         if(b->variable[0] == '!') {
 1127           apr_table_unset(r->subprocess_env, &b->variable[1]);
 1128         } else {
 1129           char *replaced = apr_pstrdup(r->pool, b->value);
 1130           sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced, &sp_func_variable, NULL);
 1131           apr_table_set(r->subprocess_env, b->variable, replaced);
 1132         }
 1133       }
 1134     }
 1135   }
 1136 }
 1137 
 1138 /**
 1139  * Determines the attriubte to match and calls sp_match_*() in order
 1140  * to (un-)set the variable or query
 1141  *
 1142  * @param r
 1143  * @pram headers HTTP headers (request or response)
 1144  * @param conditionals Ruls to process
 1145  * @param isQuery Sets a query parameter instead of an env variable
 1146  * @return DECLINED
 1147  */
 1148 static int sp_setenv(request_rec *r, apr_table_t *headers,
 1149                      apr_array_header_t *conditionals, int isQuery) {
 1150   int i;
 1151   sp_std_entry_t *entries = (sp_std_entry_t *)conditionals->elts;
 1152   void (*sp_match)(request_rec *, sp_std_entry_t *, const char *);
 1153   if(isQuery) {
 1154     sp_match = &sp_match_query;
 1155   } else {
 1156     sp_match = &sp_match_var;
 1157   }
 1158 
 1159   for(i = 0; i < conditionals->nelts; ++i) {
 1160     sp_std_entry_t *b = &entries[i];
 1161     const char *val = NULL;
 1162     
 1163     switch (b->special_type) {
 1164     case SP_SPECIAL_REMOTE_ADDR:
 1165       val = SF_CONN_REMOTEIP(r);
 1166       sp_match(r, b, val);
 1167       break;
 1168     case SP_SPECIAL_REMOTE_NET:
 1169       val = sp_get_remote_net(r);
 1170       sp_match(r, b, val);
 1171       break;
 1172     case SP_SPECIAL_SERVER_ADDR:
 1173       val = r->connection->local_ip;
 1174       sp_match(r, b, val);
 1175       break;
 1176     case SP_SPECIAL_SERVER_PORT:
 1177       val = apr_psprintf(r->pool, "%d", r->connection->local_addr->port);
 1178       sp_match(r, b, val);
 1179       break;
 1180     case SP_SPECIAL_REMOTE_HOST:
 1181       val =  ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME, NULL);
 1182       sp_match(r, b, val);
 1183       break;
 1184     case SP_SPECIAL_REQUEST_URI:
 1185       val = r->uri;
 1186       sp_match(r, b, val);
 1187       break;
 1188     case SP_SPECIAL_REQUEST_METHOD:
 1189       val = r->method;
 1190       sp_match(r, b, val);
 1191       break;
 1192     case SP_SPECIAL_REQUEST_PROTOCOL:
 1193       val = r->protocol;
 1194       sp_match(r, b, val);
 1195       break;
 1196     case SP_SPECIAL_REQUEST_QUERY:
 1197       val = r->args;
 1198       sp_match(r, b, val);
 1199       break;
 1200     case SP_SPECIAL_REQUEST_USER:
 1201       val = r->user;
 1202       sp_match(r, b, val);
 1203       break;
 1204     case SP_SPECIAL_RESPONSE_STATUS:
 1205       val = apr_psprintf(r->pool, "%d", r->status);
 1206       sp_match(r, b, val);
 1207       break;
 1208     case SP_SPECIAL_NOT:
 1209       if (b->pnamereg) {
 1210         /* Matching headers_in against a regex. Iterate through
 1211          * the headers until we find a match or run out of
 1212          * headers.
 1213          */
 1214         int j;
 1215         const apr_array_header_t *arr = apr_table_elts(headers);
 1216         const apr_table_entry_t *elts = (const apr_table_entry_t *)arr->elts;
 1217         for(j = 0; j < arr->nelts; ++j) {
 1218           if(!ap_regexec(b->pnamereg, elts[j].key, 0, NULL, 0)) {
 1219             val = elts[j].val;
 1220             sp_match(r, b, val);
 1221           }
 1222         }
 1223       } else {
 1224         /* Not matching against a regex */
 1225         val = apr_table_get(headers, b->name);
 1226         if(val == NULL) {
 1227           val = apr_table_get(r->subprocess_env, b->name);
 1228         }
 1229         sp_match(r, b, val);
 1230       }
 1231     }
 1232   }  
 1233   return DECLINED;
 1234 }
 1235 
 1236 /**
 1237  * Evaluates a condition variable by looking it up within the
 1238  * provided process variable table.
 1239  *
 1240  * @param subprocess_env Process environment variable table
 1241  * @param conftition Variable name (optionally negotiated by a '!')
 1242  *        to lookup
 1243  * @return 1 If the variable is set or 0 if not
 1244  */
 1245 static int sp_condition(apr_table_t *subprocess_env, const char *condition) {
 1246   if(condition[0] == '!') {
 1247     if(!apr_table_get(subprocess_env, &condition[1])) {
 1248       return 1;
 1249     }
 1250   } else {
 1251     if(apr_table_get(subprocess_env, condition)) {
 1252       return 1;
 1253     }
 1254   }
 1255   return 0;
 1256 }
 1257 
 1258 /**
 1259  * Modifies the request or response headers as configured by on of the
 1260  * following directives:
 1261  * - ChangeRequestHeaderPlus
 1262  * - ChangeResponseHeaderPlus
 1263  *
 1264  * @param r
 1265  * @param headers Either the in or out headers table
 1266  * @param change_rules Configuration to apply
 1267  */
 1268 static void sp_change_header(request_rec *r, apr_table_t *headers, apr_table_t *change_rules) {
 1269   ap_regmatch_t regm[AP_MAX_REG_MATCH];
 1270   apr_table_t *changed = apr_table_make(r->pool, 20); /* modified headers */
 1271   apr_table_t *orig = apr_table_make(r->pool, 20);    /* unchanged headers */
 1272   int i;
 1273   apr_table_entry_t *header = (apr_table_entry_t *)apr_table_elts(headers)->elts;
 1274   for(i = 0; i < apr_table_elts(headers)->nelts; i++) {
 1275     int match = 0;
 1276     int k;
 1277     apr_table_entry_t *rule = (apr_table_entry_t *)apr_table_elts(change_rules)->elts;
 1278     for(k = 0; k < apr_table_elts(change_rules)->nelts; k++) {
 1279       sp_hdr_entry_t *b = (sp_hdr_entry_t *)rule[k].val;
 1280       if(strcasecmp(b->name, header[i].key) == 0) {
 1281         if(!ap_regexec(b->preg, header[i].val, AP_MAX_REG_MATCH, regm, 0)) {
 1282           char *replaced = ap_pregsub(r->pool, b->value, header[i].val, AP_MAX_REG_MATCH, regm);
 1283           sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &replaced, &sp_func_variable, NULL);
 1284           replaced = sp_process_functions(r, replaced);
 1285           apr_table_addn(changed, header[i].key, replaced);
 1286           match = 1;
 1287           // content type of the http response is stored within the request record too
 1288           if((strcasecmp("content-type", b->name) == 0) &&
 1289              r->content_type &&
 1290              (strcasecmp(r->content_type, header[i].val) == 0)){
 1291             r->content_type = replaced;
 1292           }
 1293           break;
 1294         }
 1295       }
 1296     }
 1297     if(!match) {
 1298       apr_table_addn(orig, header[i].key, header[i].val);
 1299     }
 1300   }
 1301   apr_table_clear(headers);
 1302   header = (apr_table_entry_t *)apr_table_elts(orig)->elts;
 1303   for(i = 0; i < apr_table_elts(orig)->nelts; i++) {
 1304     apr_table_addn(headers, header[i].key, header[i].val);
 1305   }
 1306   header = (apr_table_entry_t *)apr_table_elts(changed)->elts;
 1307   for(i = 0; i < apr_table_elts(changed)->nelts; i++) {
 1308     apr_table_addn(headers, header[i].key, header[i].val);
 1309   }
 1310 }
 1311 
 1312 /**
 1313  * Removes the pattern matching the regular expression from
 1314  * the header table.
 1315  * Empty header is removed.
 1316  *
 1317  * @param r
 1318  * @param header Headers to process
 1319  * @param list Rules (patterns for header names)*
 1320  */
 1321 static void sp_removepattern(request_rec *r, apr_table_t *headers, apr_array_header_t *list) {
 1322   int i;
 1323   sp_hdr_entry_t *entries = (sp_hdr_entry_t *)list->elts;
 1324   ap_regmatch_t ma[AP_MAX_REG_MATCH];
 1325   for(i = 0; i < list->nelts; ++i) {
 1326     sp_hdr_entry_t *b = &entries[i];
 1327     const char *header = apr_table_get(headers, b->name);
 1328     if(header) {
 1329       int changed = 0;
 1330       while(!ap_regexec(b->preg, header, AP_MAX_REG_MATCH, ma, 0)) {
 1331         int j = 1;
 1332         const char *h = &header[ma[j].rm_eo];
 1333         header = apr_pstrndup(r->pool, header, ma[j].rm_so);
 1334         header = apr_pstrcat(r->pool, header, h, NULL);
 1335         changed++;
 1336       }
 1337       if(changed) {
 1338         if(header == NULL || header[0] == '\0') {
 1339           apr_table_unset(headers, b->name);
 1340         } else {
 1341           apr_table_set(headers, b->name, header);
 1342         }
 1343       }
 1344     }
 1345   }
 1346   return;
 1347 }
 1348 
 1349 /**
 1350  * Adds the specified output filters
 1351  * @param r
 1352  * @param filters
 1353  */
 1354 static void sp_outfilter(request_rec *r, apr_array_header_t *filters) {
 1355   int i;
 1356   sp_filter_entry_t *entries = (sp_filter_entry_t *)filters->elts;
 1357   apr_table_t *addedfilter = NULL; // ensure we add the filter only once
 1358   for(i = 0; i < filters->nelts; ++i) {
 1359     sp_filter_entry_t *entry = &entries[i];
 1360     const char *var = entry->var;
 1361     if(entry->notset) {
 1362       if(apr_table_get(r->subprocess_env, var) == NULL) {
 1363         if(addedfilter == NULL) {
 1364           addedfilter = apr_table_make(r->pool, 2);
 1365         }
 1366         if(apr_table_get(addedfilter, entry->filtername) == NULL) {
 1367           apr_table_add(addedfilter, entry->filtername, "");
 1368           ap_add_output_filter(entry->filtername, NULL, r, r->connection);
 1369         }
 1370       }
 1371     } else {
 1372       if(apr_table_get(r->subprocess_env, var) != NULL) {
 1373         if(addedfilter == NULL) {
 1374           addedfilter = apr_table_make(r->pool, 2);
 1375         }
 1376         if(apr_table_get(addedfilter, entry->filtername) == NULL) {
 1377           apr_table_add(addedfilter, entry->filtername, "");
 1378           ap_add_output_filter(entry->filtername, NULL, r, r->connection);
 1379         }
 1380       }
 1381     }
 1382   }
 1383 }
 1384 
 1385 /**
 1386  * Changes the response status code if the configured
 1387  * event is set.
 1388  */
 1389 static void sp_status(request_rec *r, apr_array_header_t *vars) {
 1390   int i;
 1391   sp_status_entry_t *entries = (sp_status_entry_t *)vars->elts;
 1392   for(i = 0; i < vars->nelts; ++i) {
 1393     sp_status_entry_t *entry = &entries[i];
 1394     const char *var = entry->var;
 1395     if(apr_table_get(r->subprocess_env, var) != NULL) {
 1396       r->status = entry->code;
 1397     }
 1398   }
 1399 }
 1400 
 1401 /**
 1402  * sets/unsets/adds headers to the provided headers table (which is either
 1403  * the in or out headers table)
 1404  *
 1405  * note: Server and Date response header can't be set on locally (no
 1406  *       mod_proxy) served HTTP request because basic_http_header()
 1407  *       of http_filters.c writes direcly to the brigade removing 
 1408  *       and Server/Date header set by mod_setenvifplus (or other)
 1409  *       modules.
 1410  *
 1411  * @param r
 1412  * @param headers Either the in or out headers to modify
 1413  * @param list Rules to process (std_req_header, std_late_req_header
 1414  *        or std_res_header)
 1415  * @param isReponse Indicates if we are processing the reqest (0) or
 1416  *         response (1) header fields.
 1417  */
 1418 static int sp_header(request_rec *r, apr_table_t *headers, apr_array_header_t *list,
 1419                      int isResponse) {
 1420   apr_table_t *change_rules = NULL;
 1421   int i;
 1422   sp_hdr_entry_t *entries = (sp_hdr_entry_t *)list->elts;
 1423   for(i = 0; i < list->nelts; ++i) {
 1424     char *val;
 1425     sp_hdr_entry_t *b = &entries[i];
 1426     switch (b->action) {
 1427     case SP_ACTION_SET:
 1428       val = apr_pstrdup(r->pool, b->value);
 1429       if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &val, &sp_func_variable, NULL)) {
 1430         if(b->condition) {
 1431           if(sp_condition(r->subprocess_env, b->condition)) {
 1432             apr_table_set(headers, b->name, val);
 1433             // content type of the http response is stored within the request record too
 1434             if((strcasecmp("content-type", b->name) == 0) && isResponse) {
 1435               r->content_type = val;
 1436             }
 1437           }
 1438         } else {
 1439           apr_table_set(headers, b->name, val);
 1440           // content type of the http response is stored within the request record too
 1441           if((strcasecmp("content-type", b->name) == 0) && isResponse) {
 1442             r->content_type = val;
 1443           }
 1444         }
 1445       }
 1446       break;
 1447     case SP_ACTION_UNSET:
 1448       if(b->condition) {
 1449         if(sp_condition(r->subprocess_env, b->condition)) {
 1450           apr_table_unset(headers, b->name);
 1451         }
 1452       } else {
 1453         apr_table_unset(headers, b->name);
 1454       }
 1455       break;
 1456     case SP_ACTION_ADD:
 1457       val = apr_pstrdup(r->pool, b->value);
 1458       if(sp_reslove_variable(r, m_sp_var_pfx, 2, '}', &val, &sp_func_variable, NULL)) {
 1459         if(b->condition) {
 1460           if(sp_condition(r->subprocess_env, b->condition)) {
 1461             apr_table_add(headers, b->name, val);
 1462             // content type of the http response is stored within the request record too
 1463             if((strcasecmp("content-type", b->name) == 0) && isResponse) {
 1464               r->content_type = val;
 1465             }
 1466           }
 1467         } else {
 1468           apr_table_add(headers, b->name, val);
 1469           // content type of the http response is stored within the request record too
 1470           if((strcasecmp("content-type", b->name) == 0) && isResponse) {
 1471             r->content_type = val;
 1472           }
 1473         }
 1474       }
 1475       break;
 1476     case SP_ACTION_CHANGE:
 1477       if(!change_rules) {
 1478         change_rules = apr_table_make(r->pool, 1);
 1479       }
 1480       apr_table_addn(change_rules, apr_psprintf(r->pool, "%d", i), (char *)b);
 1481       break;
 1482     }
 1483   }
 1484   if(change_rules) {
 1485     sp_change_header(r, headers, change_rules);
 1486   }
 1487   return DECLINED;
 1488 }
 1489 
 1490 /**
 1491  * Extract the session cookie from the request.
 1492  *
 1493  * @param r
 1494  * @param name Name of the cookie
 1495  * @return Cookie or NULL if not found within the request headers
 1496  */
 1497 static char *sp_get_remove_cookie(request_rec *r, const char *name) {
 1498   const char *cookie_h = apr_table_get(r->headers_in, "Cookie");
 1499   if(cookie_h) {
 1500     char *cn = apr_pstrcat(r->pool, name, "=", NULL);
 1501     char *pt = ap_strcasestr(cookie_h, cn);
 1502     char *p = NULL;
 1503     while(pt && !p) {
 1504       // ensure we found the real cookie (and not an ending b64 str)
 1505       if(pt == cookie_h) {
 1506         // @beginning of the header
 1507         p = pt;
 1508         pt = NULL;
 1509       } else {
 1510         char pre = pt[-1];
 1511         if(pre == ' ' ||
 1512            pre == ';') {
 1513           // @beginnin of a cookie
 1514           p = pt;
 1515           pt = NULL;
 1516         } else {
 1517           // found patter somewhere else
 1518           pt++;
 1519           pt = ap_strcasestr(pt, cn);
 1520         }
 1521       }
 1522     }
 1523     if(p) {
 1524       char *value = NULL;
 1525       char *clean = p;
 1526       if(clean > cookie_h) {
 1527         clean--;
 1528         while(clean > cookie_h && clean[0] == ' ') {
 1529           clean[0] = '\0';
 1530           clean--;
 1531         }
 1532       }
 1533       p[0] = '\0'; /* terminate the beginning of the cookie header */
 1534       p = p + strlen(cn);
 1535       value = ap_getword(r->pool, (const char **)&p, ';');
 1536       while(p && (p[0] == ' ')) p++;
 1537       /* skip a path, if there is any */
 1538       if(p && (strncasecmp(p, "$path=", strlen("$path=")) == 0)) {
 1539         ap_getword(r->pool, (const char **)&p, ';');
 1540       }
 1541       /* restore cookie header */
 1542       if(p && p[0]) {
 1543         if(cookie_h[0]) {
 1544           cookie_h = apr_pstrcat(r->pool, cookie_h, " ", p, NULL);
 1545         } else {
 1546           cookie_h = apr_pstrcat(r->pool, p, NULL);
 1547         }
 1548       }
 1549       if(strlen(cookie_h) == 0) {
 1550         apr_table_unset(r->headers_in, "cookie");
 1551       } else {
 1552         if((strncasecmp(cookie_h, "$Version=", strlen("$Version=")) == 0) &&
 1553            (strlen(cookie_h) <= strlen("$Version=X; "))) {
 1554           /* nothing left */
 1555           apr_table_unset(r->headers_in, "cookie");
 1556         } else {
 1557           apr_table_set(r->headers_in, "Cookie", cookie_h);
 1558         }
 1559       }
 1560       return value;
 1561     }
 1562   }
 1563   return NULL;
 1564 }
 1565 
 1566 /**
 1567  * Encrypts the specified Set-Cookie headers
 1568  *
 1569  * @param r 
 1570  * @param key
 1571  * @param cookie_names List of cookies to be encrypted
 1572  * @param val Set-Cookie header value to process
 1573  * @param header2keep Table with the modified Set-Cookie headers
 1574  *        which shall replace all other Set-Cookie headers in the
 1575  *        response.
 1576  * @param modified Indicates, the the value has been modified or not
 1577  */
 1578 static void sp_encSecCookie(request_rec *r, unsigned char *key,
 1579                             apr_array_header_t *cookie_names,
 1580                             const char *val,
 1581                             apr_table_t *headers2keep, int *modified) {
 1582   int i;
 1583   char **names = (char **)cookie_names->elts;
 1584   for(i = 0; i < cookie_names->nelts; ++i) {
 1585     char *name = apr_pstrcat(r->pool, names[i], "=", NULL);
 1586     int len = strlen(name);
 1587     if(strncmp(val, name, len) == 0) {
 1588       char *p = apr_pstrdup(r->pool, val);
 1589       char *options = strchr(p, ';');
 1590       char *v = strchr(p, '=');
 1591       v++;
 1592       if(options) {
 1593         options[0] = '\0';
 1594         options++;
 1595       }
 1596       v = sp_enc64(r, v, key);
 1597       apr_table_add(headers2keep, "Set-Cookie",
 1598                     apr_pstrcat(r->pool, name, v, options ? ";" : "", options, NULL));
 1599       *modified = 1;
 1600       return;
 1601     }
 1602   }
 1603   // add without modification
 1604   apr_table_add(headers2keep, "Set-Cookie", val);
 1605 }
 1606 
 1607 /**
 1608  * Processes the outgoing Set-Cookie header fields encrypting
 1609  * the specified cookies.
 1610  *
 1611  * @param r
 1612  * @param headers Table of the outgoing HTTP headers
 1613  */
 1614 static void sp_setcookie(request_rec *r, apr_table_t *headers) {
 1615   sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
 1616   apr_array_header_t *cookie_names = sconf->cookie_names;
 1617   if(cookie_names->nelts > 0) {
 1618     const char *keyStr = apr_table_get(r->subprocess_env, SP_COOKIE_KEY);
 1619     if(keyStr) {
 1620       int i;
 1621       int modified = 0;
 1622       apr_table_t *headers2keep = apr_table_make(r->pool, 2);
 1623       apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(headers)->elts;
 1624       unsigned char key[EVP_MAX_KEY_LENGTH];
 1625       EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
 1626                      strlen(keyStr), 1, key, NULL);
 1627       for(i = 0; i < apr_table_elts(headers)->nelts; i++) {
 1628         if(strcasecmp(entry[i].key, "Set-Cookie") == 0) {
 1629           sp_encSecCookie(r, key, cookie_names, entry[i].val, headers2keep, &modified);
 1630         }
 1631       }
 1632       if(modified) {
 1633         apr_table_unset(headers, "Set-Cookie");
 1634         entry = (apr_table_entry_t *)apr_table_elts(headers2keep)->elts;
 1635         for(i = 0; i < apr_table_elts(headers2keep)->nelts; i++) {
 1636           apr_table_add(headers, entry[i].key, entry[i].val);
 1637         }
 1638       }
 1639     }
 1640   }  
 1641 }
 1642 
 1643 /**
 1644  * Processes the incomming HTTP Cookie header decrypting
 1645  * the specified cookies.
 1646  *
 1647  * @param r
 1648  * @param conf
 1649  */
 1650 static void sp_cookie(request_rec *r, sp_config_t *conf) {
 1651   apr_array_header_t *cookie_names = conf->cookie_names;
 1652   const char *decCookies = NULL;
 1653   if(cookie_names->nelts > 0) {
 1654     const char *keyStr = apr_table_get(r->subprocess_env, SP_COOKIE_KEY);
 1655     if(keyStr) {
 1656       // rule and key available
 1657       const char *cookie2 = NULL;
 1658       int i;
 1659       char **names = (char **)cookie_names->elts;
 1660       unsigned char key[EVP_MAX_KEY_LENGTH];
 1661       EVP_BytesToKey(EVP_des_ede3_cbc(), EVP_sha1(), NULL, (unsigned char *)keyStr,
 1662                      strlen(keyStr), 1, key, NULL);
 1663       for(i = 0; i < cookie_names->nelts; ++i) {
 1664         char **name = &names[i];
 1665         char *cookie = sp_get_remove_cookie(r, *name);
 1666         if(cookie) {
 1667           cookie = sp_dec64(r, cookie, key);
 1668           if(cookie) {
 1669             if(decCookies) {
 1670               decCookies = apr_pstrcat(r->pool, decCookies, "; ", *name, "=", cookie, NULL);
 1671             } else {
 1672               decCookies = apr_pstrcat(r->pool, *name, "=", cookie, NULL);
 1673             }
 1674           } else {
 1675             ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r,
 1676                           SP_LOG_PFX(040)"Drop cookie %s.", *name);
 1677           }
 1678         }
 1679         // TODO support cookie2 too
 1680         // no cookie2 header support, drop the header if we found the configured cookie
 1681         cookie2 = apr_table_get(r->headers_in, "Cookie2");
 1682         if(cookie2) {
 1683           char *search = apr_pstrcat(r->pool, *name, "=", NULL);
 1684           if(strstr(cookie2, search) != NULL) {
 1685             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 1686                           SP_LOG_PFX(041)"Drop cookie2 header '%s.'", cookie2);
 1687             apr_table_unset(r->headers_in, "Cookie2");
 1688           }
 1689         }
 1690       }
 1691     }    
 1692   }
 1693   if(decCookies) {
 1694     const char *cookie = apr_table_get(r->headers_in, "Cookie");
 1695     if(cookie) {
 1696       int cookieLen = strlen(cookie);
 1697       if(cookieLen > 1 && cookie[cookieLen-1] == ';') {
 1698         cookie = apr_pstrcat(r->pool, cookie, " ", decCookies, NULL);
 1699       } else {
 1700         cookie = apr_pstrcat(r->pool, cookie, "; ", decCookies, NULL);
 1701       }
 1702     } else {
 1703       cookie = decCookies;
 1704     }
 1705     apr_table_set(r->headers_in, "Cookie", cookie);
 1706   }
 1707 }
 1708 
 1709 /************************************************************************
 1710  * handlers
 1711  ***********************************************************************/
 1712 
 1713 static int sp_fixup(request_rec *r) {
 1714   sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
 1715   sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
 1716   if(conf) {
 1717     /* SetEnvIfPlus late */
 1718     sp_setenv(r, r->headers_in, conf->std_late_conditionals, 0);
 1719     /* SetEnvIfCmpPlus late */
 1720     sp_cmp(r, conf->cmp_late);
 1721     /*  RequestHeaderPlus late */
 1722     sp_header(r, r->headers_in, conf->std_late_req_header, 0);
 1723   }
 1724   if(sconf) {
 1725     sp_cookie(r, sconf);
 1726   }
 1727   return DECLINED;
 1728 }
 1729 
 1730 static int sp_user_id(request_rec *r) {
 1731   sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
 1732   if(conf) {
 1733     ap_regmatch_t regm[AP_MAX_REG_MATCH];
 1734     apr_array_header_t *c = conf->user;
 1735     int i;
 1736     sp_usr_entry_t *entries = (sp_usr_entry_t *)c->elts;
 1737     for(i = 0; i < c->nelts; ++i) {
 1738       sp_usr_entry_t *b = &entries[i];
 1739       const char *hdr = apr_table_get(r->headers_in, b->header);
 1740       if(hdr) {
 1741         if(!ap_regexec(b->preg, hdr, AP_MAX_REG_MATCH, regm, 0)) {
 1742           char *replaced = ap_pregsub(r->pool, b->value, hdr, AP_MAX_REG_MATCH, regm);
 1743           if(replaced) {
 1744             r->user = replaced;
 1745             r->ap_auth_type = (char*)apr_pstrdup(r->pool, "delegated");
 1746             return OK;
 1747           }
 1748         }
 1749       }
 1750     }
 1751   }
 1752   return DECLINED;
 1753 }
 1754 
 1755 static int sp_header_parser(request_rec *r) {
 1756   sp_config_t *conf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
 1757   if(conf) {
 1758     /* SetEnvIfPlus */
 1759     sp_setenv(r, r->headers_in, conf->std_conditionals, 0);
 1760     /*  RequestHeaderPlus ChangeRequestHeaderPlus */
 1761     sp_header(r, r->headers_in, conf->std_req_header, 0);
 1762     /* ChangeQueryIfPlus */
 1763     sp_query(r, conf->std_req_changequery);
 1764     /* SetQueryIfPlus */
 1765     sp_setenv(r, r->headers_in, conf->std_req_query, 1);
 1766     /* SetEnvIfCmpPlus */
 1767     sp_cmp(r, conf->cmp);
 1768 
 1769     sp_removepattern(r, r->headers_in, conf->std_removepattern_req_header);
 1770   }
 1771   return DECLINED;
 1772 }
 1773 
 1774 static int sp_post_read_request(request_rec *r) {
 1775   sp_config_t *conf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
 1776   if(conf) {
 1777     /* SetEnvIfPlus */
 1778     sp_setenv(r, r->headers_in, conf->std_conditionals, 0);
 1779     /*  RequestHeaderPlus ChangeRequestHeaderPlus */
 1780     sp_header(r, r->headers_in, conf->std_req_header, 0);
 1781     /* ChangeQueryIfPlus */
 1782     sp_query(r, conf->std_req_changequery);
 1783     /* SetQueryIfPlus */
 1784     sp_setenv(r, r->headers_in, conf->std_req_query, 1);
 1785     /* SetEnvIfCmpPlus */
 1786     sp_cmp(r, conf->cmp);
 1787     /* SetHashHeaderPlus */
 1788     sp_hash(r, conf->header_hash);
 1789 
 1790   }
 1791   return DECLINED;
 1792 }
 1793 
 1794 #ifdef SP_INTERNAL_TEST
 1795 /** test handler printing all variables */
 1796 static int sp_handler(request_rec * r) {
 1797   if(strcmp(r->handler, "setenvifplus-test")) {
 1798     return DECLINED;
 1799   } else {
 1800     int i;
 1801     apr_table_entry_t *entry = (apr_table_entry_t *)apr_table_elts(r->subprocess_env)->elts;
 1802     ap_set_content_type(r, "text/plain");
 1803     for(i = 0; i < apr_table_elts(r->subprocess_env)->nelts; i++) {
 1804       ap_rprintf(r, "var: <%s><%s>\n", entry[i].key, entry[i].val);
 1805     }
 1806     entry = (apr_table_entry_t *)apr_table_elts(r->headers_in)->elts;
 1807     ap_set_content_type(r, "text/plain");
 1808     apr_table_set(r->headers_out, "X-SetEnvIfPlus",
 1809                   apr_psprintf(r->pool, "testhandler=%s", g_revision));
 1810     for(i = 0; i < apr_table_elts(r->headers_in)->nelts; i++) {
 1811       ap_rprintf(r, "hdr: <%s><%s>\n", entry[i].key, entry[i].val);
 1812     }
 1813 
 1814     ap_rprintf(r, "REMOTE_ADDR: <%s>\n", SF_CONN_REMOTEIP(r));
 1815     ap_rprintf(r, "REMOTE_HOST: <%s>\n", ap_get_remote_host(r->connection, 
 1816                                                             r->per_dir_config, REMOTE_NAME, NULL));
 1817     ap_rprintf(r, "REMOTE_NET: <%s>\n", sp_get_remote_net(r));
 1818 
 1819     ap_rprintf(r, "SERVER_ADDR: <%s>\n", r->connection->local_ip);
 1820     ap_rprintf(r, "SERVER_PORT: <%d>\n", r->connection->local_addr->port);
 1821   }
 1822   return OK;
 1823 }
 1824 #endif
 1825 
 1826 /** finalize configuration */
 1827 static int sp_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp,
 1828                             server_rec *bs) {
 1829   ap_add_version_component(pconf, apr_psprintf(pconf, "mod_setenvifplus/%s", g_revision));
 1830 #ifdef SP_INTERNAL_TEST
 1831   fprintf(stdout, "\033[1mmod_setenvifplus TEST BINARY, NOT FOR PRODUCTIVE USE\033[0m\n");
 1832   fflush(stdout);
 1833 #endif
 1834   return DECLINED;
 1835 }
 1836 
 1837 /**
 1838  * Response processing
 1839  * @param r
 1840  * @paramheaders HTTP response headers
 1841  */
 1842 static void sp_out(request_rec *r, apr_table_t *headers) {
 1843   sp_config_t *dconf = ap_get_module_config(r->per_dir_config, &setenvifplus_module);
 1844   sp_config_t *sconf = ap_get_module_config(r->server->module_config, &setenvifplus_module);
 1845 
 1846   if(sconf) {
 1847     sp_header(r, headers, sconf->std_res_header, 1);
 1848     sp_status(r, sconf->status_var);
 1849     sp_outfilter(r, sconf->outfilter);
 1850   }
 1851   if(dconf) {
 1852     sp_setenv(r, headers, dconf->std_res_conditionals, 0);
 1853     sp_header(r, headers, dconf->std_res_header, 1);
 1854     sp_status(r, dconf->status_var);
 1855     sp_outfilter(r, dconf->outfilter);
 1856   }
 1857   return;
 1858 }
 1859 
 1860 static apr_status_t sp_out_err_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
 1861   request_rec *r = f->r;
 1862   sp_out(r, r->err_headers_out);
 1863   sp_setcookie(r, r->err_headers_out);
 1864   ap_remove_output_filter(f);
 1865   return ap_pass_brigade(f->next, bb); 
 1866 }
 1867 
 1868 static apr_status_t sp_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) {
 1869   request_rec *r = f->r;
 1870   sp_out(r, r->headers_out);
 1871   sp_setcookie(r, r->headers_out);
 1872   ap_remove_output_filter(f);
 1873   return ap_pass_brigade(f->next, bb); 
 1874 }
 1875 
 1876 /**
 1877  * insert response filter
 1878  */
 1879 static void sp_insert_filter(request_rec *r) {
 1880   ap_add_output_filter("sp_out_filter", NULL, r, r->connection);
 1881 }
 1882 
 1883 static void sp_insert_err_filter(request_rec *r) {
 1884   ap_add_output_filter("sp_out_err_filter", NULL, r, r->connection);
 1885 }
 1886 
 1887 /************************************************************************
 1888  * directiv handlers 
 1889  ***********************************************************************/
 1890 static void *sp_srv_config_create(apr_pool_t *p, server_rec *s) {
 1891   sp_config_t *sconf = apr_pcalloc(p, sizeof(sp_config_t));
 1892   sconf->path = NULL;
 1893   sconf->std_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1894   sconf->std_late_conditionals = NULL;
 1895   sconf->std_req_query = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1896   sconf->std_req_changequery = apr_array_make(p, 20, sizeof(char *));
 1897   sconf->std_res_conditionals = NULL;
 1898   sconf->std_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1899   sconf->std_late_req_header = NULL;
 1900   sconf->std_res_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1901   sconf->cmp = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
 1902   sconf->user = NULL;
 1903   sconf->status_var = apr_array_make(p, 2, sizeof(sp_status_entry_t));
 1904   sconf->header_hash = apr_array_make(p, 2, sizeof(sp_hash_entry_t));
 1905   sconf->outfilter = apr_array_make(p, 2, sizeof(sp_filter_entry_t));
 1906   sconf->cookie_names = apr_array_make(p, 2, sizeof(char *));
 1907   return sconf;
 1908 }
 1909 
 1910 static void *sp_srv_config_merge(apr_pool_t *p, void *basev, void *addv) {
 1911   sp_config_t *sconf = apr_pcalloc(p, sizeof(sp_config_t));
 1912   sp_config_t *base = basev;
 1913   sp_config_t *add = addv;
 1914   sconf->path = NULL;
 1915   sconf->std_conditionals = apr_array_append(p, base->std_conditionals,
 1916                                              add->std_conditionals);
 1917   sconf->std_late_conditionals = NULL;
 1918   sconf->std_req_query = apr_array_append(p, base->std_req_query,
 1919                                              add->std_req_query);
 1920   sconf->std_req_changequery = apr_array_append(p, base->std_req_changequery,
 1921                                                 add->std_req_changequery);
 1922   sconf->std_res_conditionals = NULL;
 1923   sconf->std_req_header = apr_array_append(p, base->std_req_header,
 1924                                            add->std_req_header);
 1925   sconf->std_late_req_header = NULL;
 1926   sconf->std_res_header = apr_array_append(p, base->std_res_header,
 1927                                            add->std_res_header);
 1928   sconf->cmp = apr_array_append(p, base->cmp, add->cmp);
 1929   sconf->status_var = apr_array_append(p, base->status_var, add->status_var);
 1930   sconf->header_hash = apr_array_append(p, base->header_hash, add->header_hash);
 1931   sconf->outfilter = apr_array_append(p, base->outfilter, add->outfilter);
 1932   sconf->cookie_names = apr_array_append(p, base->cookie_names, add->cookie_names);
 1933   return sconf;
 1934 }
 1935 
 1936 static void *sp_dir_config_create(apr_pool_t *p, char *d) {
 1937   sp_config_t *dconf = apr_pcalloc(p, sizeof(sp_config_t));
 1938   dconf->path = d;
 1939   dconf->std_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1940   dconf->std_late_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1941   dconf->std_req_query = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1942   dconf->std_req_changequery = apr_array_make(p, 20, sizeof(char *));
 1943   dconf->std_res_conditionals = apr_array_make(p, 20, sizeof(sp_std_entry_t));
 1944   dconf->std_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1945   dconf->std_late_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1946   dconf->std_res_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1947   dconf->cmp = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
 1948   dconf->cmp_late = apr_array_make(p, 2, sizeof(sp_cmp_entry_t));
 1949   dconf->user = apr_array_make(p, 2, sizeof(sp_usr_entry_t));
 1950   dconf->std_removepattern_req_header = apr_array_make(p, 20, sizeof(sp_hdr_entry_t));
 1951   dconf->status_var = apr_array_make(p, 2, sizeof(sp_status_entry_t));
 1952   dconf->outfilter = apr_array_make(p, 2, sizeof(sp_filter_entry_t));
 1953   return dconf;
 1954 }
 1955 
 1956 static void *sp_dir_config_merge(apr_pool_t *p, void *basev, void *addv) {
 1957   sp_config_t *dconf = apr_pcalloc(p, sizeof(sp_config_t));
 1958   sp_config_t *base = basev;
 1959   sp_config_t *add = addv;
 1960   dconf->path = add->path;
 1961   dconf->std_conditionals = apr_array_append(p, base->std_conditionals,
 1962                                              add->std_conditionals);
 1963   dconf->std_late_conditionals = apr_array_append(p, base->std_late_conditionals,
 1964                                                   add->std_late_conditionals);
 1965   dconf->std_req_query = apr_array_append(p, base->std_req_query,
 1966                                           add->std_req_query);
 1967   dconf->std_req_changequery = apr_array_append(p, base->std_req_changequery,
 1968                                                 add->std_req_changequery);
 1969   dconf->std_res_conditionals = apr_array_append(p, base->std_res_conditionals,
 1970                                                  add->std_res_conditionals);
 1971   dconf->std_req_header = apr_array_append(p, base->std_req_header,
 1972                                            add->std_req_header);
 1973   dconf->std_late_req_header = apr_array_append(p, base->std_late_req_header,
 1974                                                 add->std_late_req_header);
 1975   dconf->std_res_header = apr_array_append(p, base->std_res_header,
 1976                                            add->std_res_header);
 1977   dconf->cmp = apr_array_append(p, base->cmp, add->cmp);
 1978   dconf->cmp_late = apr_array_append(p, base->cmp_late, add->cmp_late);
 1979   dconf->user = apr_array_append(p, base->user, add->user);
 1980   dconf->status_var = apr_array_append(p, base->status_var, add->status_var);
 1981   dconf->outfilter = apr_array_append(p, base->outfilter, add->outfilter);
 1982   dconf->std_removepattern_req_header = apr_array_append(p, base->std_removepattern_req_header,
 1983                                            add->std_removepattern_req_header);
 1984   return dconf;
 1985 }
 1986 
 1987 const char *sp_header_cmd(cmd_parms *cmd, int argc, char *const argv[],
 1988                           sp_hdr_entry_t *new) {
 1989   const char *action;
 1990   if(argc < 2) {
 1991     /* always require action and header name */
 1992     return apr_psprintf(cmd->pool, "%s: takes at least two arguments",
 1993                         cmd->directive->directive);
 1994   }
 1995   action = argv[0];
 1996   new->name = apr_pstrdup(cmd->pool, argv[1]);
 1997   new->value = apr_pstrdup(cmd->pool, "");
 1998   new->condition = NULL;
 1999   if(strcasecmp(action, "set") == 0) {
 2000     new->action = SP_ACTION_SET;
 2001     if(argc < 3) {
 2002       return apr_psprintf(cmd->pool, "%s set: takes at least three arguments",
 2003                           cmd->directive->directive);
 2004     }
 2005     new->value = apr_pstrdup(cmd->pool, argv[2]);
 2006     if(argc > 3) {
 2007       char *c = argv[3];
 2008       if(strncasecmp(c, "env=", strlen("env=")) == 0) {
 2009         new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
 2010       } else {
 2011         return apr_psprintf(cmd->pool, "%s set: expects 'env=' argument",
 2012                             cmd->directive->directive);
 2013       }
 2014     }
 2015   } else if(strcasecmp(action, "add") == 0) {
 2016     new->action = SP_ACTION_ADD;
 2017     if(argc < 3) {
 2018       return apr_psprintf(cmd->pool, "%s add: takes at least three arguments",
 2019                           cmd->directive->directive);
 2020     }
 2021     new->value = apr_pstrdup(cmd->pool, argv[2]);
 2022     if(argc > 3) {
 2023       char *c = argv[3];
 2024       if(strncasecmp(c, "env=", strlen("env=")) == 0) {
 2025         new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
 2026       } else {
 2027         return apr_psprintf(cmd->pool, "%s add: expects 'env=' argument",
 2028                             cmd->directive->directive);
 2029       }
 2030     }
 2031   } else if(strcasecmp(action, "unset") == 0) {
 2032     new->action = SP_ACTION_UNSET;
 2033     if(argc > 2) {
 2034       char *c = argv[2];
 2035       if(strncasecmp(c, "env=", strlen("env=")) == 0) {
 2036         new->condition = apr_pstrdup(cmd->pool, &c[strlen("env=")]);
 2037       } else {
 2038         return apr_psprintf(cmd->pool, "%s unset: expects 'env=' argument",
 2039                             cmd->directive->directive);
 2040       }
 2041     }
 2042   } else {
 2043     return apr_psprintf(cmd->pool, "%s: unkown action",
 2044                         cmd->directive->directive);
 2045   }
 2046   return NULL;
 2047 }
 2048 
 2049 const char *sp_changeheader(cmd_parms *cmd, const char *header,
 2050                             const char *regex, const char *value,
 2051                             sp_hdr_entry_t *new) {
 2052   new->action = SP_ACTION_CHANGE;
 2053   new->preg = ap_pregcomp(cmd->pool, regex, (AP_REG_EXTENDED | AP_REG_ICASE));
 2054   if(new->preg == NULL) {
 2055     return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
 2056                         cmd->directive->directive, regex);
 2057   }
 2058   new->name = apr_pstrdup(cmd->pool, header);
 2059   new->value = apr_pstrdup(cmd->pool, value);
 2060   return NULL;
 2061 }
 2062 
 2063 const char *sp_changereqheader_cmd(cmd_parms *cmd, void *dcfg, const char *header,
 2064                                    const char *regex, const char *value) {
 2065   sp_hdr_entry_t *new;
 2066   sp_config_t *conf;
 2067   if(cmd->path) {
 2068     conf = dcfg;
 2069   } else {
 2070     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2071   }
 2072   new = apr_array_push(conf->std_req_header);
 2073   return sp_changeheader(cmd, header, regex, value, new);
 2074 }
 2075 
 2076 const char *sp_removepattern_cmd(cmd_parms *cmd, void *dcfg, const char *header,
 2077                                  const char *regex) {
 2078   sp_hdr_entry_t *new;
 2079   sp_config_t *conf;
 2080   char *pattern = apr_psprintf(cmd->pool, ".*(%s).*", regex);
 2081   if(cmd->path) {
 2082     conf = dcfg;
 2083   } else {
 2084     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2085   }
 2086   new = apr_array_push(conf->std_removepattern_req_header);
 2087   new->action = SP_ACTION_CHANGE;
 2088   new->preg = ap_pregcomp(cmd->pool, pattern, (AP_REG_EXTENDED | AP_REG_ICASE));
 2089   if(new->preg == NULL) {
 2090     return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
 2091                         cmd->directive->directive, pattern);
 2092   }
 2093   new->name = apr_pstrdup(cmd->pool, header);
 2094   new->value = pattern;
 2095   return NULL;
 2096 }
 2097 
 2098 const char *sp_changeresheader_cmd(cmd_parms *cmd, void *dcfg, const char *header,
 2099                                    const char *regex, const char *value) {
 2100   sp_hdr_entry_t *new;
 2101   sp_config_t *conf;
 2102   if(cmd->path) {
 2103     conf = dcfg;
 2104   } else {
 2105     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2106   }
 2107   new = apr_array_push(conf->std_res_header);
 2108   return sp_changeheader(cmd, header, regex, value, new);
 2109 }
 2110 
 2111 const char *sp_cmp_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
 2112   sp_cmp_entry_t *new;
 2113   char *del;
 2114   int late = 0;
 2115   sp_config_t *conf;
 2116   if(cmd->path) {
 2117     conf = dcfg;
 2118     if(argc == 5) {
 2119       if(strcasecmp(argv[4], "late") == 0) {
 2120         late = 1;
 2121       } else {
 2122         return apr_psprintf(cmd->pool, "%s: unknown parameter '%s'",
 2123                             cmd->directive->directive, argv[4]);
 2124       }
 2125     } else if(argc != 4) {
 2126       return apr_psprintf(cmd->pool, "%s: requires 4 or 5 arguments",
 2127                           cmd->directive->directive);
 2128     }
 2129   } else {
 2130     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2131     if(argc != 4) {
 2132       return apr_psprintf(cmd->pool, "%s: requires 4 arguments",
 2133                           cmd->directive->directive);
 2134     }
 2135   }
 2136   if(late) {
 2137     new = apr_array_push(conf->cmp_late);
 2138   } else {
 2139     new = apr_array_push(conf->cmp);
 2140   }
 2141   new->left = apr_pstrdup(cmd->pool, argv[0]);
 2142   if(strcasecmp(argv[1], "eq") == 0) {
 2143     new->cmp = SP_CMP_EQ;
 2144   } else if(strcasecmp(argv[1], "ne") == 0) {
 2145     new->cmp = SP_CMP_NE;
 2146   } else if(strcasecmp(argv[1], "lt") == 0) {
 2147     new->cmp = SP_CMP_LT;
 2148   } else if(strcasecmp(argv[1], "gt") == 0) {
 2149     new->cmp = SP_CMP_GT;
 2150   } else {
 2151     return apr_psprintf(cmd->pool, "%s: invalid operator '%s",
 2152                         cmd->directive->directive, argv[1]);
 2153   }
 2154   new->right = apr_pstrdup(cmd->pool, argv[2]);
 2155   new->variable = apr_pstrdup(cmd->pool, argv[3]);
 2156   del = strchr(new->variable, '=');
 2157   if(del) {
 2158     new->value = &del[1];
 2159     del[0] = '\0';
 2160   } else {
 2161     new->value = apr_pstrdup(cmd->pool, "");
 2162   }
 2163   return NULL;
 2164 }
 2165 
 2166 /**
 2167  * RequestHeaderPlus 
 2168  */
 2169 const char *sp_reqheader_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
 2170   sp_hdr_entry_t *new;
 2171   sp_config_t *conf;
 2172   if(cmd->path) {
 2173     conf = dcfg;
 2174   } else {
 2175     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2176   }
 2177   if(strcmp(argv[argc-1], "late") == 0) {
 2178     if(!cmd->path) {
 2179       return apr_psprintf(cmd->pool, "%s: 'late' option not allowed here",
 2180                           cmd->directive->directive);
 2181     }
 2182     new = apr_array_push(conf->std_late_req_header);
 2183     argc--;
 2184   } else {
 2185     new = apr_array_push(conf->std_req_header);
 2186   }
 2187   return sp_header_cmd(cmd, argc, argv, new);
 2188 }
 2189 
 2190 const char *sp_resheader_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
 2191   sp_hdr_entry_t *new;
 2192   sp_config_t *conf;
 2193   if(cmd->path) {
 2194     conf = dcfg;
 2195   } else {
 2196     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2197   }
 2198   new = apr_array_push(conf->std_res_header);
 2199   return sp_header_cmd(cmd, argc, argv, new);
 2200 }
 2201 
 2202 const char *sp_std_core_cmd(cmd_parms *cmd, apr_array_header_t *conditionals,
 2203                             const char *name, const char *regex, const char *var,
 2204                             int icase, int late) {
 2205   const char *val;
 2206   const char *simple_pattern;
 2207   sp_std_entry_t *new;
 2208 
 2209   /* create a new entry */
 2210   new = apr_array_push(conditionals);
 2211   new->name = name;
 2212   new->regex = regex;
 2213   new->icase = icase;
 2214   if((simple_pattern = sp_non_regex_pattern(cmd->pool, regex))) {
 2215     new->pattern = apr_strmatch_precompile(cmd->pool, simple_pattern, !icase);
 2216     if(new->pattern == NULL) {
 2217       return apr_psprintf(cmd->pool, "%s: strmatch pattern '%s' could not be compiled",
 2218                             cmd->directive->directive, simple_pattern);
 2219     }
 2220     new->preg = NULL;
 2221   } else {
 2222     new->preg = ap_pregcomp(cmd->pool, regex, (AP_REG_EXTENDED | (icase ? AP_REG_ICASE : 0)));
 2223     if(new->preg == NULL) {
 2224       return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
 2225                           cmd->directive->directive, regex);
 2226     }
 2227     new->pattern = NULL;
 2228   }
 2229   if(!strcasecmp(name, "remote_addr")) {
 2230     new->special_type = SP_SPECIAL_REMOTE_ADDR;
 2231   } else if(!strcasecmp(name, "remote_net")) {
 2232     new->special_type = SP_SPECIAL_REMOTE_NET;
 2233   } else if(!strcasecmp(name, "remote_host")) {
 2234     new->special_type = SP_SPECIAL_REMOTE_HOST;
 2235   } else if(!strcasecmp(name, "request_uri")) {
 2236     new->special_type = SP_SPECIAL_REQUEST_URI;
 2237   } else if(!strcasecmp(name, "request_path")) {
 2238     new->special_type = SP_SPECIAL_REQUEST_URI;
 2239   } else if(!strcasecmp(name, "request_method")) {
 2240     new->special_type = SP_SPECIAL_REQUEST_METHOD;
 2241   } else if(!strcasecmp(name, "request_protocol")) {
 2242     new->special_type = SP_SPECIAL_REQUEST_PROTOCOL;
 2243   } else if(!strcasecmp(name, "server_addr")) {
 2244     new->special_type = SP_SPECIAL_SERVER_ADDR;
 2245   } else if(!strcasecmp(name, "server_port")) {
 2246     new->special_type = SP_SPECIAL_SERVER_PORT;
 2247   } else if(!strcasecmp(name, "request_query")) {
 2248     new->special_type =  SP_SPECIAL_REQUEST_QUERY;
 2249   } else if(!strcasecmp(name, "response_status")) {
 2250     /* TODO: must be used for ResponseSetEnfIfPlus only */ 
 2251     new->special_type =  SP_SPECIAL_RESPONSE_STATUS;
 2252   } else if(!strcasecmp(name, "request_user")) {
 2253     if(!late) {
 2254       return apr_psprintf(cmd->pool, "%s: user match works only"
 2255                           " when using the 'late' handler",
 2256                           cmd->directive->directive);
 2257     }
 2258     new->special_type = SP_SPECIAL_REQUEST_USER;
 2259   } else {
 2260     new->special_type = SP_SPECIAL_NOT;
 2261     /* Handle name as a regular expression.
 2262      * If name a simple header string, identify as such
 2263      * (new->pnamereg = NULL) to avoid the overhead of searching
 2264      * through headers_in for a regex match.
 2265      */
 2266     if(sp_is_header_regex(cmd->pool, name)) {
 2267       new->pnamereg = ap_pregcomp(cmd->pool, name,
 2268                                   (AP_REG_EXTENDED | AP_REG_NOSUB
 2269                                    | (icase ? AP_REG_ICASE : 0)));
 2270       if (new->pnamereg == NULL)
 2271       return apr_psprintf(cmd->pool, "%s: header name '%s' could not be compiled",
 2272                           cmd->directive->directive, name);
 2273     } else {
 2274       new->pnamereg = NULL;
 2275     }
 2276   }
 2277   val = strchr(var, '=');
 2278   if(val) {
 2279     new->variable = apr_pstrndup(cmd->pool, var, val-var);
 2280     val++;
 2281     new->value = apr_pstrdup(cmd->pool, val);
 2282     new->valueSet = 1;
 2283   } else {
 2284     new->variable = apr_pstrdup(cmd->pool, var);
 2285     new->value = apr_pstrdup(cmd->pool, "1");
 2286     new->valueSet = 0;
 2287   }
 2288   return NULL;
 2289 }
 2290 
 2291 /**
 2292  * SetEnvIfPlus
 2293  * syntax: 
 2294  * <attribute> <regex> [!]<env-variable>[=<value>] [late]
 2295  */
 2296 const char *sp_std_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
 2297   int icase = cmd->info == SP_ICASE_MAGIC;
 2298   sp_config_t *conf;
 2299   apr_array_header_t *conditionals;
 2300   int late = 0;
 2301   const char *name;
 2302   const char *regex;
 2303   const char *var;
 2304   if(argc < 3) {
 2305     return apr_psprintf(cmd->pool, "%s: takes at least three arguments",
 2306                         cmd->directive->directive);
 2307   }
 2308   name = argv[0];
 2309   regex = argv[1];
 2310   var = argv[2];
 2311   /* determine scope of the directive */
 2312   if(cmd->path) {
 2313     if(argc > 4) {
 2314       return apr_psprintf(cmd->pool, "%s: takes not more than four arguments",
 2315                           cmd->directive->directive);
 2316     }
 2317     conf = dcfg;
 2318     conditionals = conf->std_conditionals;
 2319     if(argc == 4) {
 2320       if(strcasecmp(argv[3], "late") == 0) {
 2321         late = 1;
 2322         conditionals = conf->std_late_conditionals;
 2323       } else {
 2324         return apr_psprintf(cmd->pool, "%s: unknown parameter '%s'",
 2325                             cmd->directive->directive, argv[3]);
 2326       }
 2327     }
 2328   } else {
 2329     if(argc > 3) {
 2330       return apr_psprintf(cmd->pool, "%s: takes not more than 3 arguments"
 2331                           " when used at server level",
 2332                           cmd->directive->directive);
 2333     }
 2334     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2335     conditionals = conf->std_conditionals;
 2336   }
 2337 
 2338   return sp_std_core_cmd(cmd, conditionals, name, regex, var, icase, late);
 2339 }
 2340 
 2341 /**
 2342  * SetQueryIfPlus
 2343  * syntax: 
 2344  * <attribute> <regex> <query>[=<value>]
 2345  */
 2346 const char *sp_std_req_query_cmd(cmd_parms *cmd, void *dcfg, const char *attribute,
 2347                                  const char *regex, const char *query) {
 2348   int icase = cmd->info == SP_ICASE_MAGIC;
 2349   sp_config_t *conf;
 2350   apr_array_header_t *req_query;
 2351   /* determine scope of the directive */
 2352   if(cmd->path) {
 2353     conf = dcfg;
 2354     req_query = conf->std_req_query;
 2355   } else {
 2356     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2357     req_query = conf->std_req_query;
 2358   }
 2359 
 2360   return sp_std_core_cmd(cmd, req_query, attribute, regex, query, icase, 0);
 2361 }
 2362 
 2363 const char *sp_std_req_changequery_cmd(cmd_parms *cmd, void *dcfg, const char *var) {
 2364   sp_config_t *conf;
 2365   apr_array_header_t *req_query;
 2366   char **variable;
 2367   /* determine scope of the directive */
 2368   if(cmd->path) {
 2369     conf = dcfg;
 2370     req_query = conf->std_req_changequery;
 2371   } else {
 2372     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2373     req_query = conf->std_req_changequery;
 2374   }
 2375   variable = apr_array_push(req_query);
 2376   *variable = apr_pstrdup(cmd->pool, var);
 2377   return NULL;
 2378 }
 2379 
 2380 /**
 2381  * ResponseSetEnvIfPlus
 2382  * syntax: 
 2383  * <attribute> <regex> [!]<env-variable>[=<value>]
 2384  */
 2385 const char *sp_res_std_cmd(cmd_parms *cmd, void *dcfg, int argc, char *const argv[]) {
 2386   int icase = cmd->info == SP_ICASE_MAGIC;
 2387   sp_config_t *conf = dcfg;
 2388   int late = 0;
 2389   const char *name;
 2390   const char *regex;
 2391   const char *var;
 2392   if(argc != 3) {
 2393     return apr_psprintf(cmd->pool, "%s: takes three arguments",
 2394                         cmd->directive->directive);
 2395   }
 2396   name = argv[0];
 2397   regex = argv[1];
 2398   var = argv[2];
 2399   return sp_std_core_cmd(cmd, conf->std_res_conditionals, name, regex, var, icase, late);
 2400 }
 2401 
 2402 const char *sp_setuser_cmd(cmd_parms *cmd, void *dcfg, const char *header,
 2403                            const char *regex, const char *value) {
 2404   sp_usr_entry_t *new;
 2405   sp_config_t *conf;
 2406   if(cmd->path) {
 2407     conf = dcfg;
 2408   } else {
 2409     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2410   }
 2411   new = apr_array_push(conf->user);
 2412   new->header = apr_pstrdup(cmd->pool, header);
 2413   new->preg = ap_pregcomp(cmd->pool, regex, AP_REG_EXTENDED);
 2414   if(new->preg == NULL) {
 2415     return apr_psprintf(cmd->pool, "%s: regex pattern '%s' could not be compiled",
 2416                         cmd->directive->directive, regex);
 2417   }
 2418   new->value = apr_pstrdup(cmd->pool, value);
 2419   return NULL;
 2420 }
 2421 
 2422 const char *sp_status_cmd(cmd_parms *cmd, void *dcfg, const char *status,
 2423                            const char *var) {
 2424   sp_status_entry_t *new;
 2425   sp_config_t *conf;
 2426   int idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
 2427   if(cmd->path) {
 2428     conf = dcfg;
 2429   } else {
 2430     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2431   }
 2432   new = apr_array_push(conf->status_var);
 2433   new->code = atoi(status);
 2434   if(new->code == 0) {
 2435     return apr_psprintf(cmd->pool, "%s: invalid status code",
 2436                         cmd->directive->directive);
 2437   }
 2438   if(new->code != 500) {
 2439     if(ap_index_of_response(new->code) == idx500) {
 2440       return apr_psprintf(cmd->pool, "%s: unsupported HTTP response code", 
 2441                           cmd->directive->directive);
 2442     }
 2443   }
 2444   new->var = var;
 2445   return NULL;
 2446 }
 2447 
 2448 
 2449 const char *sp_var_pfx_cmd(cmd_parms *cmd, void *dcfg, const char *pfx) {
 2450   const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
 2451   if (err != NULL) {
 2452     return err;
 2453   }
 2454   if(strcmp(pfx, "$") == 0) {
 2455     m_sp_var_pfx = apr_pstrdup(cmd->pool, "${");
 2456   } else if(strcmp(pfx, "%") == 0) {
 2457     m_sp_var_pfx = apr_pstrdup(cmd->pool, "%{");
 2458   } else {
 2459     return apr_psprintf(cmd->pool, "%s: unsupported variable prefix (use either %% or $)", 
 2460                         cmd->directive->directive);
 2461   }
 2462   return NULL;
 2463 }
 2464 
 2465 const char *sp_outfilter_cmd(cmd_parms *cmd, void *dcfg, const char *filter,
 2466                              const char *var) {
 2467   sp_filter_entry_t *new;
 2468   sp_config_t *conf;
 2469   if(cmd->path) {
 2470     conf = dcfg;
 2471   } else {
 2472     conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2473   }
 2474   new = apr_array_push(conf->outfilter);
 2475   new->filtername = filter;
 2476   new->var = var;
 2477   new->notset = 0;
 2478   if(new->var[0] == '!') {
 2479     new->var = &new->var[1];
 2480     new->notset = 1;
 2481   }
 2482   return NULL;
 2483 }
 2484 
 2485 const char *sp_cookie_cmd(cmd_parms *cmd, void *dcfg, const char *name) {
 2486   char **new;
 2487   sp_config_t *conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2488   new = apr_array_push(conf->cookie_names);
 2489   *new = apr_pstrdup(cmd->pool, name);
 2490   return NULL;
 2491 }
 2492 
 2493 const char *sp_hash_cmd(cmd_parms *cmd, void *dcfg, const char *header, const char *var) {
 2494   sp_config_t *conf = ap_get_module_config(cmd->server->module_config, &setenvifplus_module);
 2495   sp_hash_entry_t *new = apr_array_push(conf->header_hash);
 2496   new->header = apr_pstrdup(cmd->pool, header);
 2497   new->var = apr_pstrdup(cmd->pool, var);
 2498   return NULL;
 2499 }
 2500 
 2501 static const command_rec sp_config_cmds[] = {
 2502   AP_INIT_TAKE_ARGV("SetEnvIfPlus", sp_std_cmd, NULL,
 2503                     RSRC_CONF|ACCESS_CONF,
 2504                     "SetEnvIfPlus"
 2505                     " <attribute> <regex> [!]<env-variable>[=<value>] [late],"
 2506                     " defines an expression to apply to the specified request"
 2507                     " attribute (request header) and sets the defined"
 2508                     " environment variable if the expression matches."),
 2509   AP_INIT_TAKE_ARGV("SetEnvIfPlusNoCase", sp_std_cmd, SP_ICASE_MAGIC,
 2510                     RSRC_CONF|ACCESS_CONF,
 2511                     "SetEnvIfPlusNoCase"
 2512                     " <attribute> <regex> [!]<env-variable>[=<value>] [late]"
 2513                     " defines an expression to apply to the specified request"
 2514                     " attribute (request header) and sets the defined"
 2515                     " environment variable if the expression matches."),
 2516   AP_INIT_TAKE3("SetQueryIfPlus", sp_std_req_query_cmd, NULL,
 2517                 RSRC_CONF|ACCESS_CONF,
 2518                 "SetQueryIfPlus <attribute> <regex> <query>[=<value>]"
 2519                 " defines an expression to apply to the specified request"
 2520                 " attribute (request header) and sets the defined"
 2521                 " request query paramater if the expression matches."),
 2522   AP_INIT_TAKE1("ChangeQueryIfPlus", sp_std_req_changequery_cmd, NULL,
 2523                 RSRC_CONF|ACCESS_CONF,
 2524                 "CahngeQueryIfPlus <env-variable>"
 2525                 " replaces the request query by the value of the"
 2526                 " specifed environment variable."),
 2527   AP_INIT_TAKE_ARGV("ResponseSetEnvIfPlus", sp_res_std_cmd, NULL,
 2528                     ACCESS_CONF,
 2529                     "ResponseSetEnvIfPlus"
 2530                     " <attribute> <regex> [!]<env-variable>[=<value>],"
 2531                     " defines an expression to apply to the specified response"
 2532                     " attribute (response header) and sets the defined"
 2533                     " environment variable if the expression matches."),
 2534   AP_INIT_TAKE_ARGV("ResponseSetEnvIfPlusNoCase", sp_res_std_cmd, SP_ICASE_MAGIC,
 2535                     ACCESS_CONF,
 2536                     "ResponseSetEnvIfPlusNoCase"
 2537                     " <attribute> <regex> [!]<env-variable>[=<value>]"
 2538                     " defines an expression to apply to the specified response"
 2539                     " attribute (response header) and sets the defined"
 2540                     " environment variable if the expression matches."),
 2541   AP_INIT_TAKE_ARGV("RequestHeaderPlus", sp_reqheader_cmd, NULL,
 2542                     RSRC_CONF|ACCESS_CONF,
 2543                     "RequestHeaderPlus set|unset|add <header> [<value>] [env=[!]<env-variable>] [late],"
 2544                     " manipulates HTTP request headers. The first argument" 
 2545                     " defines the action to take while the second argument"
 2546                     " specifies the header name."
 2547                     " The 'env=' attributes"
 2548                     " is used to define additional conditions (variable must be set)."
 2549                     " The 'late' attribute enforces late processing similar to"
 2550                     " the SetEnvIfPlus directive."),
 2551   AP_INIT_TAKE_ARGV("HeaderPlus", sp_resheader_cmd, NULL,
 2552                     RSRC_CONF|ACCESS_CONF,
 2553                     "HeaderPlus directive is deprecated (use RequestHeaderPlus)."),
 2554   AP_INIT_TAKE_ARGV("ResponseHeaderPlus", sp_resheader_cmd, NULL,
 2555                     RSRC_CONF|ACCESS_CONF,
 2556                     "ResponseHeaderPlus set|unset|add <header> [<value>] [env=[!]<env-variable>],"
 2557                     " manipulates HTTP request headers. The first argument" 
 2558                     " defines the action to take while the second argument"
 2559                     " specifies the header name."
 2560                     " The 'env=' attributes"
 2561                     " is used to define additional conditions (variable must be set)."),
 2562   AP_INIT_TAKE3("ChangeRequestHeaderPlus", sp_changereqheader_cmd, NULL,
 2563                 RSRC_CONF|ACCESS_CONF,
 2564                 "ChangeRequestHeaderPlus <header> <regex> <value>, changes the"
 2565                 " value of the specified request header if the regex matches."),
 2566   AP_INIT_TAKE2("RequestHeaderRemovePattern", sp_removepattern_cmd, NULL,
 2567                 ACCESS_CONF,
 2568                 "RequestHeaderRemovePattern <header> <pattern>, removes the"
 2569                 " specified pattern from the HTTP request header."),
 2570   AP_INIT_TAKE3("ChangeResponseHeaderPlus", sp_changeresheader_cmd, NULL,
 2571                 RSRC_CONF|ACCESS_CONF,
 2572                 "ChangeResponseHeaderPlus <header> <regex> <value>, changes the"
 2573                 " value of the specified response header if the regex matches."),
 2574   AP_INIT_TAKE_ARGV("SetEnvIfCmpPlus", sp_cmp_cmd, NULL,
 2575                     RSRC_CONF|ACCESS_CONF,
 2576                     "SetEnvIfCmpPlus <env-variable1> eq|ne|gt|lt <env-variable2> [!]<env-variable>[=<value>] [late],"
 2577                     " sets the specifed environment variable if the specifed env-variables"
 2578                     " are alphabetically or numerical equal (eq), not equal (ne),"
 2579                     " greater (gt), less (lt). The value string may also contain other"
 2580                     " environment variable names surrounded by \""SP_VAR_PFX"\" and \"}\" which"
 2581                     " are resolved by the values of those variables."),
 2582   AP_INIT_TAKE3("SetUserPlus", sp_setuser_cmd, NULL,
 2583                 ACCESS_CONF,
 2584                 "SetUserPlus <header> <regex> <value>,"
 2585                 " used to set the an authenticated user name using the 'value'."),
 2586   AP_INIT_TAKE2("SetStatusPlus", sp_status_cmd, NULL,
 2587                 RSRC_CONF|ACCESS_CONF,
 2588                 "SetStatusPlus <code> <env-variable>,"
 2589                 " changes the status code of the HTTP response if the specified"
 2590                 " environment variable is set."),
 2591   // TODO support per-directory config
 2592   AP_INIT_TAKE1("CookieEncPlus", sp_cookie_cmd, NULL,
 2593                 RSRC_CONF,
 2594                 "CookieEncPlus <name>,"
 2595                 " encrypts the value of the specified cookies. The passphrase for"
 2596                 " encryption is defined by the "SP_COOKIE_KEY" variable."),
 2597   AP_INIT_TAKE2("SetHashHeaderPlus", sp_hash_cmd, NULL,
 2598                 RSRC_CONF,
 2599                 "SetHashHeaderPlus <header name> <env-variable>, creates a hash"
 2600                 " of the value stored in the defined variable and stores it"
 2601                 " within the defined requst header."),
 2602   AP_INIT_TAKE2("AddOutputFilterPlus", sp_outfilter_cmd, NULL,
 2603                 RSRC_CONF|ACCESS_CONF,
 2604                 "AddOutputFilterPlus <filter name> [!]<env-variable>,"
 2605                 " adds the ouput filter to the response if"
 2606                 " the specified environment variable is set."),
 2607   AP_INIT_TAKE1("SetVarPrefixPlus", sp_var_pfx_cmd, NULL,
 2608                 RSRC_CONF,
 2609                 "SetVarPrefixPlus '$'|'%', defines the prefix of the"
 2610                 " variable place holder which is either ${...} or"
 2611                 " %{...} for compatibilty with Apache 2.4."
 2612                 " Default is '$'."),
 2613   { NULL }
 2614 };
 2615 
 2616 /************************************************************************
 2617  * apache register 
 2618  ***********************************************************************/
 2619 static void sp_register_hooks(apr_pool_t * p) {
 2620   static const char *pre[] = { "mod_setenvif.c", "mod_ssl.c", "mod_session.c", NULL };
 2621   static const char *post[] = { "mod_headers.c", NULL };
 2622   ap_hook_post_read_request(sp_post_read_request, pre, post, APR_HOOK_MIDDLE);
 2623   ap_hook_header_parser(sp_header_parser, pre, post, APR_HOOK_MIDDLE);
 2624   ap_hook_check_user_id(sp_user_id, pre, NULL, APR_HOOK_FIRST);
 2625   ap_hook_fixups(sp_fixup, pre, post, APR_HOOK_MIDDLE);
 2626   ap_hook_post_config(sp_post_config, pre, NULL, APR_HOOK_MIDDLE);
 2627 #ifdef SP_INTERNAL_TEST
 2628   ap_hook_handler(sp_handler, NULL, NULL, APR_HOOK_MIDDLE);
 2629 #endif
 2630   ap_register_output_filter("sp_out_filter", sp_out_filter, NULL, AP_FTYPE_RESOURCE);
 2631   ap_register_output_filter("sp_out_err_filter", sp_out_err_filter, NULL, AP_FTYPE_RESOURCE);
 2632   ap_hook_insert_filter(sp_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
 2633   ap_hook_insert_error_filter(sp_insert_err_filter, NULL, NULL, APR_HOOK_MIDDLE);
 2634 }
 2635 
 2636 /************************************************************************
 2637  * apache module definition 
 2638  ***********************************************************************/
 2639 module AP_MODULE_DECLARE_DATA setenvifplus_module ={ 
 2640   STANDARD20_MODULE_STUFF,
 2641   sp_dir_config_create,                    /**< dir config creater */
 2642   sp_dir_config_merge,                     /**< dir merger */
 2643   sp_srv_config_create,                    /**< server config */
 2644   sp_srv_config_merge,                     /**< server merger */
 2645   sp_config_cmds,                          /**< command table */
 2646   sp_register_hooks,                       /**< hook registery */
 2647 };