]> granicus.if.org Git - apache/blobdiff - server/util.c
Spelling fix in comment.
[apache] / server / util.c
index 23a968ee28785f34c72b359385c95f820d00ef11..729c754baa29acf3534b7183d6003a7332e61ebb 100644 (file)
@@ -1,8 +1,9 @@
-/* Copyright 1999-2004 The Apache Software Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
 
 /*
  * util.c: string utility things
- * 
+ *
  * 3/21/93 Rob McCool
  * 1995-96 Many changes by the Apache Software Foundation
- * 
+ *
  */
 
 /* Debugging aid:
@@ -41,8 +42,6 @@
 #include <netdb.h>              /* for gethostbyname() */
 #endif
 
-#define CORE_PRIVATE
-
 #include "ap_config.h"
 #include "apr_base64.h"
 #include "httpd.h"
@@ -95,7 +94,7 @@ AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
     semi = ap_strchr_c(intype, ';');
     if (semi == NULL) {
         return apr_pstrdup(p, intype);
-    } 
+    }
     else {
         while ((semi > intype) && apr_isspace(semi[-1])) {
             semi--;
@@ -220,11 +219,11 @@ AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
 }
 
 /* We actually compare the canonical root to this root, (but we don't
- * waste time checking the case), since every use of this function in 
+ * waste time checking the case), since every use of this function in
  * httpd-2.1 tests if the path is 'proper', meaning we've already passed
  * it through apr_filepath_merge, or we haven't.
  */
-AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir) 
+AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
 {
     const char *newpath;
     const char *ourdir = dir;
@@ -246,25 +245,25 @@ AP_DECLARE(int) ap_is_matchexp(const char *str)
 }
 
 /*
- * Here's a pool-based interface to POSIX regex's regcomp().
- * Note that we return regex_t instead of being passed one.
- * The reason is that if you use an already-used regex_t structure,
+ * Here's a pool-based interface to the POSIX-esque ap_regcomp().
+ * Note that we return ap_regex_t instead of being passed one.
+ * The reason is that if you use an already-used ap_regex_t structure,
  * the memory that you've already allocated gets forgotten, and
  * regfree() doesn't clear it. So we don't allow it.
  */
 
 static apr_status_t regex_cleanup(void *preg)
 {
-    regfree((regex_t *) preg);
+    ap_regfree((ap_regex_t *) preg);
     return APR_SUCCESS;
 }
 
-AP_DECLARE(regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
-                                   int cflags)
+AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
+                                     int cflags)
 {
-    regex_t *preg = apr_palloc(p, sizeof(regex_t));
+    ap_regex_t *preg = apr_palloc(p, sizeof *preg);
 
-    if (regcomp(preg, pattern, cflags)) {
+    if (ap_regcomp(preg, pattern, cflags)) {
         return NULL;
     }
 
@@ -274,9 +273,9 @@ AP_DECLARE(regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
     return preg;
 }
 
-AP_DECLARE(void) ap_pregfree(apr_pool_t *p, regex_t * reg)
+AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
 {
-    regfree(reg);
+    ap_regfree(reg);
     apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
 }
 
@@ -343,29 +342,10 @@ AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
     return bigstring;
 }
 
-/* 
- * Apache stub function for the regex libraries regexec() to make sure the
- * whole regex(3) API is available through the Apache (exported) namespace.
- * This is especially important for the DSO situations of modules.
- * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
- */
-AP_DECLARE(int) ap_regexec(regex_t *preg, const char *string,
-                           size_t nmatch, regmatch_t pmatch[], int eflags)
-{
-    return regexec(preg, string, nmatch, pmatch, eflags);
-}
-
-AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf,
-                               size_t errbuf_size)
-{
-    return regerror(errcode, preg, errbuf, errbuf_size);
-}
-
-
 /* This function substitutes for $0-$9, filling in regular expression
  * submatches. Pass it the same nmatch and pmatch arguments that you
  * passed ap_regexec(). pmatch should not be greater than the maximum number
- * of subexpressions - i.e. one more than the re_nsub member of regex_t.
+ * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
  *
  * input should be the string with the $-expressions, source should be the
  * string that was matched against.
@@ -378,7 +358,7 @@ AP_DECLARE(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf,
 
 AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
                               const char *source, size_t nmatch,
-                              regmatch_t pmatch[])
+                              ap_regmatch_t pmatch[])
 {
     const char *src = input;
     char *dest, *dst;
@@ -457,7 +437,7 @@ AP_DECLARE(void) ap_getparents(char *name)
     /* a) remove ./ path segments */
     for (next = name; *next && (*next != '.'); next++) {
     }
-    
+
     l = w = first_dot = next - name;
     while (name[l] != '\0') {
         if (name[l] == '.' && IS_SLASH(name[l + 1])
@@ -547,8 +527,8 @@ AP_DECLARE(void) ap_no2slash(char *name)
  * assumes n > 0
  * the return value is the ever useful pointer to the trailing \0 of d
  *
- * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments, 
- * so that if n == 0, "/" is returned in d with n == 1 
+ * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
+ * so that if n == 0, "/" is returned in d with n == 1
  * and s == "e:/test.html", "e:/" is returned in d
  * *** See also directory_walk in modules/http/http_request.c
 
@@ -598,9 +578,8 @@ AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
         return apr_pstrdup(p, "");
     }
     l = (last_slash - s) + 1;
-    d = apr_palloc(p, l + 1);
-    memcpy(d, s, l);
-    d[l] = 0;
+    d = apr_pstrmemdup(p, s, l);
+
     return (d);
 }
 
@@ -631,9 +610,7 @@ AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
     }
 
     len = pos - *line;
-    res = (char *)apr_palloc(atrans, len + 1);
-    memcpy(res, *line, len);
-    res[len] = 0;
+    res = apr_pstrmemdup(atrans, *line, len);
 
     if (stop) {
         while (*pos == stop) {
@@ -661,9 +638,7 @@ AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
     }
 
     len = pos - *line;
-    res = (char *)apr_palloc(atrans, len + 1);
-    memcpy(res, *line, len);
-    res[len] = 0;
+    res = apr_pstrmemdup(atrans, *line, len);
 
     while (apr_isspace(*pos)) {
         ++pos;
@@ -722,7 +697,7 @@ static char *substring_conf(apr_pool_t *p, const char *start, int len,
 
     *resp++ = '\0';
 #if RESOLVE_ENV_PER_TOKEN
-    return ap_resolve_env(p,result);
+    return (char *)ap_resolve_env(p,result);
 #else
     return result;
 #endif
@@ -750,10 +725,13 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     if ((quote = *str) == '"' || quote == '\'') {
         strend = str + 1;
         while (*strend && *strend != quote) {
-            if (*strend == '\\' && strend[1] && strend[1] == quote)
+            if (*strend == '\\' && strend[1] &&
+                (strend[1] == quote || strend[1] == '\\')) {
                 strend += 2;
-            else
+            }
+            else {
                 ++strend;
+            }
         }
         res = substring_conf(p, str + 1, strend - str - 1, quote);
 
@@ -782,43 +760,91 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
  */
 AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
 {
-       char tmp[ MAX_STRING_LEN ];
-       const char *s, *e;
-       tmp[0] = '\0';
-
-       if (!(s=ap_strchr_c(word,'$')))
-               return word;
-
-       do {
-               /* XXX - relies on strncat() to add '\0'
-                */
-               strncat(tmp,word,s - word);
-               if ((s[1] == '{') && (e=ap_strchr_c(s,'}'))) {
-                       const char *e2 = e;
-                       char *var;
-                       word = e + 1;
-                       var = apr_pstrndup(p, s+2, e2-(s+2));
-                       e = getenv(var);
-                       if (e) {
-                           strcat(tmp,e);
-                       } else {
-                           strncat(tmp, s, e2-s);
-                           strcat(tmp,"}");
-                       }
-               } else {
-                       /* ignore invalid strings */
-                       word = s+1;
-                       strcat(tmp,"$");
-               };
-       } while ((s=ap_strchr_c(word,'$')));
-       strcat(tmp,word);
-
-       return apr_pstrdup(p,tmp);
+# define SMALL_EXPANSION 5
+    struct sll {
+        struct sll *next;
+        const char *string;
+        apr_size_t len;
+    } *result, *current, sresult[SMALL_EXPANSION];
+    char *res_buf, *cp;
+    const char *s, *e, *ep;
+    unsigned spc;
+    apr_size_t outlen;
+
+    s = ap_strchr_c(word, '$');
+    if (!s) {
+        return word;
+    }
+
+    /* well, actually something to do */
+    ep = word + strlen(word);
+    spc = 0;
+    result = current = &(sresult[spc++]);
+    current->next = NULL;
+    current->string = word;
+    current->len = s - word;
+    outlen = current->len;
+
+    do {
+        /* prepare next entry */
+        if (current->len) {
+            current->next = (spc < SMALL_EXPANSION)
+                            ? &(sresult[spc++])
+                            : (struct sll *)apr_palloc(p,
+                                                       sizeof(*current->next));
+            current = current->next;
+            current->next = NULL;
+            current->len = 0;
+        }
+
+        if (*s == '$') {
+            if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
+                word = getenv(apr_pstrndup(p, s+2, e-s-2));
+                if (word) {
+                    current->string = word;
+                    current->len = strlen(word);
+                    outlen += current->len;
+                }
+                else {
+                    current->string = s;
+                    current->len = e - s + 1;
+                    outlen += current->len;
+                }
+                s = e + 1;
+            }
+            else {
+                current->string = s++;
+                current->len = 1;
+                ++outlen;
+            }
+        }
+        else {
+            word = s;
+            s = ap_strchr_c(s, '$');
+            current->string = word;
+            current->len = s ? s - word : ep - word;
+            outlen += current->len;
+        }
+    } while (s && *s);
+
+    /* assemble result */
+    res_buf = cp = apr_palloc(p, outlen + 1);
+    do {
+        if (result->len) {
+            memcpy(cp, result->string, result->len);
+            cp += result->len;
+        }
+        result = result->next;
+    } while (result);
+    res_buf[outlen] = '\0';
+
+    return res_buf;
 }
+
 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
 {
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
         "Done with config file %s", cfp->name);
 #endif
     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
@@ -873,7 +899,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 #ifdef DEBUG
     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
                 "Opening config file %s (%s)",
-                name, (status != APR_SUCCESS) ? 
+                name, (status != APR_SUCCESS) ?
                 apr_strerror(status, buf, sizeof(buf)) : "successful");
 #endif
     if (status != APR_SUCCESS)
@@ -904,14 +930,14 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
      * would signify utf-8 text files.
      *
      * Since MS configuration files are all protecting utf-8 encoded
-     * Unicode path, file and resource names, we already have the correct 
+     * Unicode path, file and resource names, we already have the correct
      * WinNT encoding.  But at least eat the stupid three bytes up front.
      */
     {
         unsigned char buf[4];
         apr_size_t len = 3;
         status = apr_file_read(file, buf, &len);
-        if ((status != APR_SUCCESS) || (len < 3) 
+        if ((status != APR_SUCCESS) || (len < 3)
               || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
             apr_off_t zero = 0;
             apr_file_seek(file, APR_SET, &zero);
@@ -952,16 +978,16 @@ AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
     new_cfg->line_number = 0;
     return new_cfg;
 }
-   
+
 /* Read one character from a configfile_t */
 AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
 {
     register int ch = cfp->getch(cfp->param);
-    if (ch == LF) 
+    if (ch == LF)
         ++cfp->line_number;
     return ch;
 }
-  
+
 /* Read one line from open ap_configfile_t, strip LF, increase line number */
 /* If custom handler does not define a getstr() function, read char by char */
 AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
@@ -1001,13 +1027,13 @@ AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
                         continue;
                     }
                     else {
-                        /* 
+                        /*
                          * no real continuation because escaped -
                          * then just remove escape character
                          */
                         for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
                             cp[0] = cp[1];
-                    }   
+                    }
                 }
             }
             break;
@@ -1045,7 +1071,7 @@ AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
 
         if (c == EOF)
             return 1;
-        
+
         if(bufsize < 2) {
             /* too small, assume caller is crazy */
             return 1;
@@ -1066,7 +1092,7 @@ AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
                 ++cfp->line_number;
             }
             if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
-                /* 
+                /*
                  *  check for line continuation
                  */
                 if (i > 0 && buf[i-1] == '\\') {
@@ -1464,7 +1490,7 @@ AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
     for (; *s; ++s) {
 
 #if defined(OS2) || defined(WIN32)
-        /* 
+        /*
          * Newlines to Win32/OS2 CreateProcess() are ill advised.
          * Convert them to spaces since they are effectively white
          * space to most applications
@@ -1509,54 +1535,15 @@ static char x2c(const char *what)
 }
 
 /*
- * Unescapes a URL.
+ * Unescapes a URL, leaving reserved characters intact.
  * Returns 0 on success, non-zero on error
  * Failure is due to
  *   bad % escape       returns HTTP_BAD_REQUEST
  *
- *   decoding %00 -> \0  (the null character)
- *   decoding %2f -> /   (a special character)
- *                      returns HTTP_NOT_FOUND
+ *   decoding %00 or a forbidden character returns HTTP_NOT_FOUND
  */
-AP_DECLARE(int) ap_unescape_url(char *url)
-{
-    register int badesc, badpath;
-    char *x, *y;
-
-    badesc = 0;
-    badpath = 0;
-    /* Initial scan for first '%'. Don't bother writing values before
-     * seeing a '%' */
-    y = strchr(url, '%');
-    if (y == NULL) {
-        return OK;
-    }
-    for (x = y; *y; ++x, ++y) {
-        if (*y != '%')
-            *x = *y;
-        else {
-            if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
-                badesc = 1;
-                *x = '%';
-            }
-            else {
-                *x = x2c(y + 1);
-                y += 2;
-                if (IS_SLASH(*x) || *x == '\0')
-                    badpath = 1;
-            }
-        }
-    }
-    *x = '\0';
-    if (badesc)
-        return HTTP_BAD_REQUEST;
-    else if (badpath)
-        return HTTP_NOT_FOUND;
-    else
-        return OK;
-}
 
-AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
+static int unescape_url(char *url, const char *forbid, const char *reserved)
 {
     register int badesc, badpath;
     char *x, *y;
@@ -1581,16 +1568,20 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
             else {
                 char decoded;
                 decoded = x2c(y + 1);
-                if (IS_SLASH(decoded)) {
+                if ((decoded == '\0')
+                    || (forbid && ap_strchr_c(forbid, decoded))) {
+                    badpath = 1;
+                    *x = decoded;
+                    y += 2;
+                }
+                else if (reserved && ap_strchr_c(reserved, decoded)) {
+                    *x++ = *y++;
                     *x++ = *y++;
                     *x = *y;
                 }
                 else {
                     *x = decoded;
                     y += 2;
-                    if (decoded == '\0') {
-                        badpath = 1;
-                    }
                 }
             }
         }
@@ -1606,6 +1597,36 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
         return OK;
     }
 }
+AP_DECLARE(int) ap_unescape_url(char *url)
+{
+    /* Traditional */
+#ifdef CASE_BLIND_FILESYSTEM
+    return unescape_url(url, "/\\", NULL);
+#else
+    return unescape_url(url, "/", NULL);
+#endif
+}
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
+{
+    /* AllowEncodedSlashes (corrected) */
+    return unescape_url(url, NULL, "/");
+}
+#ifdef NEW_APIS
+/* IFDEF these out until they've been thought through.
+ * Just a germ of an API extension for now
+ */
+AP_DECLARE(int) ap_unescape_url_proxy(char *url)
+{
+    /* leave RFC1738 reserved characters intact, * so proxied URLs
+     * don't get mangled.  Where does that leave encoded '&' ?
+     */
+    return unescape_url(url, NULL, "/;?");
+}
+AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
+{
+    return unescape_url(url, NULL, reserved);
+}
+#endif
 
 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
                                        apr_port_t port, const request_rec *r)
@@ -1618,6 +1639,11 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
     }
 }
 
+AP_DECLARE(int) ap_unescape_all(char *url)
+{
+    return unescape_url(url, NULL, NULL);
+}
+
 /* c2x takes an unsigned, and expects the caller has guaranteed that
  * 0 <= what < 256... which usually means that you have to cast to
  * unsigned char first, because (unsigned)(char)(x) first goes through
@@ -1630,12 +1656,13 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
  */
 static const char c2x_table[] = "0123456789abcdef";
 
-static APR_INLINE unsigned char *c2x(unsigned what, unsigned char *where)
+static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
+                                     unsigned char *where)
 {
 #if APR_CHARSET_EBCDIC
     what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
 #endif /*APR_CHARSET_EBCDIC*/
-    *where++ = '%';
+    *where++ = prefix;
     *where++ = c2x_table[what >> 4];
     *where++ = c2x_table[what & 0xf];
     return where;
@@ -1656,16 +1683,15 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char *where)
  * something with a '/' in it (and thus does not prefix "./").
  */
 
-AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
 {
-    char *copy = apr_palloc(p, 3 * strlen(segment) + 1);
     const unsigned char *s = (const unsigned char *)segment;
     unsigned char *d = (unsigned char *)copy;
     unsigned c;
 
     while ((c = *s)) {
         if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
-            d = c2x(c, d);
+            d = c2x(c, '%', d);
         }
         else {
             *d++ = c;
@@ -1676,6 +1702,11 @@ AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
     return copy;
 }
 
+AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
+{
+    return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
+}
+
 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
 {
     char *copy = apr_palloc(p, 3 * strlen(path) + 3);
@@ -1694,7 +1725,7 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
     }
     while ((c = *s)) {
         if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
-            d = c2x(c, d);
+            d = c2x(c, '%', d);
         }
         else {
             *d++ = c;
@@ -1707,7 +1738,7 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
 
 /* ap_escape_uri is now a macro for os_escape_path */
 
-AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
+AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
 {
     int i, j;
     char *x;
@@ -1718,6 +1749,10 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             j += 3;
         else if (s[i] == '&')
             j += 4;
+        else if (s[i] == '"')
+            j += 5;
+        else if (toasc && !apr_isascii(s[i]))
+            j += 5;
 
     if (j == 0)
         return apr_pstrmemdup(p, s, i);
@@ -1736,13 +1771,21 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             memcpy(&x[j], "&amp;", 5);
             j += 4;
         }
+        else if (s[i] == '"') {
+            memcpy(&x[j], "&quot;", 6);
+            j += 5;
+        }
+        else if (toasc && !apr_isascii(s[i])) {
+            char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
+            memcpy(&x[j], esc, 6);
+            j += 5;
+        }
         else
             x[j] = s[i];
 
     x[j] = '\0';
     return x;
 }
-
 AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
 {
     char *ret;
@@ -1781,8 +1824,7 @@ AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
                 *d++ = *s;
                 break;
             default:
-                c2x(*s, d);
-                *d = 'x';
+                c2x(*s, 'x', d);
                 d += 3;
             }
         }
@@ -1845,8 +1887,7 @@ AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
                     ep = --d; /* break the for loop as well */
                     break;
                 }
-                c2x(*s, d);
-                *d = 'x';
+                c2x(*s, 'x', d);
                 d += 3;
             }
         }
@@ -1951,24 +1992,9 @@ AP_DECLARE(void) ap_str_tolower(char *str)
     }
 }
 
-static char *find_fqdn(apr_pool_t *a, struct hostent *p)
-{
-    int x;
-
-    if (!strchr(p->h_name, '.')) {
-        if (p->h_aliases) {
-            for (x = 0; p->h_aliases[x]; ++x) {
-                if (strchr(p->h_aliases[x], '.') &&
-                    (!strncasecmp(p->h_aliases[x], p->h_name,
-                                  strlen(p->h_name))))
-                    return apr_pstrdup(a, p->h_aliases[x]);
-            }
-        }
-        return NULL;
-    }
-    return apr_pstrdup(a, (void *) p->h_name);
-}
-
+/*
+ * We must return a FQDN
+ */
 char *ap_get_local_host(apr_pool_t *a)
 {
 #ifndef MAXHOSTNAMELEN
@@ -1976,45 +2002,41 @@ char *ap_get_local_host(apr_pool_t *a)
 #endif
     char str[MAXHOSTNAMELEN + 1];
     char *server_hostname = NULL;
-    struct hostent *p;
+    apr_sockaddr_t *sockaddr;
+    char *hostname;
 
-#ifdef BEOS_R5
-    if (gethostname(str, sizeof(str) - 1) == 0)
-#else
-    if (gethostname(str, sizeof(str) - 1) != 0)
-#endif
-    {
+    if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
         ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
-                     "%s: gethostname() failed to determine ServerName",
+                     "%s: apr_gethostname() failed to determine ServerName",
                      ap_server_argv0);
-    }
-    else 
-    {
+    } else {
         str[sizeof(str) - 1] = '\0';
-        /* TODO: Screaming for APR-ization */
-        if ((!(p = gethostbyname(str))) 
-            || (!(server_hostname = find_fqdn(a, p)))) {
-            /* Recovery - return the default servername by IP: */
-            if (p && p->h_addr_list[0]) {
-                apr_snprintf(str, sizeof(str), "%pA", p->h_addr_list[0]);
+        if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
+            if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
+                (ap_strchr_c(hostname, '.')) ) {
+                server_hostname = apr_pstrdup(a, hostname);
+                return server_hostname;
+            } else if (ap_strchr_c(str, '.')) {
                 server_hostname = apr_pstrdup(a, str);
-                /* We will drop through to report the IP-named server */
+            } else {
+                apr_sockaddr_ip_get(&hostname, sockaddr);
+                server_hostname = apr_pstrdup(a, hostname);
             }
-        }
-        else {
-            /* Since we found a fdqn, return it with no logged message. */
-            return server_hostname;
+        } else {
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
+                         "%s: apr_sockaddr_info_get() failed for %s",
+                         ap_server_argv0, str);
         }
     }
 
-    if (!server_hostname) 
+    if (!server_hostname)
         server_hostname = apr_pstrdup(a, "127.0.0.1");
 
     ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
-                 "%s: Could not determine the server's fully qualified "
+                 "%s: Could not reliably determine the server's fully qualified "
                  "domain name, using %s for ServerName",
                  ap_server_argv0, server_hostname);
-             
+
     return server_hostname;
 }
 
@@ -2032,8 +2054,8 @@ AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
     return decoded;
 }
 
-AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string) 
-{ 
+AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
+{
     char *encoded;
     int l = strlen(string);
 
@@ -2059,10 +2081,9 @@ AP_DECLARE(void) ap_content_type_tolower(char *str)
     if (semi) {
         *semi = '\0';
     }
-    while (*str) {
-        *str = apr_tolower(*str);
-        ++str;
-    }
+
+    ap_str_tolower(str);
+
     if (semi) {
         *semi = ';';
     }
@@ -2118,3 +2139,85 @@ AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
     *outchr = '\0';
     return outstring;
 }
+
+/*
+ * Given a string, append the PID deliminated by delim.
+ * Usually used to create a pid-appended filepath name
+ * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
+ * a macro, to avoid unistd.h dependency
+ */
+AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
+                                    const char *delim)
+{
+    return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
+                        delim, getpid());
+
+}
+
+/**
+ * Parse a given timeout parameter string into an apr_interval_time_t value.
+ * The unit of the time interval is given as postfix string to the numeric
+ * string. Currently the following units are understood:
+ *
+ * ms    : milliseconds
+ * s     : seconds
+ * mi[n] : minutes
+ * h     : hours
+ *
+ * If no unit is contained in the given timeout parameter the default_time_unit
+ * will be used instead.
+ * @param timeout_parameter The string containing the timeout parameter.
+ * @param timeout The timeout value to be returned.
+ * @param default_time_unit The default time unit to use if none is specified
+ * in timeout_parameter.
+ * @return Status value indicating whether the parsing was successful or not.
+ */
+AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
+                                               const char *timeout_parameter,
+                                               apr_interval_time_t *timeout,
+                                               const char *default_time_unit)
+{
+    char *endp;
+    const char *time_str;
+    apr_int64_t tout;
+
+    tout = apr_strtoi64(timeout_parameter, &endp, 10);
+    if (errno) {
+        return errno;
+    }
+    if (!endp || !*endp) {
+        time_str = default_time_unit;
+    }
+    else {
+        time_str = endp;
+    }
+
+    switch (*time_str) {
+        /* Time is in seconds */
+    case 's':
+        *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
+        break;
+    case 'h':
+        /* Time is in hours */
+        *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
+        break;
+    case 'm':
+        switch (*(++time_str)) {
+        /* Time is in milliseconds */
+        case 's':
+            *timeout = (apr_interval_time_t) tout * 1000;
+            break;
+        /* Time is in minutes */
+        case 'i':
+            *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
+            break;
+        default:
+            return APR_EGENERAL;
+        }
+        break;
+    default:
+        return APR_EGENERAL;
+    }
+    return APR_SUCCESS;
+}
+