]> granicus.if.org Git - apache/blobdiff - server/util.c
Introduce ap_(get|set)_core_module_config() functions/macros and use them
[apache] / server / util.c
index 36dfc0f3cdd3209fcc8144bf4d4ca87d40b59a54..86b7843c45faa1fc78b498ba78297791dab782c0 100644 (file)
@@ -1,9 +1,9 @@
-/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
- * applicable.
- *
- * 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
  *
 #if APR_HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#if APR_HAVE_PROCESS_H
+#include <process.h>            /* for getpid() on Win32 */
+#endif
 #if APR_HAVE_NETDB_H
 #include <netdb.h>              /* for gethostbyname() */
 #endif
 
-#define CORE_PRIVATE
-
 #include "ap_config.h"
 #include "apr_base64.h"
 #include "httpd.h"
@@ -51,6 +52,7 @@
 #include "http_log.h"
 #include "http_protocol.h"
 #include "http_config.h"
+#include "http_core.h"
 #include "util_ebcdic.h"
 
 #ifdef HAVE_PWD_H
  */
 #ifdef CASE_BLIND_FILESYSTEM
 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
+#define SLASHES "/\\"
 #else
 #define IS_SLASH(s) (s == '/')
+#define SLASHES "/"
 #endif
 
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
 
 /*
  * Examine a field value (such as a media-/content-type) string and return
@@ -378,16 +386,14 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
     len = 0;
 
     while ((c = *src++) != '\0') {
-        if (c == '&')
-            no = 0;
-        else if (c == '$' && apr_isdigit(*src))
+        if (c == '$' && apr_isdigit(*src))
             no = *src++ - '0';
         else
             no = 10;
 
         if (no > 9) {                /* Ordinary character. */
-            if (c == '\\' && (*src == '$' || *src == '&'))
-                c = *src++;
+            if (c == '\\' && *src)
+                src++;
             len++;
         }
         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
@@ -580,9 +586,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);
 }
 
@@ -613,9 +618,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) {
@@ -643,9 +646,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;
@@ -759,95 +760,6 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     return res;
 }
 
-/* Check a string for any ${ENV} environment variable
- * construct and replace each them by the value of
- * that environment variable, if it exists. If the
- * environment value does not exist, leave the ${ENV}
- * construct alone; it means something else.
- */
-AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
-{
-# 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
@@ -857,30 +769,20 @@ AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
 }
 
+/* we can't use apr_file_* directly because of linking issues on Windows */
 static apr_status_t cfg_close(void *param)
 {
-    apr_file_t *cfp = (apr_file_t *) param;
-    return (apr_file_close(cfp));
+    return apr_file_close(param);
 }
 
-static int cfg_getch(void *param)
+static apr_status_t cfg_getch(char *ch, void *param)
 {
-    char ch;
-    apr_file_t *cfp = (apr_file_t *) param;
-    if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
-        return ch;
-    return (int)EOF;
+    return apr_file_getc(ch, param);
 }
 
-static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t cfg_getstr(void *buf, size_t bufsiz, void *param)
 {
-    apr_file_t *cfp = (apr_file_t *) param;
-    apr_status_t rv;
-    rv = apr_file_gets(buf, bufsiz, cfp);
-    if (rv == APR_SUCCESS) {
-        return buf;
-    }
-    return NULL;
+    return apr_file_gets(buf, bufsiz, param);
 }
 
 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
@@ -955,9 +857,9 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
     new_cfg = apr_palloc(p, sizeof(*new_cfg));
     new_cfg->param = file;
     new_cfg->name = apr_pstrdup(p, name);
-    new_cfg->getch = (int (*)(void *)) cfg_getch;
-    new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
-    new_cfg->close = (int (*)(void *)) cfg_close;
+    new_cfg->getch = cfg_getch;
+    new_cfg->getstr = cfg_getstr;
+    new_cfg->close = cfg_close;
     new_cfg->line_number = 0;
     *ret_cfg = new_cfg;
     return APR_SUCCESS;
@@ -965,170 +867,156 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 
 
 /* Allocate a ap_configfile_t handle with user defined functions and params */
-AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
-                       const char *descr,
-                       void *param,
-                       int(*getch)(void *param),
-                       void *(*getstr) (void *buf, size_t bufsiz, void *param),
-                       int(*close_func)(void *param))
+AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
+            apr_pool_t *p, const char *descr, void *param,
+            apr_status_t (*getc_func) (char *ch, void *param),
+            apr_status_t (*gets_func) (void *buf, size_t bufsize, void *param),
+            apr_status_t (*close_func) (void *param))
 {
     ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
-#ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
-                 "Opening config handler %s", descr);
-#endif
     new_cfg->param = param;
     new_cfg->name = descr;
-    new_cfg->getch = getch;
-    new_cfg->getstr = getstr;
+    new_cfg->getch = getc_func;
+    new_cfg->getstr = gets_func;
     new_cfg->close = close_func;
     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)
+AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
 {
-    register int ch = cfp->getch(cfp->param);
-    if (ch == LF)
+    apr_status_t rc = cfp->getch(ch, cfp->param);
+    if (rc == APR_SUCCESS && *ch == LF)
         ++cfp->line_number;
-    return ch;
+    return rc;
+}
+
+AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
+                                          apr_status_t rc)
+{
+    char buf[MAX_STRING_LEN];
+    if (rc == APR_SUCCESS)
+        return NULL;
+    return apr_psprintf(p, "Error reading %s at line %d: %s",
+                        cfp->name, cfp->line_number,
+                        rc == APR_ENOSPC ? "Line too long"
+                                         : apr_strerror(rc, buf, sizeof(buf)));
 }
 
 /* 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)
+AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
 {
+    apr_status_t rc;
+    char *src, *dst;
     /* If a "get string" function is defined, use it */
     if (cfp->getstr != NULL) {
-        char *src, *dst;
         char *cp;
         char *cbuf = buf;
         size_t cbufsize = bufsize;
 
         while (1) {
             ++cfp->line_number;
-            if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
-                return 1;
+            rc = cfp->getstr(cbuf, cbufsize, cfp->param);
+            if (rc == APR_EOF) {
+                if (cbuf != buf) {
+                   *cbuf = '\0';
+                    break;
+               }
+                else {
+                    return APR_EOF;
+               }
+            }
+            if (rc != APR_SUCCESS) {
+                return rc;
+            }
 
             /*
              *  check for line continuation,
              *  i.e. match [^\\]\\[\r]\n only
              */
             cp = cbuf;
-            while (cp < cbuf+cbufsize && *cp != '\0')
-                cp++;
+            cp += strlen(cp);
             if (cp > cbuf && cp[-1] == LF) {
                 cp--;
                 if (cp > cbuf && cp[-1] == CR)
                     cp--;
                 if (cp > cbuf && cp[-1] == '\\') {
                     cp--;
-                    if (!(cp > cbuf && cp[-1] == '\\')) {
-                        /*
-                         * line continuation requested -
-                         * then remove backslash and continue
-                         */
-                        cbufsize -= (cp-cbuf);
-                        cbuf = cp;
-                        continue;
-                    }
-                    else {
-                        /*
-                         * no real continuation because escaped -
-                         * then just remove escape character
-                         */
-                        for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
-                            cp[0] = cp[1];
-                    }
+                    /*
+                     * line continuation requested -
+                     * then remove backslash and continue
+                     */
+                    cbufsize -= (cp-cbuf);
+                    cbuf = cp;
+                    continue;
                 }
             }
+            else if (cp - buf >= bufsize - 1) {
+                return APR_ENOSPC;
+            }
             break;
         }
-
-        /*
-         * Leading and trailing white space is eliminated completely
-         */
-        src = buf;
-        while (apr_isspace(*src))
-            ++src;
-        /* blast trailing whitespace */
-        dst = &src[strlen(src)];
-        while (--dst >= src && apr_isspace(*dst))
-            *dst = '\0';
-        /* Zap leading whitespace by shifting */
-        if (src != buf)
-            for (dst = buf; (*dst++ = *src++) != '\0'; )
-                ;
-
-#ifdef DEBUG_CFG_LINES
-        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
-#endif
-        return 0;
     } else {
         /* No "get string" function defined; read character by character */
-        register int c;
-        register size_t i = 0;
-
-        buf[0] = '\0';
-        /* skip leading whitespace */
-        do {
-            c = cfp->getch(cfp->param);
-        } while (c == '\t' || c == ' ');
-
-        if (c == EOF)
-            return 1;
+        size_t i = 0;
 
-        if(bufsize < 2) {
+        if (bufsize < 2) {
             /* too small, assume caller is crazy */
-            return 1;
+            return APR_EINVAL;
         }
+        buf[0] = '\0';
 
         while (1) {
-            if ((c == '\t') || (c == ' ')) {
-                buf[i++] = ' ';
-                while ((c == '\t') || (c == ' '))
-                    c = cfp->getch(cfp->param);
-            }
-            if (c == CR) {
-                /* silently ignore CR (_assume_ that a LF follows) */
-                c = cfp->getch(cfp->param);
+            char c;
+            rc = cfp->getch(&c, cfp->param);
+            if (rc == APR_EOF) {
+                if (i > 0)
+                    break;
+                else
+                    return APR_EOF;
             }
+            if (rc != APR_SUCCESS)
+                return rc;
             if (c == LF) {
-                /* increase line number and return on LF */
                 ++cfp->line_number;
-            }
-            if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
-                /*
-                 *  check for line continuation
-                 */
+                /* check for line continuation */
                 if (i > 0 && buf[i-1] == '\\') {
                     i--;
-                    if (!(i > 0 && buf[i-1] == '\\')) {
-                        /* line is continued */
-                        c = cfp->getch(cfp->param);
-                        continue;
-                    }
-                    /* else nothing needs be done because
-                     * then the backslash is escaped and
-                     * we just strip to a single one
-                     */
+                    continue;
                 }
-                /* blast trailing whitespace */
-                while (i > 0 && apr_isspace(buf[i - 1]))
-                    --i;
-                buf[i] = '\0';
-#ifdef DEBUG_CFG_LINES
-                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
-                             "Read config: %s", buf);
-#endif
-                return 0;
+                else {
+                    break;
+                }
+            }
+            else if (i >= bufsize - 2) {
+                return APR_ENOSPC;
             }
             buf[i] = c;
             ++i;
-            c = cfp->getch(cfp->param);
         }
+       buf[i] = '\0';
     }
+
+    /*
+     * Leading and trailing white space is eliminated completely
+     */
+    src = buf;
+    while (apr_isspace(*src))
+        ++src;
+    /* blast trailing whitespace */
+    dst = &src[strlen(src)];
+    while (--dst >= src && apr_isspace(*dst))
+        *dst = '\0';
+    /* Zap leading whitespace by shifting */
+    if (src != buf)
+        memmove(buf, src, dst - src + 2);
+
+#ifdef DEBUG_CFG_LINES
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Read config: '%s'", buf);
+#endif
+    return APR_SUCCESS;
 }
 
 /* Size an HTTP header field list item, as separated by a comma.
@@ -1542,54 +1430,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;
@@ -1614,8 +1463,16 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
             else {
                 char decoded;
                 decoded = x2c(y + 1);
-                if (decoded == '\0') {
+                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;
@@ -1635,6 +1492,38 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
         return OK;
     }
 }
+AP_DECLARE(int) ap_unescape_url(char *url)
+{
+    /* Traditional */
+    return unescape_url(url, SLASHES, NULL);
+}
+AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
+{
+    /* AllowEncodedSlashes (corrected) */
+    if (decode_slashes) {
+        /* no chars reserved */
+        return unescape_url(url, NULL, NULL);
+    } else {
+        /* reserve (do not decode) encoded slashes */
+        return unescape_url(url, NULL, SLASHES);
+    }
+}
+#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)
@@ -1647,6 +1536,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
@@ -1686,9 +1580,8 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
  * 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;
@@ -1706,6 +1599,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);
@@ -1737,7 +1635,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;
@@ -1750,6 +1648,8 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             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);
@@ -1772,13 +1672,17 @@ AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s)
             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;
@@ -2074,10 +1978,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 = ';';
     }
@@ -2147,3 +2050,344 @@ AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *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;
+}
+
+/**
+ * Determine if a request has a request body or not.
+ *
+ * @param r the request_rec of the request
+ * @return truth value
+ */
+AP_DECLARE(int) ap_request_has_body(request_rec *r)
+{
+    apr_off_t cl;
+    char *estr;
+    const char *cls;
+    int has_body;
+
+    has_body = (!r->header_only
+                && (r->kept_body
+                    || apr_table_get(r->headers_in, "Transfer-Encoding")
+                    || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
+                        && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
+                        && (!*estr)
+                        && (cl > 0) )
+                    )
+                );
+    return has_body;
+}
+
+AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
+{
+    void **ptr = (void **)data_;
+    *ptr = NULL;
+    return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
+
+    for ( ; *src; src++, dest++)
+    {
+        if (!apr_isprint(*src))
+            *dest = 'x';
+        else if (!apr_isalnum(*src))
+            *dest = '_';
+        else
+            *dest = (char)*src;
+    }
+    *dest = '\0';
+    return APR_SUCCESS;
+
+}
+
+AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
+                                        const char **dest)
+{
+    char *new = apr_palloc(p, strlen(src)+1);
+    if (!new)
+        return APR_ENOMEM;
+    *dest = new;
+    return ap_str2_alnum(src, new);
+}
+
+/**
+ * Read the body and parse any form found, which must be of the
+ * type application/x-www-form-urlencoded.
+ *
+ * Name/value pairs are returned in an array, with the names as
+ * strings with a maximum length of HUGE_STRING_LEN, and the
+ * values as bucket brigades. This allows values to be arbitrarily
+ * large.
+ *
+ * All url-encoding is removed from both the names and the values
+ * on the fly. The names are interpreted as strings, while the
+ * values are interpreted as blocks of binary data, that may
+ * contain the 0 character.
+ *
+ * In order to ensure that resource limits are not exceeded, a
+ * maximum size must be provided. If the sum of the lengths of
+ * the names and the values exceed this size, this function
+ * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
+ *
+ * An optional number of parameters can be provided, if the number
+ * of parameters provided exceeds this amount, this function will
+ * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
+ * no limit is imposed, and the number of parameters is in turn
+ * constrained by the size parameter above.
+ *
+ * This function honours any kept_body configuration, and the
+ * original raw request body will be saved to the kept_body brigade
+ * if so configured, just as ap_discard_request_body does.
+ *
+ * NOTE: File upload is not yet supported, but can be without change
+ * to the function call.
+ */
+
+/* form parsing stuff */
+typedef enum {
+    FORM_NORMAL,
+    FORM_AMP,
+    FORM_NAME,
+    FORM_VALUE,
+    FORM_PERCENTA,
+    FORM_PERCENTB,
+    FORM_ABORT
+} ap_form_type_t;
+
+AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
+                                   apr_array_header_t **ptr,
+                                   apr_size_t num, apr_size_t usize)
+{
+    apr_bucket_brigade *bb = NULL;
+    int seen_eos = 0;
+    char buffer[HUGE_STRING_LEN + 1];
+    const char *ct;
+    apr_size_t offset = 0;
+    apr_ssize_t size;
+    ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
+    ap_form_pair_t *pair = NULL;
+    apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
+
+    char hi = 0;
+    char low = 0;
+
+    *ptr = pairs;
+
+    /* sanity check - we only support forms for now */
+    ct = apr_table_get(r->headers_in, "Content-Type");
+    if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
+        return ap_discard_request_body(r);
+    }
+
+    if (usize > APR_SIZE_MAX >> 1)
+        size = APR_SIZE_MAX >> 1;
+    else
+        size = usize;
+
+    if (!f) {
+        f = r->input_filters;
+    }
+
+    bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    do {
+        apr_bucket *bucket = NULL, *last = NULL;
+
+        int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
+                                APR_BLOCK_READ, HUGE_STRING_LEN);
+        if (rv != APR_SUCCESS) {
+            apr_brigade_destroy(bb);
+            return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST;
+        }
+
+        for (bucket = APR_BRIGADE_FIRST(bb);
+             bucket != APR_BRIGADE_SENTINEL(bb);
+             last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
+            const char *data;
+            apr_size_t len, slide;
+
+            if (last) {
+                apr_bucket_delete(last);
+            }
+            if (APR_BUCKET_IS_EOS(bucket)) {
+                seen_eos = 1;
+                break;
+            }
+            if (bucket->length == 0) {
+                continue;
+            }
+
+            rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+            if (rv != APR_SUCCESS) {
+                apr_brigade_destroy(bb);
+                return HTTP_BAD_REQUEST;
+            }
+
+            slide = len;
+            while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
+                char c = *data++;
+                if ('+' == c) {
+                    c = ' ';
+                }
+                else if ('&' == c) {
+                    state = FORM_AMP;
+                }
+                if ('%' == c) {
+                    percent = FORM_PERCENTA;
+                    continue;
+                }
+                if (FORM_PERCENTA == percent) {
+                    if (c >= 'a') {
+                        hi = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        hi = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        hi = c - '0';
+                    }
+                    hi = hi << 4;
+                    percent = FORM_PERCENTB;
+                    continue;
+                }
+                if (FORM_PERCENTB == percent) {
+                    if (c >= 'a') {
+                        low = c - 'a' + 10;
+                    }
+                    else if (c >= 'A') {
+                        low = c - 'A' + 10;
+                    }
+                    else if (c >= '0') {
+                        low = c - '0';
+                    }
+                    c = low | hi;
+                    percent = FORM_NORMAL;
+                }
+                switch (state) {
+                    case FORM_AMP:
+                        if (pair) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                        }
+                        state = FORM_NAME;
+                        pair = NULL;
+                        offset = 0;
+                        num--;
+                        break;
+                    case FORM_NAME:
+                        if (offset < HUGE_STRING_LEN) {
+                            if ('=' == c) {
+                                buffer[offset] = 0;
+                                offset = 0;
+                                pair = (ap_form_pair_t *) apr_array_push(pairs);
+                                pair->name = apr_pstrdup(r->pool, buffer);
+                                pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+                                state = FORM_VALUE;
+                            }
+                            else {
+                                buffer[offset++] = c;
+                                size--;
+                            }
+                        }
+                        else {
+                            state = FORM_ABORT;
+                        }
+                        break;
+                    case FORM_VALUE:
+                        if (offset >= HUGE_STRING_LEN) {
+                            const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+                            apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+                            APR_BRIGADE_INSERT_TAIL(pair->value, b);
+                            offset = 0;
+                        }
+                        buffer[offset++] = c;
+                        size--;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+        }
+
+        apr_brigade_cleanup(bb);
+    } while (!seen_eos);
+
+    if (FORM_ABORT == state || size < 0 || num == 0) {
+        return HTTP_REQUEST_ENTITY_TOO_LARGE;
+    }
+    else if (FORM_VALUE == state && pair && offset > 0) {
+        const char *tmp = apr_pmemdup(r->pool, buffer, offset);
+        apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
+        APR_BRIGADE_INSERT_TAIL(pair->value, b);
+    }
+
+    return OK;
+
+}