]> granicus.if.org Git - apache/blobdiff - server/util.c
Wildcard name-based vhosts printed twice in apachectl -S
[apache] / server / util.c
index 9e20d37bba3029829c9a41489c856785ff95d9af..e0ba5c28a39a5c33cdfd9f4763ac47b4cf77aabd 100644 (file)
 #ifdef HAVE_GRP_H
 #include <grp.h>
 #endif
+#ifdef HAVE_SYS_LOADAVG_H
+#include <sys/loadavg.h>
+#endif
+
+#include "ap_mpm.h"
 
 /* A bunch of functions in util.c scan strings looking for certain characters.
  * To make that more efficient we encode a lookup table.  The test_char_table
@@ -273,8 +278,10 @@ AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
                                      int cflags)
 {
     ap_regex_t *preg = apr_palloc(p, sizeof *preg);
-
-    if (ap_regcomp(preg, pattern, cflags)) {
+    int err = ap_regcomp(preg, pattern, cflags);
+    if (err) {
+        if (err == AP_REG_ESPACE)
+            ap_abort_on_oom();
         return NULL;
     }
 
@@ -358,67 +365,92 @@ AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
  * passed ap_regexec(). pmatch should not be greater than the maximum number
  * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
  *
+ * nmatch must be <=AP_MAX_REG_MATCH (10).
+ *
  * input should be the string with the $-expressions, source should be the
  * string that was matched against.
  *
- * It returns the substituted string, or NULL on error.
+ * It returns the substituted string, or NULL if a vbuf is used.
+ * On errors, returns the orig string.
  *
  * Parts of this code are based on Henry Spencer's regsub(), from his
  * AT&T V8 regexp package.
  */
 
-AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
-                              const char *source, size_t nmatch,
-                              ap_regmatch_t pmatch[])
+static apr_status_t regsub_core(apr_pool_t *p, char **result,
+                                struct ap_varbuf *vb, const char *input,
+                                const char *source, apr_size_t nmatch,
+                                ap_regmatch_t pmatch[], apr_size_t maxlen)
 {
     const char *src = input;
-    char *dest, *dst;
+    char *dst;
     char c;
-    size_t no;
-    int len;
-
-    if (!source)
-        return NULL;
-    if (!nmatch)
-        return apr_pstrdup(p, src);
+    apr_size_t no;
+    apr_size_t len = 0;
+
+    AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result));
+    if (!source || nmatch>AP_MAX_REG_MATCH)
+        return APR_EINVAL;
+    if (!nmatch) {
+        len = strlen(src);
+        if (maxlen > 0 && len >= maxlen)
+            return APR_ENOMEM;
+        if (!vb) {
+            *result = apr_pstrmemdup(p, src, len);
+            return APR_SUCCESS;
+        }
+        else {
+            ap_varbuf_strmemcat(vb, src, len);
+            return APR_SUCCESS;
+        }
+    }
 
     /* First pass, find the size */
-
-    len = 0;
-
     while ((c = *src++) != '\0') {
         if (c == '$' && apr_isdigit(*src))
             no = *src++ - '0';
         else
-            no = 10;
+            no = AP_MAX_REG_MATCH;
 
-        if (no > 9) {                /* Ordinary character. */
+        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
             if (c == '\\' && *src)
                 src++;
             len++;
         }
         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
+            if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so)
+                return APR_ENOMEM;
             len += pmatch[no].rm_eo - pmatch[no].rm_so;
         }
 
     }
 
-    dest = dst = apr_pcalloc(p, len + 1);
+    if (len >= maxlen && maxlen > 0)
+        return APR_ENOMEM;
+
+    if (!vb) {
+        *result = dst = apr_palloc(p, len + 1);
+    }
+    else {
+        if (vb->strlen == AP_VARBUF_UNKNOWN)
+            vb->strlen = strlen(vb->buf);
+        ap_varbuf_grow(vb, vb->strlen + len);
+        dst = vb->buf + vb->strlen;
+        vb->strlen += len;
+    }
 
     /* Now actually fill in the string */
 
     src = input;
 
     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;
+            no = AP_MAX_REG_MATCH;
 
-        if (no > 9) {                /* Ordinary character. */
-            if (c == '\\' && (*src == '$' || *src == '&'))
+        if (no >= AP_MAX_REG_MATCH) {  /* Ordinary character. */
+            if (c == '\\' && *src)
                 c = *src++;
             *dst++ = c;
         }
@@ -431,7 +463,34 @@ AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
     }
     *dst = '\0';
 
-    return dest;
+    return APR_SUCCESS;
+}
+
+#ifndef AP_PREGSUB_MAXLEN
+#define AP_PREGSUB_MAXLEN   (HUGE_STRING_LEN * 8)
+#endif
+AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
+                              const char *source, apr_size_t nmatch,
+                              ap_regmatch_t pmatch[])
+{
+    char *result;
+    apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch,
+                                  pmatch, AP_PREGSUB_MAXLEN);
+    if (rc != APR_SUCCESS)
+        result = NULL;
+    return result;
+}
+
+AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
+                                       const char *input, const char *source,
+                                       apr_size_t nmatch, ap_regmatch_t pmatch[],
+                                       apr_size_t maxlen)
+{
+    apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch,
+                                  pmatch, maxlen);
+    if (rc != APR_SUCCESS)
+        *result = NULL;
+    return rc;
 }
 
 /*
@@ -671,7 +730,7 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
     char *res;
 
     if (!pos) {
-        size_t len = strlen(*line);
+        apr_size_t len = strlen(*line);
         res = apr_pstrmemdup(atrans, *line, len);
         *line += len;
         return res;
@@ -693,7 +752,7 @@ AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
 static char *substring_conf(apr_pool_t *p, const char *start, int len,
                             char quote)
 {
-    char *result = apr_palloc(p, len + 2);
+    char *result = apr_palloc(p, len + 1);
     char *resp = result;
     int i;
 
@@ -724,7 +783,7 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
     char *res;
     char quote;
 
-    while (*str && apr_isspace(*str))
+    while (apr_isspace(*str))
         ++str;
 
     if (!*str) {
@@ -756,7 +815,7 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
         res = substring_conf(p, str, strend - str, 0);
     }
 
-    while (*strend && apr_isspace(*strend))
+    while (apr_isspace(*strend))
         ++strend;
     *line = strend;
     return res;
@@ -765,7 +824,7 @@ AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
 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, APLOGNO(00551)
         "Done with config file %s", cfp->name);
 #endif
     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
@@ -782,7 +841,7 @@ static apr_status_t cfg_getch(char *ch, void *param)
     return apr_file_getc(ch, param);
 }
 
-static apr_status_t cfg_getstr(void *buf, size_t bufsiz, void *param)
+static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
 {
     return apr_file_gets(buf, bufsiz, param);
 }
@@ -800,7 +859,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 #endif
 
     if (name == NULL) {
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
                "Internal error: pcfg_openfile() called with NULL filename");
         return APR_EBADF;
     }
@@ -808,7 +867,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
     status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
                            APR_OS_DEFAULT, p);
 #ifdef DEBUG
-    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
                 "Opening config file %s (%s)",
                 name, (status != APR_SUCCESS) ?
                 apr_strerror(status, buf, sizeof(buf)) : "successful");
@@ -826,7 +885,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 #else
         strcmp(name, "/dev/null") != 0) {
 #endif /* WIN32 || OS2 */
-        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+        ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
                      "Access to file %s denied by server: not a regular file",
                      name);
         apr_file_close(file);
@@ -872,7 +931,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
 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 (*gets_func) (void *buf, apr_size_t bufsize, void *param),
             apr_status_t (*close_func) (void *param))
 {
     ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
@@ -908,7 +967,7 @@ AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
 
 /* 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 */
-static apr_status_t ap_cfg_getline_core(char *buf, size_t bufsize,
+static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize,
                                         ap_configfile_t *cfp)
 {
     apr_status_t rc;
@@ -916,7 +975,7 @@ static apr_status_t ap_cfg_getline_core(char *buf, size_t bufsize,
     if (cfp->getstr != NULL) {
         char *cp;
         char *cbuf = buf;
-        size_t cbufsize = bufsize;
+        apr_size_t cbufsize = bufsize;
 
         while (1) {
             ++cfp->line_number;
@@ -962,7 +1021,7 @@ static apr_status_t ap_cfg_getline_core(char *buf, size_t bufsize,
         }
     } else {
         /* No "get string" function defined; read character by character */
-        size_t i = 0;
+        apr_size_t i = 0;
 
         if (bufsize < 2) {
             /* too small, assume caller is crazy */
@@ -1020,14 +1079,15 @@ static int cfg_trim_line(char *buf)
     if (start != buf)
         memmove(buf, start, end - start + 2);
 #ifdef DEBUG_CFG_LINES
-    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "Read config: '%s'", buf);
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
 #endif
     return end - start + 1;
 }
 
 /* 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(apr_status_t) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
+AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize,
+                                        ap_configfile_t *cfp)
 {
     apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp);
     if (rc == APR_SUCCESS)
@@ -1040,11 +1100,22 @@ AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
                                                apr_size_t max_len)
 {
     apr_status_t rc;
+    apr_size_t new_len;
     vb->strlen = 0;
     *vb->buf = '\0';
 
+    if (vb->strlen == AP_VARBUF_UNKNOWN)
+        vb->strlen = strlen(vb->buf);
+    if (vb->avail - vb->strlen < 3) {
+        new_len = vb->avail * 2;
+        if (new_len > max_len)
+            new_len = max_len;
+        else if (new_len < 3)
+            new_len = 3;
+        ap_varbuf_grow(vb, new_len);
+    }
+
     for (;;) {
-        apr_size_t new_len;
         rc = ap_cfg_getline_core(vb->buf + vb->strlen, vb->avail - vb->strlen, cfp);
         if (rc == APR_ENOSPC || rc == APR_SUCCESS)
             vb->strlen += strlen(vb->buf + vb->strlen);
@@ -1058,6 +1129,8 @@ AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
         ap_varbuf_grow(vb, new_len);
         --cfp->line_number;
     }
+    if (vb->strlen > max_len)
+        return APR_ENOSPC;
     if (rc == APR_SUCCESS)
         vb->strlen = cfg_trim_line(vb->buf);
     return rc;
@@ -1213,27 +1286,58 @@ AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
     return token;
 }
 
+typedef enum ap_etag_e {
+    AP_ETAG_NONE,
+    AP_ETAG_WEAK,
+    AP_ETAG_STRONG
+} ap_etag_e;
+
 /* Find an item in canonical form (lowercase, no extra spaces) within
  * an HTTP field value list.  Returns 1 if found, 0 if not found.
  * This would be much more efficient if we stored header fields as
  * an array of list items as they are received instead of a plain string.
  */
-AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
-                                  const char *tok)
+static int find_list_item(apr_pool_t *p, const char *line,
+                                  const char *tok, ap_etag_e type)
 {
     const unsigned char *pos;
     const unsigned char *ptr = (const unsigned char *)line;
     int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
 
-    if (!line || !tok)
+    if (!line || !tok) {
+        return 0;
+    }
+    if (type == AP_ETAG_STRONG && *tok != '\"') {
         return 0;
+    }
+    if (type == AP_ETAG_WEAK) {
+        if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') {
+            tok += 2;
+        }
+        else if (*tok != '\"') {
+            return 0;
+        }
+    }
 
     do {  /* loop for each item in line's list */
 
         /* Find first non-comma, non-whitespace byte */
-
-        while (*ptr == ',' || apr_isspace(*ptr))
+        while (*ptr == ',' || apr_isspace(*ptr)) {
             ++ptr;
+        }
+
+        /* Account for strong or weak Etags, depending on our search */
+        if (type == AP_ETAG_STRONG && *ptr != '\"') {
+            break;
+        }
+        if (type == AP_ETAG_WEAK) {
+            if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
+                ptr += 2;
+            }
+            else if (*ptr != '\"') {
+                break;
+            }
+        }
 
         if (*ptr)
             good = 1;  /* until proven otherwise for this item */
@@ -1301,7 +1405,8 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
                                if (in_com || in_qstr)
                                    good = good && (*pos++ == *ptr);
                                else
-                                   good = good && (*pos++ == apr_tolower(*ptr));
+                                   good = good
+                                       && (apr_tolower(*pos++) == apr_tolower(*ptr));
                                addspace = 0;
                                break;
                 }
@@ -1315,6 +1420,34 @@ AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
     return good;
 }
 
+/* Find an item in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ * This would be much more efficient if we stored header fields as
+ * an array of list items as they are received instead of a plain string.
+ */
+AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
+                                  const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_NONE);
+}
+
+/* Find a strong Etag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line,
+                                    const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_STRONG);
+}
+
+/* Find a weak ETag in canonical form (lowercase, no extra spaces) within
+ * an HTTP field value list.  Returns 1 if found, 0 if not found.
+ */
+AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line,
+                                  const char *tok)
+{
+    return find_list_item(p, line, tok, AP_ETAG_WEAK);
+}
 
 /* Retrieve a token, spacing over it and returning a pointer to
  * the first non-white byte afterwards.  Note that these tokens
@@ -1332,7 +1465,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
 
     /* Find first non-white byte */
 
-    while (*ptr && apr_isspace(*ptr))
+    while (apr_isspace(*ptr))
         ++ptr;
 
     tok_start = ptr;
@@ -1354,7 +1487,7 @@ AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
 
     /* Advance accept_line pointer to the next non-white byte */
 
-    while (*ptr && apr_isspace(*ptr))
+    while (apr_isspace(*ptr))
         ++ptr;
 
     *accept_line = ptr;
@@ -1569,6 +1702,23 @@ AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
 }
 #endif
 
+AP_DECLARE(int) ap_unescape_urlencoded(char *query)
+{
+    char *slider;
+
+    /* replace plus with a space */
+    if (query) {
+        for (slider = query; *slider; slider++) {
+            if (*slider == '+') {
+                *slider = ' ';
+            }
+        }
+    }
+
+    /* unescape everything else */
+    return unescape_url(query, NULL, NULL);
+}
+
 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
                                        apr_port_t port, const request_rec *r)
 {
@@ -1677,6 +1827,33 @@ AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partia
     return copy;
 }
 
+AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer)
+{
+    const unsigned char *s = (const unsigned char *)buffer;
+    unsigned char *d = (unsigned char *)copy;
+    unsigned c;
+
+    while ((c = *s)) {
+        if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
+            d = c2x(c, '%', d);
+        }
+        else if (c == ' ') {
+            *d++ = '+';
+        }
+        else {
+            *d++ = c;
+        }
+        ++s;
+    }
+    *d = '\0';
+    return copy;
+}
+
+AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
+{
+    return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer);
+}
+
 /* ap_escape_uri is now a macro for os_escape_path */
 
 AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
@@ -1732,16 +1909,33 @@ AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
     char *ret;
     unsigned char *d;
     const unsigned char *s;
+    apr_size_t length, escapes = 0;
 
     if (!str) {
         return NULL;
     }
 
-    ret = apr_palloc(p, 4 * strlen(str) + 1); /* Be safe */
+    /* Compute how many characters need to be escaped */
+    s = (const unsigned char *)str;
+    for (; *s; ++s) {
+        if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
+            escapes++;
+        }
+    }
+    
+    /* Compute the length of the input string, including NULL */
+    length = s - (const unsigned char *)str + 1;
+    
+    /* Fast path: nothing to escape */
+    if (escapes == 0) {
+        return apr_pmemdup(p, str, length);
+    }
+    
+    /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
+    ret = apr_palloc(p, length + 3 * escapes);
     d = (unsigned char *)ret;
     s = (const unsigned char *)str;
     for (; *s; ++s) {
-
         if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
             *d++ = '\\';
             switch(*s) {
@@ -1841,6 +2035,18 @@ AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
     return (d - (unsigned char *)dest);
 }
 
+AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
+{
+    const unsigned char *in = src;
+    apr_size_t i;
+
+    for (i = 0; i < srclen; i++) {
+        *dest++ = c2x_table[in[i] >> 4];
+        *dest++ = c2x_table[in[i] & 0xf];
+    }
+    *dest = '\0';
+}
+
 AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
 {
     apr_finfo_t finfo;
@@ -1955,7 +2161,7 @@ char *ap_get_local_host(apr_pool_t *a)
     char *hostname;
 
     if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
-        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
+        ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556)
                      "%s: apr_gethostname() failed to determine ServerName",
                      ap_server_argv0);
     } else {
@@ -1972,7 +2178,7 @@ char *ap_get_local_host(apr_pool_t *a)
                 server_hostname = apr_pstrdup(a, hostname);
             }
         } else {
-            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
+            ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557)
                          "%s: apr_sockaddr_info_get() failed for %s",
                          ap_server_argv0, str);
         }
@@ -1981,9 +2187,10 @@ char *ap_get_local_host(apr_pool_t *a)
     if (!server_hostname)
         server_hostname = apr_pstrdup(a, "127.0.0.1");
 
-    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
+    ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
                  "%s: Could not reliably determine the server's fully qualified "
-                 "domain name, using %s for ServerName",
+                 "domain name, using %s. Set the 'ServerName' directive globally "
+                 "to suppress this message",
                  ap_server_argv0, server_hostname);
 
     return server_hostname;
@@ -2293,7 +2500,7 @@ AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
 
     /* 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)) {
+    if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
         return ap_discard_request_body(r);
     }
 
@@ -2461,10 +2668,13 @@ static apr_status_t varbuf_cleanup(void *info_)
     return APR_SUCCESS;
 }
 
+const char nul = '\0';
+static char * const varbuf_empty = (char *)&nul;
+
 AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
                                 apr_size_t init_size)
 {
-    vb->buf = NULL;
+    vb->buf = varbuf_empty;
     vb->avail = 0;
     vb->strlen = AP_VARBUF_UNKNOWN;
     vb->pool = p;
@@ -2480,6 +2690,8 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
     struct ap_varbuf_info *new_info;
     char *new;
 
+    AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN || vb->avail >= vb->strlen);
+
     if (new_len <= vb->avail)
         return;
 
@@ -2498,8 +2710,9 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
     if (new_len <= VARBUF_SMALL_SIZE) {
         new_len = APR_ALIGN_DEFAULT(new_len);
         new = apr_palloc(vb->pool, new_len);
-        if (vb->buf && vb->strlen > 0) {
-            AP_DEBUG_ASSERT(vb->avail > 0);
+        if (vb->avail && vb->strlen != 0) {
+            AP_DEBUG_ASSERT(vb->buf != NULL);
+            AP_DEBUG_ASSERT(vb->buf != varbuf_empty);
             if (new == vb->buf + vb->avail + 1) {
                 /* We are lucky: the new memory lies directly after our old
                  * buffer, we can now use both.
@@ -2509,7 +2722,7 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
             }
             else {
                 /* copy up to vb->strlen + 1 bytes */
-                memcpy(new, vb->buf, vb->strlen > vb->avail ?
+                memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
                                      vb->avail + 1 : vb->strlen + 1);
             }
         }
@@ -2541,9 +2754,9 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
     AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len);
     new_len = new_node->endp - new_node->first_avail;
 
-    if (vb->buf && vb->strlen > 0)
-        memcpy(new, vb->buf, vb->strlen > vb->avail ?
-                             vb->avail + 1: vb->strlen + 1);
+    if (vb->avail && vb->strlen != 0)
+        memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
+                             vb->avail + 1 : vb->strlen + 1);
     else
         *new = '\0';
     if (vb->info)
@@ -2558,16 +2771,17 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
 AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str,
                                      int len)
 {
-    AP_DEBUG_ASSERT(len == strlen(str));
+    if (len == 0)
+        return;
     if (!vb->avail) {
         ap_varbuf_grow(vb, len);
-        memcpy(vb->buf, str, len + 1);
+        memcpy(vb->buf, str, len);
+        vb->buf[len] = '\0';
+        vb->strlen = len;
         return;
     }
-    if (vb->strlen > vb->avail) {
-        AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN);
+    if (vb->strlen == AP_VARBUF_UNKNOWN)
         vb->strlen = strlen(vb->buf);
-    }
     ap_varbuf_grow(vb, vb->strlen + len);
     memcpy(vb->buf + vb->strlen, str, len);
     vb->strlen += len;
@@ -2582,3 +2796,212 @@ AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb)
     }
     vb->buf = NULL;
 }
+
+AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf,
+                                  const char *prepend, apr_size_t prepend_len,
+                                  const char *append, apr_size_t append_len,
+                                  apr_size_t *new_len)
+{
+    apr_size_t i = 0;
+    struct iovec vec[3];
+
+    if (prepend) {
+        vec[i].iov_base = (void *)prepend;
+        vec[i].iov_len = prepend_len;
+        i++;
+    }
+    if (buf->avail && buf->strlen) {
+        if (buf->strlen == AP_VARBUF_UNKNOWN)
+            buf->strlen = strlen(buf->buf);
+        vec[i].iov_base = (void *)buf->buf;
+        vec[i].iov_len = buf->strlen;
+        i++;
+    }
+    if (append) {
+        vec[i].iov_base = (void *)append;
+        vec[i].iov_len = append_len;
+        i++;
+    }
+    if (i)
+        return apr_pstrcatv(p, vec, i, new_len);
+
+    if (new_len)
+        *new_len = 0;
+    return "";
+}
+
+AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
+                                          const char *input,
+                                          const char *source,
+                                          apr_size_t nmatch,
+                                          ap_regmatch_t pmatch[],
+                                          apr_size_t maxlen)
+{
+    return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen);
+}
+
+static const char * const oom_message = "[crit] Memory allocation failed, "
+                                        "aborting process." APR_EOL_STR;
+
+AP_DECLARE(void) ap_abort_on_oom()
+{
+    int written, count = strlen(oom_message);
+    const char *buf = oom_message;
+    do {
+        written = write(STDERR_FILENO, buf, count);
+        if (written == count)
+            break;
+        if (written > 0) {
+            buf += written;
+            count -= written;
+        }
+    } while (written >= 0 || errno == EINTR);
+    abort();
+}
+
+AP_DECLARE(void *) ap_malloc(size_t size)
+{
+    void *p = malloc(size);
+    if (p == NULL && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size)
+{
+    void *p = calloc(nelem, size);
+    if (p == NULL && nelem != 0 && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
+{
+    void *p = realloc(ptr, size);
+    if (p == NULL && size != 0)
+        ap_abort_on_oom();
+    return p;
+}
+
+AP_DECLARE(void) ap_get_sload(ap_sload_t *ld)
+{
+    int i, j, server_limit, thread_limit;
+    int ready = 0;
+    int busy = 0;
+    int total;
+    ap_generation_t mpm_generation;
+
+    /* preload errored fields, we overwrite */
+    ld->idle = -1;
+    ld->busy = -1;
+    ld->bytes_served = 0;
+    ld->access_count = 0;
+
+    ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
+
+    for (i = 0; i < server_limit; i++) {
+        process_score *ps;
+        ps = ap_get_scoreboard_process(i);
+
+        for (j = 0; j < thread_limit; j++) {
+            int res;
+            worker_score *ws = NULL;
+            ws = &ap_scoreboard_image->servers[i][j];
+            res = ws->status;
+
+            if (!ps->quiescing && ps->pid) {
+                if (res == SERVER_READY && ps->generation == mpm_generation) {
+                    ready++;
+                }
+                else if (res != SERVER_DEAD &&
+                         res != SERVER_STARTING && res != SERVER_IDLE_KILL &&
+                         ps->generation == mpm_generation) {
+                    busy++;
+                }   
+            }
+
+            if (ap_extended_status && !ps->quiescing && ps->pid) {
+                if (ws->access_count != 0 
+                    || (res != SERVER_READY && res != SERVER_DEAD)) {
+                    ld->access_count += ws->access_count;
+                    ld->bytes_served += ws->bytes_served;
+                }
+            }
+        }
+    }
+    total = busy + ready;
+    if (total) {
+        ld->idle = ready * 100 / total;
+        ld->busy = busy * 100 / total;
+    }
+}
+
+AP_DECLARE(void) ap_get_loadavg(ap_loadavg_t *ld)
+{
+    /* preload errored fields, we overwrite */
+    ld->loadavg = -1.0;
+    ld->loadavg5 = -1.0;
+    ld->loadavg15 = -1.0;
+
+#if HAVE_GETLOADAVG
+    {
+        double la[3];
+        int num;
+
+        num = getloadavg(la, 3);
+        if (num > 0) {
+            ld->loadavg = (float)la[0];
+        }
+        if (num > 1) {
+            ld->loadavg5 = (float)la[1];
+        }
+        if (num > 2) {
+            ld->loadavg15 = (float)la[2];
+        }
+    }
+#endif
+}
+
+AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p,
+                                    const char *cmd,
+                                    const char * const * argv)
+{
+    char buf[MAX_STRING_LEN];
+    apr_procattr_t *procattr;
+    apr_proc_t *proc;
+    apr_file_t *fp;
+    apr_size_t nbytes = 1;
+    char c;
+    int k;
+
+    if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
+                            APR_FULL_BLOCK) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_dir_set(procattr,
+                             ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
+        return NULL;
+    if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
+        return NULL;
+    proc = apr_pcalloc(p, sizeof(apr_proc_t));
+    if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
+        return NULL;
+    fp = proc->out;
+
+    if (fp == NULL)
+        return NULL;
+    /* XXX: we are reading 1 byte at a time here */
+    for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
+                && nbytes == 1 && (k < MAX_STRING_LEN-1)     ; ) {
+        if (c == '\n' || c == '\r')
+            break;
+        buf[k++] = c;
+    }
+    buf[k] = '\0'; 
+    apr_file_close(fp);
+
+    return apr_pstrndup(p, buf, k);
+}