1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * util.c: string utility things
21 * 1995-96 Many changes by the Apache Software Foundation
26 * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls
27 * #define DEBUG_CFG_LINES to trace every line read from the config files
31 #include "apr_strings.h"
33 #include "apr_md5.h" /* for apr_password_validate */
35 #define APR_WANT_STDIO
36 #define APR_WANT_STRFUNC
42 #if APR_HAVE_PROCESS_H
43 #include <process.h> /* for getpid() on Win32 */
46 #include <netdb.h> /* for gethostbyname() */
49 #include "ap_config.h"
50 #include "apr_base64.h"
52 #include "http_main.h"
54 #include "http_protocol.h"
55 #include "http_config.h"
56 #include "http_core.h"
57 #include "util_ebcdic.h"
58 #include "util_varbuf.h"
66 #ifdef HAVE_SYS_LOADAVG_H
67 #include <sys/loadavg.h>
72 /* A bunch of functions in util.c scan strings looking for certain characters.
73 * To make that more efficient we encode a lookup table. The test_char_table
74 * is generated automatically by gen_test_char.c.
76 #include "test_char.h"
78 /* we assume the folks using this ensure 0 <= c < 256... which means
79 * you need a cast to (unsigned char) first, you can't just plug a
80 * char in here and get it to work, because if char is signed then it
81 * will first be sign extended.
83 #define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
85 /* Win32/NetWare/OS2 need to check for both forward and back slashes
86 * in ap_getparents() and ap_escape_url.
88 #ifdef CASE_BLIND_FILESYSTEM
89 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
92 #define IS_SLASH(s) (s == '/')
96 /* we know core's module_index is 0 */
97 #undef APLOG_MODULE_INDEX
98 #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
101 * Examine a field value (such as a media-/content-type) string and return
102 * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
104 AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
108 if (intype == NULL) return NULL;
110 semi = ap_strchr_c(intype, ';');
112 return apr_pstrdup(p, intype);
115 while ((semi > intype) && apr_isspace(semi[-1])) {
118 return apr_pstrmemdup(p, intype, semi - intype);
122 AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
126 char ts[MAX_STRING_LEN];
127 char tf[MAX_STRING_LEN];
134 apr_time_exp_gmt(&xt, t);
135 /* Convert %Z to "GMT" and %z to "+0000";
136 * on hosts that do not have a time zone string in struct tm,
137 * strftime must assume its argument is local time.
139 for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
141 if (*f != '%') continue;
152 case 'z': /* common extension */
166 apr_time_exp_lt(&xt, t);
169 /* check return code? */
170 apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt);
171 ts[MAX_STRING_LEN - 1] = '\0';
172 return apr_pstrdup(p, ts);
175 /* Roy owes Rob beer. */
176 /* Rob owes Roy dinner. */
178 /* These legacy comments would make a lot more sense if Roy hadn't
179 * replaced the old later_than() routine with util_date.c.
181 * Well, okay, they still wouldn't make any sense.
184 /* Match = 0, NoMatch = 1, Abort = -1
185 * Based loosely on sections of wildmat.c by Rich Salz
186 * Hmmm... shouldn't this really go component by component?
188 AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
192 for (x = 0, y = 0; expected[y]; ++y, ++x) {
193 if ((!str[x]) && (expected[y] != '*'))
195 if (expected[y] == '*') {
196 while (expected[++y] == '*');
201 if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
206 else if ((expected[y] != '?') && (str[x] != expected[y]))
209 return (str[x] != '\0');
212 AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
216 for (x = 0, y = 0; expected[y]; ++y, ++x) {
217 if (!str[x] && expected[y] != '*')
219 if (expected[y] == '*') {
220 while (expected[++y] == '*');
225 if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
230 else if (expected[y] != '?'
231 && apr_tolower(str[x]) != apr_tolower(expected[y]))
234 return (str[x] != '\0');
237 /* We actually compare the canonical root to this root, (but we don't
238 * waste time checking the case), since every use of this function in
239 * httpd-2.1 tests if the path is 'proper', meaning we've already passed
240 * it through apr_filepath_merge, or we haven't.
242 AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
245 const char *ourdir = dir;
246 if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS
247 || strncmp(newpath, ourdir, strlen(newpath)) != 0) {
253 AP_DECLARE(int) ap_is_matchexp(const char *str)
257 for (x = 0; str[x]; x++)
258 if ((str[x] == '*') || (str[x] == '?'))
264 * Here's a pool-based interface to the POSIX-esque ap_regcomp().
265 * Note that we return ap_regex_t instead of being passed one.
266 * The reason is that if you use an already-used ap_regex_t structure,
267 * the memory that you've already allocated gets forgotten, and
268 * regfree() doesn't clear it. So we don't allow it.
271 static apr_status_t regex_cleanup(void *preg)
273 ap_regfree((ap_regex_t *) preg);
277 AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
280 ap_regex_t *preg = apr_palloc(p, sizeof *preg);
281 int err = ap_regcomp(preg, pattern, cflags);
283 if (err == AP_REG_ESPACE)
288 apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
289 apr_pool_cleanup_null);
294 AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
297 apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
301 * Similar to standard strstr() but we ignore case in this version.
302 * Based on the strstr() implementation further below.
304 AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
312 for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
316 /* found first character of s2, see if the rest matches */
319 for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) {
321 /* both strings ended together */
326 /* second string ended, a match */
329 /* didn't find a match here, try starting at next character in s1 */
336 * Returns an offsetted pointer in bigstring immediately after
337 * prefix. Returns bigstring if bigstring doesn't start with
338 * prefix or if prefix is longer than bigstring while still matching.
339 * NOTE: pointer returned is relative to bigstring, so we
340 * can use standard pointer comparisons in the calling function
341 * (eg: test if ap_stripprefix(a,b) == a)
343 AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
352 while (*p1 && *prefix) {
353 if (*p1++ != *prefix++)
359 /* hit the end of bigstring! */
363 /* This function substitutes for $0-$9, filling in regular expression
364 * submatches. Pass it the same nmatch and pmatch arguments that you
365 * passed ap_regexec(). pmatch should not be greater than the maximum number
366 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
368 * nmatch must be <=AP_MAX_REG_MATCH (10).
370 * input should be the string with the $-expressions, source should be the
371 * string that was matched against.
373 * It returns the substituted string, or NULL if a vbuf is used.
374 * On errors, returns the orig string.
376 * Parts of this code are based on Henry Spencer's regsub(), from his
377 * AT&T V8 regexp package.
380 static apr_status_t regsub_core(apr_pool_t *p, char **result,
381 struct ap_varbuf *vb, const char *input,
382 const char *source, apr_size_t nmatch,
383 ap_regmatch_t pmatch[], apr_size_t maxlen)
385 const char *src = input;
391 AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result));
392 if (!source || nmatch>AP_MAX_REG_MATCH)
396 if (maxlen > 0 && len >= maxlen)
399 *result = apr_pstrmemdup(p, src, len);
403 ap_varbuf_strmemcat(vb, src, len);
408 /* First pass, find the size */
409 while ((c = *src++) != '\0') {
410 if (c == '$' && apr_isdigit(*src))
413 no = AP_MAX_REG_MATCH;
415 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */
416 if (c == '\\' && *src)
420 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
421 if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so)
423 len += pmatch[no].rm_eo - pmatch[no].rm_so;
428 if (len >= maxlen && maxlen > 0)
432 *result = dst = apr_palloc(p, len + 1);
435 if (vb->strlen == AP_VARBUF_UNKNOWN)
436 vb->strlen = strlen(vb->buf);
437 ap_varbuf_grow(vb, vb->strlen + len);
438 dst = vb->buf + vb->strlen;
442 /* Now actually fill in the string */
446 while ((c = *src++) != '\0') {
447 if (c == '$' && apr_isdigit(*src))
450 no = AP_MAX_REG_MATCH;
452 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */
453 if (c == '\\' && *src)
457 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
458 len = pmatch[no].rm_eo - pmatch[no].rm_so;
459 memcpy(dst, source + pmatch[no].rm_so, len);
469 #ifndef AP_PREGSUB_MAXLEN
470 #define AP_PREGSUB_MAXLEN (HUGE_STRING_LEN * 8)
472 AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
473 const char *source, apr_size_t nmatch,
474 ap_regmatch_t pmatch[])
477 apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch,
478 pmatch, AP_PREGSUB_MAXLEN);
479 if (rc != APR_SUCCESS)
484 AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
485 const char *input, const char *source,
486 apr_size_t nmatch, ap_regmatch_t pmatch[],
489 apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch,
491 if (rc != APR_SUCCESS)
497 * Parse .. so we don't compromise security
499 AP_DECLARE(void) ap_getparents(char *name)
504 /* Four paseses, as per RFC 1808 */
505 /* a) remove ./ path segments */
506 for (next = name; *next && (*next != '.'); next++) {
509 l = w = first_dot = next - name;
510 while (name[l] != '\0') {
511 if (name[l] == '.' && IS_SLASH(name[l + 1])
512 && (l == 0 || IS_SLASH(name[l - 1])))
515 name[w++] = name[l++];
518 /* b) remove trailing . path, segment */
519 if (w == 1 && name[0] == '.')
521 else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
525 /* c) remove all xx/../ segments. (including leading ../ and /../) */
528 while (name[l] != '\0') {
529 if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
530 && (l == 0 || IS_SLASH(name[l - 1]))) {
535 while (l >= 0 && !IS_SLASH(name[l]))
542 while ((name[n] = name[m]))
549 /* d) remove trailing xx/.. segment. */
550 if (l == 2 && name[0] == '.' && name[1] == '.')
552 else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
553 && IS_SLASH(name[l - 3])) {
556 while (l >= 0 && !IS_SLASH(name[l]))
566 AP_DECLARE(void) ap_no2slash(char *name)
572 #ifdef HAVE_UNC_PATHS
573 /* Check for UNC names. Leave leading two slashes. */
574 if (s[0] == '/' && s[1] == '/')
579 if ((*d++ = *s) == '/') {
593 * copy at most n leading directories of s into d
594 * d should be at least as large as s plus 1 extra byte
596 * the return value is the ever useful pointer to the trailing \0 of d
598 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
599 * so that if n == 0, "/" is returned in d with n == 1
600 * and s == "e:/test.html", "e:/" is returned in d
601 * *** See also directory_walk in modules/http/http_request.c
604 * /a/b, 0 ==> / (true for all platforms)
613 * c:/a/b 3 ==> c:/a/b
614 * c:/a/b 4 ==> c:/a/b
616 AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
625 if (*s == '\0' || (*s == '/' && (--n) == 0)) {
637 * return the parent directory name including trailing / of the file s
639 AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
641 const char *last_slash = ap_strrchr_c(s, '/');
645 if (last_slash == NULL) {
646 return apr_pstrdup(p, "");
648 l = (last_slash - s) + 1;
649 d = apr_pstrmemdup(p, s, l);
655 AP_DECLARE(int) ap_count_dirs(const char *path)
659 for (x = 0, n = 0; path[x]; x++)
665 AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
667 return ap_getword(atrans, (const char **) line, stop);
670 AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
672 const char *pos = *line;
676 while ((*pos != stop) && *pos) {
681 res = apr_pstrmemdup(atrans, *line, len);
684 while (*pos == stop) {
693 AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
695 return ap_getword_white(atrans, (const char **) line);
698 AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
700 const char *pos = *line;
704 while (!apr_isspace(*pos) && *pos) {
709 res = apr_pstrmemdup(atrans, *line, len);
711 while (apr_isspace(*pos)) {
720 AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
723 return ap_getword_nulls(atrans, (const char **) line, stop);
726 AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
729 const char *pos = ap_strchr_c(*line, stop);
733 apr_size_t len = strlen(*line);
734 res = apr_pstrmemdup(atrans, *line, len);
739 res = apr_pstrmemdup(atrans, *line, pos - *line);
748 /* Get a word, (new) config-file style --- quoted strings and backslashes
752 static char *substring_conf(apr_pool_t *p, const char *start, int len,
755 char *result = apr_palloc(p, len + 1);
759 for (i = 0; i < len; ++i) {
760 if (start[i] == '\\' && (start[i + 1] == '\\'
761 || (quote && start[i + 1] == quote)))
762 *resp++ = start[++i];
768 #if RESOLVE_ENV_PER_TOKEN
769 return (char *)ap_resolve_env(p,result);
775 AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
777 return ap_getword_conf(p, (const char **) line);
780 AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
782 const char *str = *line, *strend;
786 while (apr_isspace(*str))
794 if ((quote = *str) == '"' || quote == '\'') {
796 while (*strend && *strend != quote) {
797 if (*strend == '\\' && strend[1] &&
798 (strend[1] == quote || strend[1] == '\\')) {
805 res = substring_conf(p, str + 1, strend - str - 1, quote);
807 if (*strend == quote)
812 while (*strend && !apr_isspace(*strend))
815 res = substring_conf(p, str, strend - str, 0);
818 while (apr_isspace(*strend))
824 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
827 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00551)
828 "Done with config file %s", cfp->name);
830 return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
833 /* we can't use apr_file_* directly because of linking issues on Windows */
834 static apr_status_t cfg_close(void *param)
836 return apr_file_close(param);
839 static apr_status_t cfg_getch(char *ch, void *param)
841 return apr_file_getc(ch, param);
844 static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param)
846 return apr_file_gets(buf, bufsiz, param);
849 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
850 AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
851 apr_pool_t *p, const char *name)
853 ap_configfile_t *new_cfg;
854 apr_file_t *file = NULL;
862 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552)
863 "Internal error: pcfg_openfile() called with NULL filename");
867 status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
870 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553)
871 "Opening config file %s (%s)",
872 name, (status != APR_SUCCESS) ?
873 apr_strerror(status, buf, sizeof(buf)) : "successful");
875 if (status != APR_SUCCESS)
878 status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
879 if (status != APR_SUCCESS)
882 if (finfo.filetype != APR_REG &&
883 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
884 strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
886 strcmp(name, "/dev/null") != 0) {
887 #endif /* WIN32 || OS2 */
888 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554)
889 "Access to file %s denied by server: not a regular file",
891 apr_file_close(file);
896 /* Some twisted character [no pun intended] at MS decided that a
897 * zero width joiner as the lead wide character would be ideal for
898 * describing Unicode text files. This was further convoluted to
899 * another MSism that the same character mapped into utf-8, EF BB BF
900 * would signify utf-8 text files.
902 * Since MS configuration files are all protecting utf-8 encoded
903 * Unicode path, file and resource names, we already have the correct
904 * WinNT encoding. But at least eat the stupid three bytes up front.
907 unsigned char buf[4];
909 status = apr_file_read(file, buf, &len);
910 if ((status != APR_SUCCESS) || (len < 3)
911 || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
913 apr_file_seek(file, APR_SET, &zero);
918 new_cfg = apr_palloc(p, sizeof(*new_cfg));
919 new_cfg->param = file;
920 new_cfg->name = apr_pstrdup(p, name);
921 new_cfg->getch = cfg_getch;
922 new_cfg->getstr = cfg_getstr;
923 new_cfg->close = cfg_close;
924 new_cfg->line_number = 0;
930 /* Allocate a ap_configfile_t handle with user defined functions and params */
931 AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(
932 apr_pool_t *p, const char *descr, void *param,
933 apr_status_t (*getc_func) (char *ch, void *param),
934 apr_status_t (*gets_func) (void *buf, apr_size_t bufsize, void *param),
935 apr_status_t (*close_func) (void *param))
937 ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
938 new_cfg->param = param;
939 new_cfg->name = descr;
940 new_cfg->getch = getc_func;
941 new_cfg->getstr = gets_func;
942 new_cfg->close = close_func;
943 new_cfg->line_number = 0;
947 /* Read one character from a configfile_t */
948 AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp)
950 apr_status_t rc = cfp->getch(ch, cfp->param);
951 if (rc == APR_SUCCESS && *ch == LF)
956 AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp,
959 if (rc == APR_SUCCESS)
962 if (rc == APR_ENOSPC)
963 return apr_psprintf(p, "Error reading %s at line %d: Line too long",
964 cfp->name, cfp->line_number);
966 return apr_psprintf(p, "Error reading %s at line %d: %pm",
967 cfp->name, cfp->line_number, &rc);
970 /* Read one line from open ap_configfile_t, strip LF, increase line number */
971 /* If custom handler does not define a getstr() function, read char by char */
972 static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize,
973 apr_size_t offset, ap_configfile_t *cfp)
976 /* If a "get string" function is defined, use it */
977 if (cfp->getstr != NULL) {
979 char *cbuf = buf + offset;
980 apr_size_t cbufsize = bufsize - offset;
984 rc = cfp->getstr(cbuf, cbufsize, cfp->param);
986 if (cbuf != buf + offset) {
994 if (rc != APR_SUCCESS) {
999 * check for line continuation,
1000 * i.e. match [^\\]\\[\r]\n only
1004 if (cp > buf && cp[-1] == LF) {
1006 if (cp > buf && cp[-1] == CR)
1008 if (cp > buf && cp[-1] == '\\') {
1011 * line continuation requested -
1012 * then remove backslash and continue
1014 cbufsize -= (cp-cbuf);
1019 else if (cp - buf >= bufsize - 1) {
1025 /* No "get string" function defined; read character by character */
1026 apr_size_t i = offset;
1029 /* too small, assume caller is crazy */
1036 rc = cfp->getch(&c, cfp->param);
1037 if (rc == APR_EOF) {
1043 if (rc != APR_SUCCESS)
1047 /* check for line continuation */
1048 if (i > 0 && buf[i-1] == '\\') {
1058 if (i >= bufsize - 1) {
1067 static int cfg_trim_line(char *buf)
1071 * Leading and trailing white space is eliminated completely
1074 while (apr_isspace(*start))
1076 /* blast trailing whitespace */
1077 end = &start[strlen(start)];
1078 while (--end >= start && apr_isspace(*end))
1080 /* Zap leading whitespace by shifting */
1082 memmove(buf, start, end - start + 2);
1083 #ifdef DEBUG_CFG_LINES
1084 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf);
1086 return end - start + 1;
1089 /* Read one line from open ap_configfile_t, strip LF, increase line number */
1090 /* If custom handler does not define a getstr() function, read char by char */
1091 AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize,
1092 ap_configfile_t *cfp)
1094 apr_status_t rc = ap_cfg_getline_core(buf, bufsize, 0, cfp);
1095 if (rc == APR_SUCCESS)
1100 AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb,
1101 ap_configfile_t *cfp,
1109 if (vb->strlen == AP_VARBUF_UNKNOWN)
1110 vb->strlen = strlen(vb->buf);
1111 if (vb->avail - vb->strlen < 3) {
1112 new_len = vb->avail * 2;
1113 if (new_len > max_len)
1115 else if (new_len < 3)
1117 ap_varbuf_grow(vb, new_len);
1121 rc = ap_cfg_getline_core(vb->buf, vb->avail, vb->strlen, cfp);
1122 if (rc == APR_ENOSPC || rc == APR_SUCCESS)
1123 vb->strlen += strlen(vb->buf + vb->strlen);
1124 if (rc != APR_ENOSPC)
1126 if (vb->avail >= max_len)
1128 new_len = vb->avail * 2;
1129 if (new_len > max_len)
1131 ap_varbuf_grow(vb, new_len);
1134 if (vb->strlen > max_len)
1136 if (rc == APR_SUCCESS)
1137 vb->strlen = cfg_trim_line(vb->buf);
1141 /* Size an HTTP header field list item, as separated by a comma.
1142 * The return value is a pointer to the beginning of the non-empty list item
1143 * within the original string (or NULL if there is none) and the address
1144 * of field is shifted to the next non-comma, non-whitespace character.
1145 * len is the length of the item excluding any beginning whitespace.
1147 AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
1149 const unsigned char *ptr = (const unsigned char *)*field;
1150 const unsigned char *token;
1151 int in_qpair, in_qstr, in_com;
1153 /* Find first non-comma, non-whitespace byte */
1155 while (*ptr == ',' || apr_isspace(*ptr))
1160 /* Find the end of this item, skipping over dead bits */
1162 for (in_qpair = in_qstr = in_com = 0;
1163 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1171 case '\\': in_qpair = 1; /* quoted-pair */
1173 case '"' : if (!in_com) /* quoted string delim */
1176 case '(' : if (!in_qstr) /* comment (may nest) */
1179 case ')' : if (in_com) /* end comment */
1187 if ((*len = (ptr - token)) == 0) {
1188 *field = (const char *)ptr;
1192 /* Advance field pointer to the next non-comma, non-white byte */
1194 while (*ptr == ',' || apr_isspace(*ptr))
1197 *field = (const char *)ptr;
1198 return (const char *)token;
1201 /* Retrieve an HTTP header field list item, as separated by a comma,
1202 * while stripping insignificant whitespace and lowercasing anything not in
1203 * a quoted string or comment. The return value is a new string containing
1204 * the converted list item (or NULL if none) and the address pointed to by
1205 * field is shifted to the next non-comma, non-whitespace.
1207 AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
1209 const char *tok_start;
1210 const unsigned char *ptr;
1213 int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
1215 /* Find the beginning and maximum length of the list item so that
1216 * we can allocate a buffer for the new string and reset the field.
1218 if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1221 token = apr_palloc(p, tok_len + 1);
1223 /* Scan the token again, but this time copy only the good bytes.
1224 * We skip extra whitespace and any whitespace around a '=', '/',
1225 * or ';' and lowercase normal characters not within a comment,
1226 * quoted-string or quoted-pair.
1228 for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1229 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1238 case '\\': in_qpair = 1;
1244 case '"' : if (!in_com)
1251 case '(' : if (!in_qstr)
1258 case ')' : if (in_com)
1264 case '\t': if (addspace)
1266 if (in_com || in_qstr)
1273 case ';' : if (!(in_com || in_qstr))
1277 default : if (addspace == 1)
1279 *pos++ = (in_com || in_qstr) ? *ptr
1280 : apr_tolower(*ptr);
1291 typedef enum ap_etag_e {
1297 /* Find an item in canonical form (lowercase, no extra spaces) within
1298 * an HTTP field value list. Returns 1 if found, 0 if not found.
1299 * This would be much more efficient if we stored header fields as
1300 * an array of list items as they are received instead of a plain string.
1302 static int find_list_item(apr_pool_t *p, const char *line,
1303 const char *tok, ap_etag_e type)
1305 const unsigned char *pos;
1306 const unsigned char *ptr = (const unsigned char *)line;
1307 int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
1309 if (!line || !tok) {
1312 if (type == AP_ETAG_STRONG && *tok != '\"') {
1315 if (type == AP_ETAG_WEAK) {
1316 if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') {
1319 else if (*tok != '\"') {
1324 do { /* loop for each item in line's list */
1326 /* Find first non-comma, non-whitespace byte */
1327 while (*ptr == ',' || apr_isspace(*ptr)) {
1331 /* Account for strong or weak Etags, depending on our search */
1332 if (type == AP_ETAG_STRONG && *ptr != '\"') {
1335 if (type == AP_ETAG_WEAK) {
1336 if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
1339 else if (*ptr != '\"') {
1345 good = 1; /* until proven otherwise for this item */
1347 break; /* no items left and nothing good found */
1349 /* We skip extra whitespace and any whitespace around a '=', '/',
1350 * or ';' and lowercase normal characters not within a comment,
1351 * quoted-string or quoted-pair.
1353 for (pos = (const unsigned char *)tok;
1354 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1360 good = (*pos++ == *ptr);
1364 case '\\': in_qpair = 1;
1366 good = good && (*pos++ == ' ');
1367 good = good && (*pos++ == *ptr);
1370 case '"' : if (!in_com)
1373 good = good && (*pos++ == ' ');
1374 good = good && (*pos++ == *ptr);
1377 case '(' : if (!in_qstr)
1380 good = good && (*pos++ == ' ');
1381 good = good && (*pos++ == *ptr);
1384 case ')' : if (in_com)
1386 good = good && (*pos++ == *ptr);
1390 case '\t': if (addspace || !good)
1392 if (in_com || in_qstr)
1393 good = (*pos++ == *ptr);
1399 case ';' : if (!(in_com || in_qstr))
1401 good = good && (*pos++ == *ptr);
1403 default : if (!good)
1406 good = (*pos++ == ' ');
1407 if (in_com || in_qstr)
1408 good = good && (*pos++ == *ptr);
1411 && (apr_tolower(*pos++) == apr_tolower(*ptr));
1418 good = 0; /* not good if only a prefix was matched */
1420 } while (*ptr && !good);
1425 /* Find an item in canonical form (lowercase, no extra spaces) within
1426 * an HTTP field value list. Returns 1 if found, 0 if not found.
1427 * This would be much more efficient if we stored header fields as
1428 * an array of list items as they are received instead of a plain string.
1430 AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
1433 return find_list_item(p, line, tok, AP_ETAG_NONE);
1436 /* Find a strong Etag in canonical form (lowercase, no extra spaces) within
1437 * an HTTP field value list. Returns 1 if found, 0 if not found.
1439 AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line,
1442 return find_list_item(p, line, tok, AP_ETAG_STRONG);
1445 /* Find a weak ETag in canonical form (lowercase, no extra spaces) within
1446 * an HTTP field value list. Returns 1 if found, 0 if not found.
1448 AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line,
1451 return find_list_item(p, line, tok, AP_ETAG_WEAK);
1454 /* Grab a list of tokens of the format 1#token (from RFC7230) */
1455 AP_DECLARE(const char *) ap_parse_token_list_strict(apr_pool_t *p,
1457 apr_array_header_t **tokens,
1460 int in_leading_space = 1;
1461 int in_trailing_space = 0;
1463 const char *tok_begin;
1470 tok_begin = cur = str_in;
1472 while (!string_end) {
1473 const unsigned char c = (unsigned char)*cur;
1475 if (!TEST_CHAR(c, T_HTTP_TOKEN_STOP) && c != '\0') {
1476 /* Non-separator character; we are finished with leading
1477 * whitespace. We must never have encountered any trailing
1478 * whitespace before the delimiter (comma) */
1479 in_leading_space = 0;
1480 if (in_trailing_space) {
1481 return "Encountered illegal whitespace in token";
1484 else if (c == ' ' || c == '\t') {
1485 /* "Linear whitespace" only includes ASCII CRLF, space, and tab;
1486 * we can't get a CRLF since headers are split on them already,
1487 * so only look for a space or a tab */
1488 if (in_leading_space) {
1489 /* We're still in leading whitespace */
1493 /* We must be in trailing whitespace */
1494 ++in_trailing_space;
1497 else if (c == ',' || c == '\0') {
1498 if (!in_leading_space) {
1499 /* If we're out of the leading space, we know we've read some
1500 * characters of a token */
1501 if (*tokens == NULL) {
1502 *tokens = apr_array_make(p, 4, sizeof(char *));
1504 APR_ARRAY_PUSH(*tokens, char *) =
1505 apr_pstrmemdup((*tokens)->pool, tok_begin,
1506 (cur - tok_begin) - in_trailing_space);
1508 /* We're allowed to have null elements, just don't add them to the
1511 tok_begin = cur + 1;
1512 in_leading_space = 1;
1513 in_trailing_space = 0;
1514 string_end = (c == '\0');
1517 /* Encountered illegal separator char */
1519 /* Skip to the next separator */
1521 temp = ap_strchr_c(cur, ',');
1523 temp = ap_strchr_c(cur, '\0');
1526 /* Act like we haven't seen a token so we reset */
1528 in_leading_space = 1;
1529 in_trailing_space = 0;
1532 return apr_psprintf(p, "Encountered illegal separator "
1533 "'\\x%.2x'", (unsigned int)c);
1543 /* Retrieve a token, spacing over it and returning a pointer to
1544 * the first non-white byte afterwards. Note that these tokens
1545 * are delimited by semis and commas; and can also be delimited
1546 * by whitespace at the caller's option.
1549 AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
1552 const char *ptr = *accept_line;
1553 const char *tok_start;
1556 /* Find first non-white byte */
1558 while (apr_isspace(*ptr))
1563 /* find token end, skipping over quoted strings.
1564 * (comments are already gone).
1567 while (*ptr && (accept_white || !apr_isspace(*ptr))
1568 && *ptr != ';' && *ptr != ',') {
1575 token = apr_pstrmemdup(p, tok_start, ptr - tok_start);
1577 /* Advance accept_line pointer to the next non-white byte */
1579 while (apr_isspace(*ptr))
1587 /* find http tokens, see the definition of token from RFC2068 */
1588 AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
1590 const unsigned char *start_token;
1591 const unsigned char *s;
1596 s = (const unsigned char *)line;
1598 /* find start of token, skip all stop characters, note NUL
1599 * isn't a token stop, so we don't need to test for it
1601 while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1608 /* find end of the token */
1609 while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1612 if (!strncasecmp((const char *)start_token, (const char *)tok,
1623 AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
1626 int llen, tlen, lidx;
1631 llen = strlen(line);
1636 (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1639 return (strncasecmp(&line[lidx], tok, tlen) == 0);
1642 AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
1646 const unsigned char *s;
1648 cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */
1649 d = (unsigned char *)cmd;
1650 s = (const unsigned char *)str;
1653 #if defined(OS2) || defined(WIN32)
1655 * Newlines to Win32/OS2 CreateProcess() are ill advised.
1656 * Convert them to spaces since they are effectively white
1657 * space to most applications
1659 if (*s == '\r' || *s == '\n') {
1665 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1675 static char x2c(const char *what)
1679 #if !APR_CHARSET_EBCDIC
1680 digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
1683 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
1685 #else /*APR_CHARSET_EBCDIC*/
1692 digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
1693 0xFF & strtol(xstr, NULL, 16));
1694 #endif /*APR_CHARSET_EBCDIC*/
1699 * Unescapes a URL, leaving reserved characters intact.
1700 * Returns 0 on success, non-zero on error
1702 * bad % escape returns HTTP_BAD_REQUEST
1704 * decoding %00 or a forbidden character returns HTTP_NOT_FOUND
1707 static int unescape_url(char *url, const char *forbid, const char *reserved)
1709 int badesc, badpath;
1714 /* Initial scan for first '%'. Don't bother writing values before
1716 y = strchr(url, '%');
1720 for (x = y; *y; ++x, ++y) {
1725 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1731 decoded = x2c(y + 1);
1732 if ((decoded == '\0')
1733 || (forbid && ap_strchr_c(forbid, decoded))) {
1738 else if (reserved && ap_strchr_c(reserved, decoded)) {
1752 return HTTP_BAD_REQUEST;
1755 return HTTP_NOT_FOUND;
1761 AP_DECLARE(int) ap_unescape_url(char *url)
1764 return unescape_url(url, SLASHES, NULL);
1766 AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
1768 /* AllowEncodedSlashes (corrected) */
1769 if (decode_slashes) {
1770 /* no chars reserved */
1771 return unescape_url(url, NULL, NULL);
1773 /* reserve (do not decode) encoded slashes */
1774 return unescape_url(url, NULL, SLASHES);
1778 /* IFDEF these out until they've been thought through.
1779 * Just a germ of an API extension for now
1781 AP_DECLARE(int) ap_unescape_url_proxy(char *url)
1783 /* leave RFC1738 reserved characters intact, * so proxied URLs
1784 * don't get mangled. Where does that leave encoded '&' ?
1786 return unescape_url(url, NULL, "/;?");
1788 AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
1790 return unescape_url(url, NULL, reserved);
1794 AP_DECLARE(int) ap_unescape_urlencoded(char *query)
1798 /* replace plus with a space */
1800 for (slider = query; *slider; slider++) {
1801 if (*slider == '+') {
1807 /* unescape everything else */
1808 return unescape_url(query, NULL, NULL);
1811 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
1812 apr_port_t port, const request_rec *r)
1814 if (ap_is_default_port(port, r)) {
1815 return apr_pstrdup(p, hostname);
1818 return apr_psprintf(p, "%s:%u", hostname, port);
1822 AP_DECLARE(int) ap_unescape_all(char *url)
1824 return unescape_url(url, NULL, NULL);
1827 /* c2x takes an unsigned, and expects the caller has guaranteed that
1828 * 0 <= what < 256... which usually means that you have to cast to
1829 * unsigned char first, because (unsigned)(char)(x) first goes through
1830 * signed extension to an int before the unsigned cast.
1832 * The reason for this assumption is to assist gcc code generation --
1833 * the unsigned char -> unsigned extension is already done earlier in
1834 * both uses of this code, so there's no need to waste time doing it
1837 static const char c2x_table[] = "0123456789abcdef";
1839 static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
1840 unsigned char *where)
1842 #if APR_CHARSET_EBCDIC
1843 what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
1844 #endif /*APR_CHARSET_EBCDIC*/
1846 *where++ = c2x_table[what >> 4];
1847 *where++ = c2x_table[what & 0xf];
1852 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1853 * routine is (should be) OS independent.
1855 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1856 * cases if a ':' occurs before the first '/' in the URL, the URL should be
1857 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1858 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1859 * efficiency reasons, we don't use escape_path_segment(), which is provided for
1860 * reference. Again, RFC 1808 is where this stuff is defined.
1862 * If partial is set, os_escape_path() assumes that the path will be appended to
1863 * something with a '/' in it (and thus does not prefix "./").
1866 AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
1868 const unsigned char *s = (const unsigned char *)segment;
1869 unsigned char *d = (unsigned char *)copy;
1873 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1885 AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
1887 return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
1890 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
1892 /* Allocate +3 for potential "./" and trailing NULL.
1893 * Allocate another +1 to allow the caller to add a trailing '/' (see
1894 * comment in 'ap_sub_req_lookup_dirent')
1896 char *copy = apr_palloc(p, 3 * strlen(path) + 3 + 1);
1897 const unsigned char *s = (const unsigned char *)path;
1898 unsigned char *d = (unsigned char *)copy;
1902 const char *colon = ap_strchr_c(path, ':');
1903 const char *slash = ap_strchr_c(path, '/');
1905 if (colon && (!slash || colon < slash)) {
1911 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1923 AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer)
1925 const unsigned char *s = (const unsigned char *)buffer;
1926 unsigned char *d = (unsigned char *)copy;
1930 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) {
1933 else if (c == ' ') {
1945 AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
1947 return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer);
1950 /* ap_escape_uri is now a macro for os_escape_path */
1952 AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
1957 /* first, count the number of extra characters */
1958 for (i = 0, j = 0; s[i] != '\0'; i++)
1959 if (s[i] == '<' || s[i] == '>')
1961 else if (s[i] == '&')
1963 else if (s[i] == '"')
1965 else if (toasc && !apr_isascii(s[i]))
1969 return apr_pstrmemdup(p, s, i);
1971 x = apr_palloc(p, i + j + 1);
1972 for (i = 0, j = 0; s[i] != '\0'; i++, j++)
1974 memcpy(&x[j], "<", 4);
1977 else if (s[i] == '>') {
1978 memcpy(&x[j], ">", 4);
1981 else if (s[i] == '&') {
1982 memcpy(&x[j], "&", 5);
1985 else if (s[i] == '"') {
1986 memcpy(&x[j], """, 6);
1989 else if (toasc && !apr_isascii(s[i])) {
1990 char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
1991 memcpy(&x[j], esc, 6);
2000 AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
2004 const unsigned char *s;
2005 apr_size_t length, escapes = 0;
2011 /* Compute how many characters need to be escaped */
2012 s = (const unsigned char *)str;
2014 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
2019 /* Compute the length of the input string, including NULL */
2020 length = s - (const unsigned char *)str + 1;
2022 /* Fast path: nothing to escape */
2024 return apr_pmemdup(p, str, length);
2027 /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
2028 ret = apr_palloc(p, length + 3 * escapes);
2029 d = (unsigned char *)ret;
2030 s = (const unsigned char *)str;
2032 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
2068 AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
2071 unsigned char *d, *ep;
2072 const unsigned char *s;
2074 if (!source || !buflen) { /* be safe */
2078 d = (unsigned char *)dest;
2079 s = (const unsigned char *)source;
2080 ep = d + buflen - 1;
2082 for (; d < ep && *s; ++s) {
2084 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
2110 case '"': /* no need for this in error log */
2115 ep = --d; /* break the for loop as well */
2128 return (d - (unsigned char *)dest);
2131 AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
2133 const unsigned char *in = src;
2136 for (i = 0; i < srclen; i++) {
2137 *dest++ = c2x_table[in[i] >> 4];
2138 *dest++ = c2x_table[in[i] & 0xf];
2143 AP_DECLARE(int) ap_has_cntrl(const char *str)
2146 if (apr_iscntrl(*str))
2153 AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
2157 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
2158 return 0; /* in error condition, just return no */
2160 return (finfo.filetype == APR_DIR);
2163 AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
2167 if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
2168 return 0; /* in error condition, just return no */
2170 return (finfo.filetype == APR_DIR);
2173 AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
2176 apr_size_t len1, len2;
2179 len1 = strlen(src1);
2180 len2 = strlen(src2);
2181 /* allocate +3 for '/' delimiter, trailing NULL and overallocate
2182 * one extra byte to allow the caller to add a trailing '/'
2184 path = (char *)apr_palloc(a, len1 + len2 + 3);
2187 memcpy(path + 1, src2, len2 + 1);
2191 memcpy(path, src1, len1);
2193 if (next[-1] != '/') {
2196 memcpy(next, src2, len2 + 1);
2202 * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
2204 AP_DECLARE(int) ap_is_url(const char *u)
2208 for (x = 0; u[x] != ':'; x++) {
2210 ((!apr_isalnum(u[x])) &&
2211 (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
2216 return (x ? 1 : 0); /* If the first character is ':', it's broken, too */
2219 AP_DECLARE(int) ap_ind(const char *s, char c)
2221 const char *p = ap_strchr_c(s, c);
2228 AP_DECLARE(int) ap_rind(const char *s, char c)
2230 const char *p = ap_strrchr_c(s, c);
2237 AP_DECLARE(void) ap_str_tolower(char *str)
2240 *str = apr_tolower(*str);
2245 AP_DECLARE(void) ap_str_toupper(char *str)
2248 *str = apr_toupper(*str);
2254 * We must return a FQDN
2256 char *ap_get_local_host(apr_pool_t *a)
2258 #ifndef MAXHOSTNAMELEN
2259 #define MAXHOSTNAMELEN 256
2261 char str[MAXHOSTNAMELEN + 1];
2262 char *server_hostname = NULL;
2263 apr_sockaddr_t *sockaddr;
2266 if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
2267 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556)
2268 "%s: apr_gethostname() failed to determine ServerName",
2271 str[sizeof(str) - 1] = '\0';
2272 if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
2273 if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
2274 (ap_strchr_c(hostname, '.')) ) {
2275 server_hostname = apr_pstrdup(a, hostname);
2276 return server_hostname;
2277 } else if (ap_strchr_c(str, '.')) {
2278 server_hostname = apr_pstrdup(a, str);
2280 apr_sockaddr_ip_get(&hostname, sockaddr);
2281 server_hostname = apr_pstrdup(a, hostname);
2284 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557)
2285 "%s: apr_sockaddr_info_get() failed for %s",
2286 ap_server_argv0, str);
2290 if (!server_hostname)
2291 server_hostname = apr_pstrdup(a, "127.0.0.1");
2293 ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558)
2294 "%s: Could not reliably determine the server's fully qualified "
2295 "domain name, using %s. Set the 'ServerName' directive globally "
2296 "to suppress this message",
2297 ap_server_argv0, server_hostname);
2299 return server_hostname;
2302 /* simple 'pool' alloc()ing glue to apr_base64.c
2304 AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
2309 decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded));
2310 l = apr_base64_decode(decoded, bufcoded);
2311 decoded[l] = '\0'; /* make binary sequence into string */
2316 AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
2319 int l = strlen(string);
2321 encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l));
2322 l = apr_base64_encode(encoded, string, l);
2323 encoded[l] = '\0'; /* make binary sequence into string */
2328 /* we want to downcase the type/subtype for comparison purposes
2329 * but nothing else because ;parameter=foo values are case sensitive.
2330 * XXX: in truth we want to downcase parameter names... but really,
2331 * apache has never handled parameters and such correctly. You
2332 * also need to compress spaces and such to be able to compare
2335 AP_DECLARE(void) ap_content_type_tolower(char *str)
2339 semi = strchr(str, ';');
2344 ap_str_tolower(str);
2352 * Given a string, replace any bare " with \" .
2354 AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
2357 const char *inchr = instring;
2358 char *outchr, *outstring;
2361 * Look through the input string, jogging the length of the output
2362 * string up by an extra byte each time we find an unescaped ".
2364 while (*inchr != '\0') {
2366 if (*inchr == '"') {
2370 * If we find a slosh, and it's not the last byte in the string,
2371 * it's escaping something - advance past both bytes.
2373 if ((*inchr == '\\') && (inchr[1] != '\0')) {
2379 outstring = apr_palloc(p, newlen + 1);
2383 * Now copy the input string to the output string, inserting a slosh
2384 * in front of every " that doesn't already have one.
2386 while (*inchr != '\0') {
2387 if ((*inchr == '\\') && (inchr[1] != '\0')) {
2388 *outchr++ = *inchr++;
2389 *outchr++ = *inchr++;
2391 if (*inchr == '"') {
2394 if (*inchr != '\0') {
2395 *outchr++ = *inchr++;
2403 * Given a string, append the PID deliminated by delim.
2404 * Usually used to create a pid-appended filepath name
2405 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
2406 * a macro, to avoid unistd.h dependency
2408 AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
2411 return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
2417 * Parse a given timeout parameter string into an apr_interval_time_t value.
2418 * The unit of the time interval is given as postfix string to the numeric
2419 * string. Currently the following units are understood:
2426 * If no unit is contained in the given timeout parameter the default_time_unit
2427 * will be used instead.
2428 * @param timeout_parameter The string containing the timeout parameter.
2429 * @param timeout The timeout value to be returned.
2430 * @param default_time_unit The default time unit to use if none is specified
2431 * in timeout_parameter.
2432 * @return Status value indicating whether the parsing was successful or not.
2434 AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
2435 const char *timeout_parameter,
2436 apr_interval_time_t *timeout,
2437 const char *default_time_unit)
2440 const char *time_str;
2443 tout = apr_strtoi64(timeout_parameter, &endp, 10);
2447 if (!endp || !*endp) {
2448 time_str = default_time_unit;
2454 switch (*time_str) {
2455 /* Time is in seconds */
2457 *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
2460 /* Time is in hours */
2461 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
2464 switch (*(++time_str)) {
2465 /* Time is in milliseconds */
2467 *timeout = (apr_interval_time_t) tout * 1000;
2469 /* Time is in minutes */
2471 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
2474 return APR_EGENERAL;
2478 return APR_EGENERAL;
2484 * Determine if a request has a request body or not.
2486 * @param r the request_rec of the request
2487 * @return truth value
2489 AP_DECLARE(int) ap_request_has_body(request_rec *r)
2496 has_body = (!r->header_only
2498 || apr_table_get(r->headers_in, "Transfer-Encoding")
2499 || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
2500 && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
2508 AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
2510 void **ptr = (void **)data_;
2515 AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) {
2517 for ( ; *src; src++, dest++)
2519 if (!apr_isprint(*src))
2521 else if (!apr_isalnum(*src))
2531 AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src,
2534 char *new = apr_palloc(p, strlen(src)+1);
2538 return ap_str2_alnum(src, new);
2542 * Read the body and parse any form found, which must be of the
2543 * type application/x-www-form-urlencoded.
2545 * Name/value pairs are returned in an array, with the names as
2546 * strings with a maximum length of HUGE_STRING_LEN, and the
2547 * values as bucket brigades. This allows values to be arbitrarily
2550 * All url-encoding is removed from both the names and the values
2551 * on the fly. The names are interpreted as strings, while the
2552 * values are interpreted as blocks of binary data, that may
2553 * contain the 0 character.
2555 * In order to ensure that resource limits are not exceeded, a
2556 * maximum size must be provided. If the sum of the lengths of
2557 * the names and the values exceed this size, this function
2558 * will return HTTP_REQUEST_ENTITY_TOO_LARGE.
2560 * An optional number of parameters can be provided, if the number
2561 * of parameters provided exceeds this amount, this function will
2562 * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative,
2563 * no limit is imposed, and the number of parameters is in turn
2564 * constrained by the size parameter above.
2566 * This function honours any kept_body configuration, and the
2567 * original raw request body will be saved to the kept_body brigade
2568 * if so configured, just as ap_discard_request_body does.
2570 * NOTE: File upload is not yet supported, but can be without change
2571 * to the function call.
2574 /* form parsing stuff */
2585 AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
2586 apr_array_header_t **ptr,
2587 apr_size_t num, apr_size_t usize)
2589 apr_bucket_brigade *bb = NULL;
2591 char buffer[HUGE_STRING_LEN + 1];
2593 apr_size_t offset = 0;
2595 ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL;
2596 ap_form_pair_t *pair = NULL;
2597 apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t));
2604 /* sanity check - we only support forms for now */
2605 ct = apr_table_get(r->headers_in, "Content-Type");
2606 if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
2607 return ap_discard_request_body(r);
2610 if (usize > APR_SIZE_MAX >> 1)
2611 size = APR_SIZE_MAX >> 1;
2616 f = r->input_filters;
2619 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2621 apr_bucket *bucket = NULL, *last = NULL;
2623 int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES,
2624 APR_BLOCK_READ, HUGE_STRING_LEN);
2625 if (rv != APR_SUCCESS) {
2626 apr_brigade_destroy(bb);
2627 return ap_map_http_request_error(rv, HTTP_BAD_REQUEST);
2630 for (bucket = APR_BRIGADE_FIRST(bb);
2631 bucket != APR_BRIGADE_SENTINEL(bb);
2632 last = bucket, bucket = APR_BUCKET_NEXT(bucket)) {
2634 apr_size_t len, slide;
2637 apr_bucket_delete(last);
2639 if (APR_BUCKET_IS_EOS(bucket)) {
2643 if (bucket->length == 0) {
2647 rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
2648 if (rv != APR_SUCCESS) {
2649 apr_brigade_destroy(bb);
2650 return HTTP_BAD_REQUEST;
2654 while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) {
2659 else if ('&' == c) {
2663 percent = FORM_PERCENTA;
2666 if (FORM_PERCENTA == percent) {
2670 else if (c >= 'A') {
2673 else if (c >= '0') {
2677 percent = FORM_PERCENTB;
2680 if (FORM_PERCENTB == percent) {
2684 else if (c >= 'A') {
2687 else if (c >= '0') {
2691 percent = FORM_NORMAL;
2696 const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2697 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2698 APR_BRIGADE_INSERT_TAIL(pair->value, b);
2706 if (offset < HUGE_STRING_LEN) {
2710 pair = (ap_form_pair_t *) apr_array_push(pairs);
2711 pair->name = apr_pstrdup(r->pool, buffer);
2712 pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
2716 buffer[offset++] = c;
2725 if (offset >= HUGE_STRING_LEN) {
2726 const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2727 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2728 APR_BRIGADE_INSERT_TAIL(pair->value, b);
2731 buffer[offset++] = c;
2741 apr_brigade_cleanup(bb);
2742 } while (!seen_eos);
2744 if (FORM_ABORT == state || size < 0 || num == 0) {
2745 return HTTP_REQUEST_ENTITY_TOO_LARGE;
2747 else if (FORM_VALUE == state && pair && offset > 0) {
2748 const char *tmp = apr_pmemdup(r->pool, buffer, offset);
2749 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc);
2750 APR_BRIGADE_INSERT_TAIL(pair->value, b);
2757 #define VARBUF_SMALL_SIZE 2048
2758 #define VARBUF_MAX_SIZE (APR_SIZE_MAX - 1 - \
2759 APR_ALIGN_DEFAULT(sizeof(struct ap_varbuf_info)))
2761 struct ap_varbuf_info {
2762 struct apr_memnode_t *node;
2763 apr_allocator_t *allocator;
2766 static apr_status_t varbuf_cleanup(void *info_)
2768 struct ap_varbuf_info *info = info_;
2769 info->node->next = NULL;
2770 apr_allocator_free(info->allocator, info->node);
2774 const char nul = '\0';
2775 static char * const varbuf_empty = (char *)&nul;
2777 AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
2778 apr_size_t init_size)
2780 vb->buf = varbuf_empty;
2782 vb->strlen = AP_VARBUF_UNKNOWN;
2786 ap_varbuf_grow(vb, init_size);
2789 AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
2791 apr_memnode_t *new_node = NULL;
2792 apr_allocator_t *allocator;
2793 struct ap_varbuf_info *new_info;
2796 AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN || vb->avail >= vb->strlen);
2798 if (new_len <= vb->avail)
2801 if (new_len < 2 * vb->avail && vb->avail < VARBUF_MAX_SIZE/2) {
2802 /* at least double the size, to avoid repeated reallocations */
2803 new_len = 2 * vb->avail;
2805 else if (new_len > VARBUF_MAX_SIZE) {
2806 apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
2807 ap_assert(abort_fn != NULL);
2808 abort_fn(APR_ENOMEM);
2812 new_len++; /* add space for trailing \0 */
2813 if (new_len <= VARBUF_SMALL_SIZE) {
2814 new_len = APR_ALIGN_DEFAULT(new_len);
2815 new = apr_palloc(vb->pool, new_len);
2816 if (vb->avail && vb->strlen != 0) {
2817 AP_DEBUG_ASSERT(vb->buf != NULL);
2818 AP_DEBUG_ASSERT(vb->buf != varbuf_empty);
2819 if (new == vb->buf + vb->avail + 1) {
2820 /* We are lucky: the new memory lies directly after our old
2821 * buffer, we can now use both.
2823 vb->avail += new_len;
2827 /* copy up to vb->strlen + 1 bytes */
2828 memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
2829 vb->avail + 1 : vb->strlen + 1);
2835 vb->avail = new_len - 1;
2840 /* The required block is rather larger. Use allocator directly so that
2841 * the memory can be freed independently from the pool. */
2842 allocator = apr_pool_allocator_get(vb->pool);
2843 if (new_len <= VARBUF_MAX_SIZE)
2844 new_node = apr_allocator_alloc(allocator,
2845 new_len + APR_ALIGN_DEFAULT(sizeof(*new_info)));
2847 apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool);
2848 ap_assert(abort_fn != NULL);
2849 abort_fn(APR_ENOMEM);
2852 new_info = (struct ap_varbuf_info *)new_node->first_avail;
2853 new_node->first_avail += APR_ALIGN_DEFAULT(sizeof(*new_info));
2854 new_info->node = new_node;
2855 new_info->allocator = allocator;
2856 new = new_node->first_avail;
2857 AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len);
2858 new_len = new_node->endp - new_node->first_avail;
2860 if (vb->avail && vb->strlen != 0)
2861 memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ?
2862 vb->avail + 1 : vb->strlen + 1);
2866 apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
2867 apr_pool_cleanup_register(vb->pool, new_info, varbuf_cleanup,
2868 apr_pool_cleanup_null);
2869 vb->info = new_info;
2871 vb->avail = new_len - 1;
2874 AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str,
2880 ap_varbuf_grow(vb, len);
2881 memcpy(vb->buf, str, len);
2882 vb->buf[len] = '\0';
2886 if (vb->strlen == AP_VARBUF_UNKNOWN)
2887 vb->strlen = strlen(vb->buf);
2888 ap_varbuf_grow(vb, vb->strlen + len);
2889 memcpy(vb->buf + vb->strlen, str, len);
2891 vb->buf[vb->strlen] = '\0';
2894 AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb)
2897 apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup);
2903 AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf,
2904 const char *prepend, apr_size_t prepend_len,
2905 const char *append, apr_size_t append_len,
2906 apr_size_t *new_len)
2909 struct iovec vec[3];
2912 vec[i].iov_base = (void *)prepend;
2913 vec[i].iov_len = prepend_len;
2916 if (buf->avail && buf->strlen) {
2917 if (buf->strlen == AP_VARBUF_UNKNOWN)
2918 buf->strlen = strlen(buf->buf);
2919 vec[i].iov_base = (void *)buf->buf;
2920 vec[i].iov_len = buf->strlen;
2924 vec[i].iov_base = (void *)append;
2925 vec[i].iov_len = append_len;
2929 return apr_pstrcatv(p, vec, i, new_len);
2936 AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
2940 ap_regmatch_t pmatch[],
2943 return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen);
2946 static const char * const oom_message = "[crit] Memory allocation failed, "
2947 "aborting process." APR_EOL_STR;
2949 AP_DECLARE(void) ap_abort_on_oom()
2951 int written, count = strlen(oom_message);
2952 const char *buf = oom_message;
2954 written = write(STDERR_FILENO, buf, count);
2955 if (written == count)
2961 } while (written >= 0 || errno == EINTR);
2965 AP_DECLARE(void *) ap_malloc(size_t size)
2967 void *p = malloc(size);
2968 if (p == NULL && size != 0)
2973 AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size)
2975 void *p = calloc(nelem, size);
2976 if (p == NULL && nelem != 0 && size != 0)
2981 AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
2983 void *p = realloc(ptr, size);
2984 if (p == NULL && size != 0)
2989 AP_DECLARE(void) ap_get_sload(ap_sload_t *ld)
2991 int i, j, server_limit, thread_limit;
2995 ap_generation_t mpm_generation;
2997 /* preload errored fields, we overwrite */
3000 ld->bytes_served = 0;
3001 ld->access_count = 0;
3003 ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation);
3004 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
3005 ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
3007 for (i = 0; i < server_limit; i++) {
3009 ps = ap_get_scoreboard_process(i);
3011 for (j = 0; j < thread_limit; j++) {
3013 worker_score *ws = NULL;
3014 ws = &ap_scoreboard_image->servers[i][j];
3017 if (!ps->quiescing && ps->pid) {
3018 if (res == SERVER_READY && ps->generation == mpm_generation) {
3021 else if (res != SERVER_DEAD &&
3022 res != SERVER_STARTING && res != SERVER_IDLE_KILL &&
3023 ps->generation == mpm_generation) {
3028 if (ap_extended_status && !ps->quiescing && ps->pid) {
3029 if (ws->access_count != 0
3030 || (res != SERVER_READY && res != SERVER_DEAD)) {
3031 ld->access_count += ws->access_count;
3032 ld->bytes_served += ws->bytes_served;
3037 total = busy + ready;
3039 ld->idle = ready * 100 / total;
3040 ld->busy = busy * 100 / total;
3044 AP_DECLARE(void) ap_get_loadavg(ap_loadavg_t *ld)
3046 /* preload errored fields, we overwrite */
3048 ld->loadavg5 = -1.0;
3049 ld->loadavg15 = -1.0;
3056 num = getloadavg(la, 3);
3058 ld->loadavg = (float)la[0];
3061 ld->loadavg5 = (float)la[1];
3064 ld->loadavg15 = (float)la[2];
3070 static const char * const pw_cache_note_name = "conn_cache_note";
3072 /* varbuf contains concatenated password and hash */
3073 struct ap_varbuf vb;
3075 apr_status_t result;
3078 AP_DECLARE(apr_status_t) ap_password_validate(request_rec *r,
3079 const char *username,
3083 struct pw_cache *cache;
3086 cache = (struct pw_cache *)apr_table_get(r->connection->notes, pw_cache_note_name);
3087 if (cache != NULL) {
3088 if (strncmp(passwd, cache->vb.buf, cache->pwlen) == 0
3089 && strcmp(hash, cache->vb.buf + cache->pwlen) == 0) {
3090 return cache->result;
3092 /* make ap_varbuf_grow below not copy the old data */
3093 cache->vb.strlen = 0;
3096 cache = apr_palloc(r->connection->pool, sizeof(struct pw_cache));
3097 ap_varbuf_init(r->connection->pool, &cache->vb, 0);
3098 apr_table_setn(r->connection->notes, pw_cache_note_name, (void *)cache);
3100 cache->pwlen = strlen(passwd);
3101 hashlen = strlen(hash);
3102 ap_varbuf_grow(&cache->vb, cache->pwlen + hashlen + 1);
3103 memcpy(cache->vb.buf, passwd, cache->pwlen);
3104 memcpy(cache->vb.buf + cache->pwlen, hash, hashlen + 1);
3105 cache->result = apr_password_validate(passwd, hash);
3106 return cache->result;
3109 AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p,
3111 const char * const * argv)
3113 char buf[MAX_STRING_LEN];
3114 apr_procattr_t *procattr;
3117 apr_size_t nbytes = 1;
3121 if (apr_procattr_create(&procattr, p) != APR_SUCCESS)
3123 if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK,
3124 APR_FULL_BLOCK) != APR_SUCCESS)
3126 if (apr_procattr_dir_set(procattr,
3127 ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS)
3129 if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS)
3131 proc = apr_pcalloc(p, sizeof(apr_proc_t));
3132 if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS)
3138 /* XXX: we are reading 1 byte at a time here */
3139 for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS
3140 && nbytes == 1 && (k < MAX_STRING_LEN-1) ; ) {
3141 if (c == '\n' || c == '\r')
3148 return apr_pstrndup(p, buf, k);
3151 AP_DECLARE(int) ap_array_str_index(const apr_array_header_t *array,
3158 for (i = start; i < array->nelts; i++) {
3159 const char *p = APR_ARRAY_IDX(array, i, const char *);
3160 if (!strcmp(p, s)) {
3169 AP_DECLARE(int) ap_array_str_contains(const apr_array_header_t *array,
3172 return (ap_array_str_index(array, s, 0) >= 0);
3176 * Provide our own known-fast implementation of str[n]casecmp()
3179 static const unsigned char ucharmap[] = {
3180 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
3181 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
3182 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
3183 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
3184 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
3185 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
3186 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
3187 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
3188 0x40, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
3189 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
3190 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
3191 'x', 'y', 'z', 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
3192 0x60, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
3193 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
3194 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
3195 'x', 'y', 'z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
3196 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
3197 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
3198 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
3199 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
3200 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
3201 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
3202 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
3203 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
3204 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
3205 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
3206 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
3207 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
3208 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
3209 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
3210 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
3211 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
3214 AP_DECLARE(int) ap_strcasecmp(const char *s1, const char *s2)
3216 const unsigned char *ps1 = (const unsigned char *) s1;
3217 const unsigned char *ps2 = (const unsigned char *) s2;
3219 while (ucharmap[*ps1] == ucharmap[*ps2++]) {
3220 if (*ps1++ == '\0') {
3224 return (ucharmap[*ps1] - ucharmap[*--ps2]);
3227 AP_DECLARE(int) ap_strncasecmp(const char *s1, const char *s2, apr_size_t n)
3229 const unsigned char *ps1 = (const unsigned char *) s1;
3230 const unsigned char *ps2 = (const unsigned char *) s2;
3232 if (ucharmap[*ps1] != ucharmap[*ps2++]) {
3233 return (ucharmap[*ps1] - ucharmap[*--ps2]);
3235 if (*ps1++ == '\0') {