I am running a 3-tier mod_perl setup as advised by the Guide:  

     Lightweight apache <-> mod_perl apache <-> db server.  

One of the problems with this setup is that the mod_perl apache no longer
knows some details about the client request, like the client IP.  

I used Ask Bjoern Hansen's module proxy_add_forward, which adds a
"X-Forwarded-For" header to to the request as it is forwarded to the
mod_perl apache server.

After I added SSL to my lightweight (but gaining!) apache, I found
I needed some SSL connection information on the mod_perl server as well.

I recently fell in love with mod_rewrite, and so I ported some of its
capabilities to mod_headers

The attached patch
  (cd apache_1.3.XX; patch -p1 < ProxyHeaderRewrite.patch; make install)
will add two commands to mod_headers:

   HeaderRewrite       - dynamically set headers for the client
   ProxyHeaderRewrite  - dynamically set headers for the upstream proxy server

While the original mod_headers would allow you so say:

Header append Foo "burfl"

Now you can say

HeaderRewrite append Foo "%{ENV:BURFL}", using the full RewriteCond syntax



My current lightweight apache server config looks something like:

ProxyHeaderRewrite append X-Forwarded-For      "%{REMOTE_ADDR}" 
ProxyHeaderRewrite append X-Frontend-Host      "%{HTTP_HOST}"
<VirtualHost _default_:443>
   ...
   SSLOptions StdEnvVars
   ProxyHeaderRewrite append X-SSL-Cipher "%{ENV:SSL_PROTOCOL} %{ENV:SSL_CIPHER}"
</VirtualHost>


Did I miss another way to do this?  Is this patch useful?


BTW,

the Guide on server architecture:
http://perl.apache.org/guide/strategy.html

Ask Bjoern Hansen's module proxy_add_forward
http://www.cpan.org/authors/id/ABH/mod_proxy_add_forward.c


-Tim
--- apache_1.3.12.dist/src/modules/standard/mod_headers.c       Wed Oct 27 09:26:53 
1999
+++ apache_1.3.12/src/modules/standard/mod_headers.c    Tue Jul 11 00:38:26 2000
@@ -99,9 +99,52 @@
  *  To remove a header:
  *     Header unset Author
  *
+ *
+ * Non-standard Additions:
+ *
+ *    Most code is from mod_rewrite, by
+ *                 Ralf S. Engelschall
+ *                 [EMAIL PROTECTED]
+ *    Assembled by Tim Bishop <[EMAIL PROTECTED]>
+ *
+ *
+ * HeaderRewrite  (set headers to client using RewriteCond syntax)
+ * 
+ * Syntax: HeaderRewrite action header rewriteValue
+ *  
+ * This works the same as the header directive, except that full
+ * mod_rewrite RewriteCond interpolation is performed on the rewriteValue
+ * string.  See http://www.apache.org/docs/mod/mod_rewrite.html#RewriteCond
+ * (Of course, back-references (%N, $N) have no meaning)
+ *
+ * 
+ * ProxyHeaderRewrite (set headers sent to upstream servers (if proxying))
+ *
+ * Syntax:  ProxyHeaderRewrite action header rewriteValue
+ *
+ * ProxyHeaderRewrite allows you to rewrite headers sent to upstream
+ * servers when your server is functioning as a proxy server.
+ * This is useful when you want to send additional header information
+ * to upstream servers.
+ *
+ * Bugs:  Cannot rewrite the Host header with ProxyHeaderRewrite
+ *
+ * Examples:
+ *  
+ *    # tell upstream server the ip of the request
+ *    ProxyHeaderRewrite append X-Forwarded-For      "%{REMOTE_ADDR}" 
+ *    # tell upstream server info on SSL status
+ *    <VirtualHost _default_:443>
+ *        SSLOptions StdEnvVars
+ *        ProxyHeaderRewrite append X-SSL-Cipher "%{ENV:SSL_PROTOCOL} 
+%{ENV:SSL_CIPHER}"
+ *    </VirtualHost>
+ *    # tell upstream server the virtual host used
+ *    ProxyHeaderRewrite append X-Frontend-Host "%{HTTP:Host}"
+ *    
  */
 
 #include "httpd.h"
+#include "http_log.h"
 #include "http_config.h"
 
 typedef enum {
@@ -111,12 +154,50 @@
     hdr_unset = 'u'             /* unset header */
 } hdr_actions;
 
+typedef enum {
+  hdr_string      = 's',  /* header is a string */
+  hdr_env_var     = 'v',  /* set header from env var */
+  hdr_interpolate = 'i'   /* header needs to be interpolated (not yet!) */
+} hdr_value_type;
+
+typedef enum {
+  hdr_client     = 'c',   /* modify headers for client */
+  hdr_upstream   = 'u'    /* modify headers for upstream server */
+} hdr_header_target;
+
 typedef struct {
-    hdr_actions action;
-    char *header;
-    char *value;
+  hdr_actions action;  
+  char *header;        
+  char *value; 
+  hdr_value_type value_type;    
+  hdr_header_target header_target;  /* one of hdr_client | hdr_upstream */
 } header_entry;
 
+
+/* env variable interpolation support */
+static void  expand_variables_inbuffer(request_rec *r, char *buf, int buf_len);
+static char *expand_variables(request_rec *r, char *str);
+static char *lookup_variable(request_rec *r, char *var);
+static char *lookup_header(request_rec *r, const char *name);
+
+#ifndef LONG_STRING_LEN
+#define LONG_STRING_LEN 2048
+#endif
+
+/* REMOTE_NAME returns the hostname, or the dotted quad if the
+ * hostname lookup fails.  It will force a DNS lookup according
+ * to the HostnameLookups setting.
+ *  from httd_core.h
+ */
+#define REMOTE_NAME (1)
+
+    /*
+     * The key in the r->notes table wherein we store our accumulated
+     * Vary values, and the one used for per-condition checks in a chain.
+     */
+#define VARY_KEY "headers-rewrite-Vary"
+#define VARY_KEY_THIS "headers-rewrite-Vary-this"
+
 /*
  * headers_conf is our per-module configuration. This is used as both
  * a per-dir and per-server config
@@ -168,6 +249,56 @@
         new = (header_entry *) ap_push_array(serverconf->headers);
     }
 
+    new->value_type    = hdr_string;
+    new->header_target = hdr_client;  /* modify "outgoing" (to client) headers */
+
+    if (!strcasecmp(action, "set"))
+        new->action = hdr_set;
+    else if (!strcasecmp(action, "add"))
+        new->action = hdr_add;
+    else if (!strcasecmp(action, "append"))
+        new->action = hdr_append;
+    else if (!strcasecmp(action, "unset"))
+        new->action = hdr_unset;
+    else
+        return "first argument must be add, set, append or unset.";
+
+    if (new->action == hdr_unset) {
+        if (value)
+            return "Header unset takes two arguments";
+    }
+    else if (!value)
+        return "Header requires three arguments";
+
+    if ((colon = strchr(hdr, ':')))
+        *colon = '\0';
+
+    new->header = hdr;
+    new->value = value;
+
+    return NULL;
+}
+
+static const char *
+header_rewrite_cmd(cmd_parms *cmd, headers_conf * dirconf, 
+                 char *action, char *hdr, char *value)
+{
+    header_entry *new;
+    server_rec *s = cmd->server;
+    headers_conf *serverconf =
+    (headers_conf *) ap_get_module_config(s->module_config, &headers_module);
+    char *colon;
+
+    if (cmd->path) {
+        new = (header_entry *) ap_push_array(dirconf->headers);
+    }
+    else {
+        new = (header_entry *) ap_push_array(serverconf->headers);
+    }
+
+    new->value_type    = hdr_interpolate;
+    new->header_target = hdr_client;
+
     if (!strcasecmp(action, "set"))
         new->action = hdr_set;
     else if (!strcasecmp(action, "add"))
@@ -195,36 +326,493 @@
     return NULL;
 }
 
+static const char *
+proxy_header_rewrite_cmd(cmd_parms *cmd, headers_conf * dirconf, 
+                 char *action, char *hdr, char *value)
+{
+    header_entry *new;
+    server_rec *s = cmd->server;
+    headers_conf *serverconf =
+    (headers_conf *) ap_get_module_config(s->module_config, &headers_module);
+    char *colon;
+
+    if (cmd->path) {
+        new = (header_entry *) ap_push_array(dirconf->headers);
+    }
+    else {
+        new = (header_entry *) ap_push_array(serverconf->headers);
+    }
+
+    new->value_type    = hdr_interpolate;
+    new->header_target = hdr_upstream; /* send these headers only to upstream servers 
+(that we are proxying for) */
+
+    if (!strcasecmp(action, "set"))
+        new->action = hdr_set;
+    else if (!strcasecmp(action, "add"))
+        new->action = hdr_add;
+    else if (!strcasecmp(action, "append"))
+        new->action = hdr_append;
+    else if (!strcasecmp(action, "unset"))
+        new->action = hdr_unset;
+    else
+        return "first argument must be add, set, append or unset.";
+
+    if (new->action == hdr_unset) {
+        if (value)
+            return "Header unset takes two arguments";
+    }
+    else if (!value)
+        return "Header requires three arguments";
+
+    if ((colon = strchr(hdr, ':')))
+        *colon = '\0';
+
+    new->header = hdr;
+    new->value = value;
+
+    return NULL;
+}
+
+
 static const command_rec headers_cmds[] =
 {
     {"Header", header_cmd, NULL, OR_FILEINFO, TAKE23,
      "an action, header and value"},
+    {"HeaderRewrite", header_rewrite_cmd, NULL, OR_FILEINFO, TAKE23,
+     "an action, header and environment var from which to get header value"},
+    {"ProxyHeaderRewrite", proxy_header_rewrite_cmd, NULL, OR_FILEINFO, TAKE23,
+     "an action, header and environment var from which to get header value"},
     {NULL}
 };
 
 static void do_headers_fixup(request_rec *r, array_header *headers)
 {
     int i;
+    table *target_hdrs;
+    const char *value;
 
     for (i = 0; i < headers->nelts; ++i) {
         header_entry *hdr = &((header_entry *) (headers->elts))[i];
-        switch (hdr->action) {
-        case hdr_add:
-            ap_table_addn(r->headers_out, hdr->header, hdr->value);
-            break;
-        case hdr_append:
-            ap_table_mergen(r->headers_out, hdr->header, hdr->value);
-            break;
-        case hdr_set:
-            ap_table_setn(r->headers_out, hdr->header, hdr->value);
-            break;
-        case hdr_unset:
-            ap_table_unset(r->headers_out, hdr->header);
-            break;
+
+       /* 
+       ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,
+                     "mod_header: processing header: %c %s: %s %c %c",
+                     hdr->action, hdr->header, hdr->value, 
+                     hdr->value_type, hdr->header_target);
+       */            
+
+       if ( hdr->header_target == hdr_upstream ) {
+         // skip to next header_entry if not a proxy request
+         if ( r->proxyreq != PROXY_PASS) { continue; }
+         target_hdrs = r->headers_in;
+       } else {
+         target_hdrs = r->headers_out;
+       }
+
+       if ( hdr->value_type == hdr_string ) {
+         value        = hdr->value; 
+
+       } else if ( hdr->value_type == hdr_env_var ) {
+         char *varname = hdr->value;
+
+         /* first try the internal Apache notes structure */
+         value = ap_table_get(r->notes, varname); 
+         /* if (value) ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+            "mod_header: found %s in notes table",varname); */
+         /* second try the internal Apache env structure  */
+         if (value == NULL) {
+            value = ap_table_get(r->subprocess_env, varname);
+           /* if (value) ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+              "mod_header: found %s in subprocess env",varname); */
+         }
+         /* third try the external OS env */
+         if (value == NULL) {
+            value = getenv(varname);
+           /* if (value) ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+              "mod_header: found %s using getenv",varname); */
+         } 
+
+         if (value == NULL) {
+           value = "";
+           /* ap_log_rerror(APLOG_MARK, APLOG_ERR, r,  
+              "mod_header: %s not found, using ''",varname); */
+         } 
+
+       } else if ( hdr->value_type == hdr_interpolate ) {
+             value = expand_variables(r, hdr->value);        
+             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, 
+                           "mod_header: interpolated %s -> %s",
+                           hdr->value,value);
+       } else {
+         /* not handled */
+         ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
+                     "mod_header: invalid ProxyHeaderFromEnv coding!");
+         continue;
+       }
+
+       ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,
+                     "mod_header: setting %s %s: %s",
+                     ( target_hdrs == r->headers_out )
+                     ? "client header" : "upstream header",
+                     hdr->header, value);
+       
+       
+       switch (hdr->action) {  
+       case hdr_add:
+         ap_table_addn(target_hdrs, hdr->header, value);
+         break;
+       case hdr_append:
+         ap_table_mergen(target_hdrs, hdr->header, value);
+         break;
+       case hdr_set:
+         ap_table_setn(target_hdrs, hdr->header, value);
+         break;
+       case hdr_unset:
+         ap_table_unset(target_hdrs, hdr->header);
+         break;
+       }       
+       
+    }  
+    
+}      
+
+/*
+**   This section pilfered from mod_rewrite    
+**
+**      Ralf S. Engelschall
+**      [EMAIL PROTECTED]
+**      www.engelschall.com
+*/
+
+/*
+** +-------------------------------------------------------+
+** |                                                       |
+** |             environment variable support
+** |                                                       |
+** +-------------------------------------------------------+
+*/
+
+
+
+
+static void expand_variables_inbuffer(request_rec *r, char *buf, int buf_len)
+{
+    char *newbuf;
+    newbuf = expand_variables(r, buf);
+    if (strcmp(newbuf, buf) != 0) {
+        ap_cpystrn(buf, newbuf, buf_len);
+    }
+    return;
+}
+
+static char *expand_variables(request_rec *r, char *str)
+{
+    char output[MAX_STRING_LEN];
+    char input[MAX_STRING_LEN];
+    char *cp;
+    char *cp2;
+    char *cp3;
+    int expanded;
+    char *outp;
+    char *endp;
+
+    ap_cpystrn(input, str, sizeof(input));
+    output[0] = '\0';
+    outp = output;
+    endp = output + sizeof(output);
+    expanded = 0;
+    for (cp = input; cp < input+MAX_STRING_LEN; ) {
+        if ((cp2 = strstr(cp, "%{")) != NULL) {
+            if ((cp3 = strstr(cp2, "}")) != NULL) {
+                *cp2 = '\0';
+                outp = ap_cpystrn(outp, cp, endp - outp);
+
+                cp2 += 2;
+                *cp3 = '\0';
+                outp = ap_cpystrn(outp, lookup_variable(r, cp2), endp - outp);
+
+                cp = cp3+1;
+                expanded = 1;
+                continue;
+            }
         }
+        outp = ap_cpystrn(outp, cp, endp - outp);
+        break;
+    }
+    return expanded ? ap_pstrdup(r->pool, output) : str;
+}
+
+static char *lookup_variable(request_rec *r, char *var)
+{
+    const char *result;
+    char resultbuf[LONG_STRING_LEN];
+    time_t tc;
+    struct tm *tm;
+    request_rec *rsub;
+#ifndef WIN32
+    struct passwd *pw;
+    struct group *gr;
+    struct stat finfo;
+#endif
+
+    result = NULL;
+
+    /* HTTP headers */
+    if (strcasecmp(var, "HTTP_USER_AGENT") == 0) {
+        result = lookup_header(r, "User-Agent");
+    }
+    else if (strcasecmp(var, "HTTP_REFERER") == 0) {
+        result = lookup_header(r, "Referer");
+    }
+    else if (strcasecmp(var, "HTTP_COOKIE") == 0) {
+        result = lookup_header(r, "Cookie");
+    }
+    else if (strcasecmp(var, "HTTP_FORWARDED") == 0) {
+        result = lookup_header(r, "Forwarded");
+    }
+    else if (strcasecmp(var, "HTTP_HOST") == 0) {
+        result = lookup_header(r, "Host");
+    }
+    else if (strcasecmp(var, "HTTP_PROXY_CONNECTION") == 0) {
+        result = lookup_header(r, "Proxy-Connection");
+    }
+    else if (strcasecmp(var, "HTTP_ACCEPT") == 0) {
+        result = lookup_header(r, "Accept");
+    }
+    /* all other headers from which we are still not know about */
+    else if (strlen(var) > 5 && strncasecmp(var, "HTTP:", 5) == 0) {
+        result = lookup_header(r, var+5);
+    }
+
+    /* connection stuff */
+    else if (strcasecmp(var, "REMOTE_ADDR") == 0) {
+        result = r->connection->remote_ip;
+    }
+    else if (strcasecmp(var, "REMOTE_HOST") == 0) {
+        result = (char *)ap_get_remote_host(r->connection,
+                                         r->per_dir_config, REMOTE_NAME);
+    }
+    else if (strcasecmp(var, "REMOTE_USER") == 0) {
+        result = r->connection->user;
+    }
+    else if (strcasecmp(var, "REMOTE_IDENT") == 0) {
+        result = (char *)ap_get_remote_logname(r);
     }
 
+    /* request stuff */
+    else if (strcasecmp(var, "THE_REQUEST") == 0) { /* non-standard */
+        result = r->the_request;
+    }
+    else if (strcasecmp(var, "REQUEST_METHOD") == 0) {
+        result = r->method;
+    }
+    else if (strcasecmp(var, "REQUEST_URI") == 0) { /* non-standard */
+        result = r->uri;
+    }
+    else if (strcasecmp(var, "SCRIPT_FILENAME") == 0 ||
+             strcasecmp(var, "REQUEST_FILENAME") == 0  ) {
+        result = r->filename;
+    }
+    else if (strcasecmp(var, "PATH_INFO") == 0) {
+        result = r->path_info;
+    }
+    else if (strcasecmp(var, "QUERY_STRING") == 0) {
+        result = r->args;
+    }
+    else if (strcasecmp(var, "AUTH_TYPE") == 0) {
+        result = r->connection->ap_auth_type;
+    }
+    else if (strcasecmp(var, "IS_SUBREQ") == 0) { /* non-standard */
+        result = (r->main != NULL ? "true" : "false");
+    }
+
+    /* internal server stuff */
+    else if (strcasecmp(var, "DOCUMENT_ROOT") == 0) {
+        result = (const char *) ap_document_root(r);
+    }
+    else if (strcasecmp(var, "SERVER_ADMIN") == 0) {
+        result = r->server->server_admin;
+    }
+    else if (strcasecmp(var, "SERVER_NAME") == 0) {
+        result = (const char *) ap_get_server_name(r);
+    }
+    else if (strcasecmp(var, "SERVER_ADDR") == 0) { /* non-standard */
+        result = r->connection->local_ip;
+    }
+    else if (strcasecmp(var, "SERVER_PORT") == 0) {
+        ap_snprintf(resultbuf, sizeof(resultbuf), "%u", ap_get_server_port(r));
+        result = resultbuf;
+    }
+    else if (strcasecmp(var, "SERVER_PROTOCOL") == 0) {
+        result = r->protocol;
+    }
+    else if (strcasecmp(var, "SERVER_SOFTWARE") == 0) {
+        result = ap_get_server_version();
+    }
+    else if (strcasecmp(var, "API_VERSION") == 0) { /* non-standard */
+        ap_snprintf(resultbuf, sizeof(resultbuf), "%d:%d",
+                    MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
+        result = resultbuf;
+    }
+
+    /* underlaying Unix system stuff */
+    else if (strcasecmp(var, "TIME_YEAR") == 0) {
+        tc = time(NULL);
+        tm = localtime(&tc);
+        ap_snprintf(resultbuf, sizeof(resultbuf), "%02d%02d",
+                    (tm->tm_year / 100) + 19, tm->tm_year % 100);
+        result = resultbuf;
+    }
+#define MKTIMESTR(format, tmfield) \
+    tc = time(NULL); \
+    tm = localtime(&tc); \
+    ap_snprintf(resultbuf, sizeof(resultbuf), format, tm->tmfield); \
+    result = resultbuf;
+    else if (strcasecmp(var, "TIME_MON") == 0) {
+        MKTIMESTR("%02d", tm_mon+1)
+    }
+    else if (strcasecmp(var, "TIME_DAY") == 0) {
+        MKTIMESTR("%02d", tm_mday)
+    }
+    else if (strcasecmp(var, "TIME_HOUR") == 0) {
+        MKTIMESTR("%02d", tm_hour)
+    }
+    else if (strcasecmp(var, "TIME_MIN") == 0) {
+        MKTIMESTR("%02d", tm_min)
+    }
+    else if (strcasecmp(var, "TIME_SEC") == 0) {
+        MKTIMESTR("%02d", tm_sec)
+    }
+    else if (strcasecmp(var, "TIME_WDAY") == 0) {
+        MKTIMESTR("%d", tm_wday)
+    }
+    else if (strcasecmp(var, "TIME") == 0) {
+        tc = time(NULL);
+        tm = localtime(&tc);
+        ap_snprintf(resultbuf, sizeof(resultbuf),
+                    "%02d%02d%02d%02d%02d%02d%02d", (tm->tm_year / 100) + 19,
+                    (tm->tm_year % 100), tm->tm_mon+1, tm->tm_mday,
+                    tm->tm_hour, tm->tm_min, tm->tm_sec);
+        result = resultbuf;
+               ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r, "mod_headers: interpolate: 
+RESULT='%s'", result);
+    }
+
+    /* all other env-variables from the parent Apache process */
+    else if (strlen(var) > 4 && strncasecmp(var, "ENV:", 4) == 0) {
+        /* first try the internal Apache notes structure */
+        result = ap_table_get(r->notes, var+4);
+        /* second try the internal Apache env structure  */
+        if (result == NULL) {
+            result = ap_table_get(r->subprocess_env, var+4);
+        }
+        /* third try the external OS env */
+        if (result == NULL) {
+            result = getenv(var+4);
+        }
+    }
+
+#define LOOKAHEAD(subrecfunc) \
+        if ( \
+          /* filename is safe to use */ \
+          r->filename != NULL \
+              /* - and we're either not in a subrequest */ \
+              && ( r->main == NULL \
+                  /* - or in a subrequest where paths are non-NULL... */ \
+                    || ( r->main->uri != NULL && r->uri != NULL \
+                        /*   ...and sub and main paths differ */ \
+                        && strcmp(r->main->uri, r->uri) != 0))) { \
+            /* process a file-based subrequest */ \
+            rsub = subrecfunc(r->filename, r); \
+            /* now recursively lookup the variable in the sub_req */ \
+            result = lookup_variable(rsub, var+5); \
+            /* copy it up to our scope before we destroy sub_req's pool */ \
+            result = ap_pstrdup(r->pool, result); \
+            /* cleanup by destroying the subrequest */ \
+            ap_destroy_sub_req(rsub); \
+            /* log it */ \
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r,"mod_headers: interpolate: 
+lookahead: path=%s var=%s -> val=%s", \
+                       r->filename, var+5, result); \
+            /* return ourself to prevent re-pstrdup */ \
+            return (char *)result; \
+        }
+
+    /* look-ahead for parameter through URI-based sub-request */
+    else if (strlen(var) > 5 && strncasecmp(var, "LA-U:", 5) == 0) {
+        LOOKAHEAD(ap_sub_req_lookup_uri)
+    }
+    /* look-ahead for parameter through file-based sub-request */
+    else if (strlen(var) > 5 && strncasecmp(var, "LA-F:", 5) == 0) {
+        LOOKAHEAD(ap_sub_req_lookup_file)
+    }
+
+#if !defined(WIN32) && !defined(NETWARE)
+    /* Win32 has a rather different view of file ownerships.
+       For now, just forget it */
+
+    /* file stuff */
+    else if (strcasecmp(var, "SCRIPT_USER") == 0) {
+        result = "<unknown>";
+        if (r->finfo.st_mode != 0) {
+            if ((pw = getpwuid(r->finfo.st_uid)) != NULL) {
+                result = pw->pw_name;
+            }
+        }
+        else {
+            if (stat(r->filename, &finfo) == 0) {
+                if ((pw = getpwuid(finfo.st_uid)) != NULL) {
+                    result = pw->pw_name;
+                }
+            }
+        }
+    }
+    else if (strcasecmp(var, "SCRIPT_GROUP") == 0) {
+        result = "<unknown>";
+        if (r->finfo.st_mode != 0) {
+            if ((gr = getgrgid(r->finfo.st_gid)) != NULL) {
+                result = gr->gr_name;
+            }
+        }
+        else {
+            if (stat(r->filename, &finfo) == 0) {
+                if ((gr = getgrgid(finfo.st_gid)) != NULL) {
+                    result = gr->gr_name;
+                }
+            }
+        }
+    }
+#endif /* ndef WIN32 && NETWARE*/
+
+    if (result == NULL) {
+        return ap_pstrdup(r->pool, "");
+    }
+    else {
+        return ap_pstrdup(r->pool, result);
+    }
+}
+
+static char *lookup_header(request_rec *r, const char *name)
+{
+    array_header *hdrs_arr;
+    table_entry *hdrs;
+    int i;
+
+    hdrs_arr = ap_table_elts(r->headers_in);
+    hdrs = (table_entry *)hdrs_arr->elts;
+    for (i = 0; i < hdrs_arr->nelts; ++i) {
+        if (hdrs[i].key == NULL) {
+            continue;
+        }
+        if (strcasecmp(hdrs[i].key, name) == 0) {
+            ap_table_merge(r->notes, VARY_KEY_THIS, name);
+            return hdrs[i].val;
+        }
+    }
+    return NULL;
 }
+
+
+
+
 
 static int fixup_headers(request_rec *r)
 {

Reply via email to