switch to a 60 bit hash
[httpd-crcsyncproxy.git] / server / util.c
blobd476e54dbb18da635b81e1176a67f173c6e5b755
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
20 * 3/21/93 Rob McCool
21 * 1995-96 Many changes by the Apache Software Foundation
25 /* Debugging aid:
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
30 #include "apr.h"
31 #include "apr_strings.h"
32 #include "apr_lib.h"
34 #define APR_WANT_STDIO
35 #define APR_WANT_STRFUNC
36 #include "apr_want.h"
38 #if APR_HAVE_UNISTD_H
39 #include <unistd.h>
40 #endif
41 #if APR_HAVE_NETDB_H
42 #include <netdb.h> /* for gethostbyname() */
43 #endif
45 #include "ap_config.h"
46 #include "apr_base64.h"
47 #include "httpd.h"
48 #include "http_main.h"
49 #include "http_log.h"
50 #include "http_protocol.h"
51 #include "http_config.h"
52 #include "util_ebcdic.h"
54 #ifdef HAVE_PWD_H
55 #include <pwd.h>
56 #endif
57 #ifdef HAVE_GRP_H
58 #include <grp.h>
59 #endif
61 /* A bunch of functions in util.c scan strings looking for certain characters.
62 * To make that more efficient we encode a lookup table. The test_char_table
63 * is generated automatically by gen_test_char.c.
65 #include "test_char.h"
67 /* we assume the folks using this ensure 0 <= c < 256... which means
68 * you need a cast to (unsigned char) first, you can't just plug a
69 * char in here and get it to work, because if char is signed then it
70 * will first be sign extended.
72 #define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f))
74 /* Win32/NetWare/OS2 need to check for both forward and back slashes
75 * in ap_getparents() and ap_escape_url.
77 #ifdef CASE_BLIND_FILESYSTEM
78 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
79 #else
80 #define IS_SLASH(s) (s == '/')
81 #endif
85 * Examine a field value (such as a media-/content-type) string and return
86 * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
88 AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
90 const char *semi;
92 if (intype == NULL) return NULL;
94 semi = ap_strchr_c(intype, ';');
95 if (semi == NULL) {
96 return apr_pstrdup(p, intype);
98 else {
99 while ((semi > intype) && apr_isspace(semi[-1])) {
100 semi--;
102 return apr_pstrndup(p, intype, semi - intype);
106 AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
107 int gmt)
109 apr_size_t retcode;
110 char ts[MAX_STRING_LEN];
111 char tf[MAX_STRING_LEN];
112 apr_time_exp_t xt;
114 if (gmt) {
115 const char *f;
116 char *strp;
118 apr_time_exp_gmt(&xt, t);
119 /* Convert %Z to "GMT" and %z to "+0000";
120 * on hosts that do not have a time zone string in struct tm,
121 * strftime must assume its argument is local time.
123 for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
124 ; f++, strp++) {
125 if (*f != '%') continue;
126 switch (f[1]) {
127 case '%':
128 *++strp = *++f;
129 break;
130 case 'Z':
131 *strp++ = 'G';
132 *strp++ = 'M';
133 *strp = 'T';
134 f++;
135 break;
136 case 'z': /* common extension */
137 *strp++ = '+';
138 *strp++ = '0';
139 *strp++ = '0';
140 *strp++ = '0';
141 *strp = '0';
142 f++;
143 break;
146 *strp = '\0';
147 fmt = tf;
149 else {
150 apr_time_exp_lt(&xt, t);
153 /* check return code? */
154 apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt);
155 ts[MAX_STRING_LEN - 1] = '\0';
156 return apr_pstrdup(p, ts);
159 /* Roy owes Rob beer. */
160 /* Rob owes Roy dinner. */
162 /* These legacy comments would make a lot more sense if Roy hadn't
163 * replaced the old later_than() routine with util_date.c.
165 * Well, okay, they still wouldn't make any sense.
168 /* Match = 0, NoMatch = 1, Abort = -1
169 * Based loosely on sections of wildmat.c by Rich Salz
170 * Hmmm... shouldn't this really go component by component?
172 AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
174 int x, y;
176 for (x = 0, y = 0; expected[y]; ++y, ++x) {
177 if ((!str[x]) && (expected[y] != '*'))
178 return -1;
179 if (expected[y] == '*') {
180 while (expected[++y] == '*');
181 if (!expected[y])
182 return 0;
183 while (str[x]) {
184 int ret;
185 if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1)
186 return ret;
188 return -1;
190 else if ((expected[y] != '?') && (str[x] != expected[y]))
191 return 1;
193 return (str[x] != '\0');
196 AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
198 int x, y;
200 for (x = 0, y = 0; expected[y]; ++y, ++x) {
201 if (!str[x] && expected[y] != '*')
202 return -1;
203 if (expected[y] == '*') {
204 while (expected[++y] == '*');
205 if (!expected[y])
206 return 0;
207 while (str[x]) {
208 int ret;
209 if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1)
210 return ret;
212 return -1;
214 else if (expected[y] != '?'
215 && apr_tolower(str[x]) != apr_tolower(expected[y]))
216 return 1;
218 return (str[x] != '\0');
221 /* We actually compare the canonical root to this root, (but we don't
222 * waste time checking the case), since every use of this function in
223 * httpd-2.1 tests if the path is 'proper', meaning we've already passed
224 * it through apr_filepath_merge, or we haven't.
226 AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
228 const char *newpath;
229 const char *ourdir = dir;
230 if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS
231 || strncmp(newpath, ourdir, strlen(newpath)) != 0) {
232 return 0;
234 return 1;
237 AP_DECLARE(int) ap_is_matchexp(const char *str)
239 register int x;
241 for (x = 0; str[x]; x++)
242 if ((str[x] == '*') || (str[x] == '?'))
243 return 1;
244 return 0;
248 * Here's a pool-based interface to the POSIX-esque ap_regcomp().
249 * Note that we return ap_regex_t instead of being passed one.
250 * The reason is that if you use an already-used ap_regex_t structure,
251 * the memory that you've already allocated gets forgotten, and
252 * regfree() doesn't clear it. So we don't allow it.
255 static apr_status_t regex_cleanup(void *preg)
257 ap_regfree((ap_regex_t *) preg);
258 return APR_SUCCESS;
261 AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
262 int cflags)
264 ap_regex_t *preg = apr_palloc(p, sizeof *preg);
266 if (ap_regcomp(preg, pattern, cflags)) {
267 return NULL;
270 apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
271 apr_pool_cleanup_null);
273 return preg;
276 AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
278 ap_regfree(reg);
279 apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
283 * Similar to standard strstr() but we ignore case in this version.
284 * Based on the strstr() implementation further below.
286 AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
288 char *p1, *p2;
289 if (*s2 == '\0') {
290 /* an empty s2 */
291 return((char *)s1);
293 while(1) {
294 for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
295 if (*s1 == '\0') {
296 return(NULL);
298 /* found first character of s2, see if the rest matches */
299 p1 = (char *)s1;
300 p2 = (char *)s2;
301 for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) {
302 if (*p1 == '\0') {
303 /* both strings ended together */
304 return((char *)s1);
307 if (*p2 == '\0') {
308 /* second string ended, a match */
309 break;
311 /* didn't find a match here, try starting at next character in s1 */
312 s1++;
314 return((char *)s1);
318 * Returns an offsetted pointer in bigstring immediately after
319 * prefix. Returns bigstring if bigstring doesn't start with
320 * prefix or if prefix is longer than bigstring while still matching.
321 * NOTE: pointer returned is relative to bigstring, so we
322 * can use standard pointer comparisons in the calling function
323 * (eg: test if ap_stripprefix(a,b) == a)
325 AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
326 const char *prefix)
328 const char *p1;
330 if (*prefix == '\0')
331 return bigstring;
333 p1 = bigstring;
334 while (*p1 && *prefix) {
335 if (*p1++ != *prefix++)
336 return bigstring;
338 if (*prefix == '\0')
339 return p1;
341 /* hit the end of bigstring! */
342 return bigstring;
345 /* This function substitutes for $0-$9, filling in regular expression
346 * submatches. Pass it the same nmatch and pmatch arguments that you
347 * passed ap_regexec(). pmatch should not be greater than the maximum number
348 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t.
350 * input should be the string with the $-expressions, source should be the
351 * string that was matched against.
353 * It returns the substituted string, or NULL on error.
355 * Parts of this code are based on Henry Spencer's regsub(), from his
356 * AT&T V8 regexp package.
359 AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input,
360 const char *source, size_t nmatch,
361 ap_regmatch_t pmatch[])
363 const char *src = input;
364 char *dest, *dst;
365 char c;
366 size_t no;
367 int len;
369 if (!source)
370 return NULL;
371 if (!nmatch)
372 return apr_pstrdup(p, src);
374 /* First pass, find the size */
376 len = 0;
378 while ((c = *src++) != '\0') {
379 if (c == '&')
380 no = 0;
381 else if (c == '$' && apr_isdigit(*src))
382 no = *src++ - '0';
383 else
384 no = 10;
386 if (no > 9) { /* Ordinary character. */
387 if (c == '\\' && (*src == '$' || *src == '&'))
388 c = *src++;
389 len++;
391 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
392 len += pmatch[no].rm_eo - pmatch[no].rm_so;
397 dest = dst = apr_pcalloc(p, len + 1);
399 /* Now actually fill in the string */
401 src = input;
403 while ((c = *src++) != '\0') {
404 if (c == '&')
405 no = 0;
406 else if (c == '$' && apr_isdigit(*src))
407 no = *src++ - '0';
408 else
409 no = 10;
411 if (no > 9) { /* Ordinary character. */
412 if (c == '\\' && (*src == '$' || *src == '&'))
413 c = *src++;
414 *dst++ = c;
416 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
417 len = pmatch[no].rm_eo - pmatch[no].rm_so;
418 memcpy(dst, source + pmatch[no].rm_so, len);
419 dst += len;
423 *dst = '\0';
425 return dest;
429 * Parse .. so we don't compromise security
431 AP_DECLARE(void) ap_getparents(char *name)
433 char *next;
434 int l, w, first_dot;
436 /* Four paseses, as per RFC 1808 */
437 /* a) remove ./ path segments */
438 for (next = name; *next && (*next != '.'); next++) {
441 l = w = first_dot = next - name;
442 while (name[l] != '\0') {
443 if (name[l] == '.' && IS_SLASH(name[l + 1])
444 && (l == 0 || IS_SLASH(name[l - 1])))
445 l += 2;
446 else
447 name[w++] = name[l++];
450 /* b) remove trailing . path, segment */
451 if (w == 1 && name[0] == '.')
452 w--;
453 else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
454 w--;
455 name[w] = '\0';
457 /* c) remove all xx/../ segments. (including leading ../ and /../) */
458 l = first_dot;
460 while (name[l] != '\0') {
461 if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
462 && (l == 0 || IS_SLASH(name[l - 1]))) {
463 register int m = l + 3, n;
465 l = l - 2;
466 if (l >= 0) {
467 while (l >= 0 && !IS_SLASH(name[l]))
468 l--;
469 l++;
471 else
472 l = 0;
473 n = l;
474 while ((name[n] = name[m]))
475 (++n, ++m);
477 else
478 ++l;
481 /* d) remove trailing xx/.. segment. */
482 if (l == 2 && name[0] == '.' && name[1] == '.')
483 name[0] = '\0';
484 else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
485 && IS_SLASH(name[l - 3])) {
486 l = l - 4;
487 if (l >= 0) {
488 while (l >= 0 && !IS_SLASH(name[l]))
489 l--;
490 l++;
492 else
493 l = 0;
494 name[l] = '\0';
498 AP_DECLARE(void) ap_no2slash(char *name)
500 char *d, *s;
502 s = d = name;
504 #ifdef HAVE_UNC_PATHS
505 /* Check for UNC names. Leave leading two slashes. */
506 if (s[0] == '/' && s[1] == '/')
507 *d++ = *s++;
508 #endif
510 while (*s) {
511 if ((*d++ = *s) == '/') {
512 do {
513 ++s;
514 } while (*s == '/');
516 else {
517 ++s;
520 *d = '\0';
525 * copy at most n leading directories of s into d
526 * d should be at least as large as s plus 1 extra byte
527 * assumes n > 0
528 * the return value is the ever useful pointer to the trailing \0 of d
530 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
531 * so that if n == 0, "/" is returned in d with n == 1
532 * and s == "e:/test.html", "e:/" is returned in d
533 * *** See also directory_walk in modules/http/http_request.c
535 * examples:
536 * /a/b, 0 ==> / (true for all platforms)
537 * /a/b, 1 ==> /
538 * /a/b, 2 ==> /a/
539 * /a/b, 3 ==> /a/b/
540 * /a/b, 4 ==> /a/b/
542 * c:/a/b 0 ==> /
543 * c:/a/b 1 ==> c:/
544 * c:/a/b 2 ==> c:/a/
545 * c:/a/b 3 ==> c:/a/b
546 * c:/a/b 4 ==> c:/a/b
548 AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
550 if (n < 1) {
551 *d = '/';
552 *++d = '\0';
553 return (d);
556 for (;;) {
557 if (*s == '\0' || (*s == '/' && (--n) == 0)) {
558 *d = '/';
559 break;
561 *d++ = *s++;
563 *++d = 0;
564 return (d);
569 * return the parent directory name including trailing / of the file s
571 AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
573 const char *last_slash = ap_strrchr_c(s, '/');
574 char *d;
575 int l;
577 if (last_slash == NULL) {
578 return apr_pstrdup(p, "");
580 l = (last_slash - s) + 1;
581 d = apr_pstrmemdup(p, s, l);
583 return (d);
587 AP_DECLARE(int) ap_count_dirs(const char *path)
589 register int x, n;
591 for (x = 0, n = 0; path[x]; x++)
592 if (path[x] == '/')
593 n++;
594 return n;
597 AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
599 return ap_getword(atrans, (const char **) line, stop);
602 AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
604 const char *pos = *line;
605 int len;
606 char *res;
608 while ((*pos != stop) && *pos) {
609 ++pos;
612 len = pos - *line;
613 res = apr_pstrmemdup(atrans, *line, len);
615 if (stop) {
616 while (*pos == stop) {
617 ++pos;
620 *line = pos;
622 return res;
625 AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
627 return ap_getword_white(atrans, (const char **) line);
630 AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
632 const char *pos = *line;
633 int len;
634 char *res;
636 while (!apr_isspace(*pos) && *pos) {
637 ++pos;
640 len = pos - *line;
641 res = apr_pstrmemdup(atrans, *line, len);
643 while (apr_isspace(*pos)) {
644 ++pos;
647 *line = pos;
649 return res;
652 AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
653 char stop)
655 return ap_getword_nulls(atrans, (const char **) line, stop);
658 AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
659 char stop)
661 const char *pos = ap_strchr_c(*line, stop);
662 char *res;
664 if (!pos) {
665 res = apr_pstrdup(atrans, *line);
666 *line += strlen(*line);
667 return res;
670 res = apr_pstrndup(atrans, *line, pos - *line);
672 ++pos;
674 *line = pos;
676 return res;
679 /* Get a word, (new) config-file style --- quoted strings and backslashes
680 * all honored
683 static char *substring_conf(apr_pool_t *p, const char *start, int len,
684 char quote)
686 char *result = apr_palloc(p, len + 2);
687 char *resp = result;
688 int i;
690 for (i = 0; i < len; ++i) {
691 if (start[i] == '\\' && (start[i + 1] == '\\'
692 || (quote && start[i + 1] == quote)))
693 *resp++ = start[++i];
694 else
695 *resp++ = start[i];
698 *resp++ = '\0';
699 #if RESOLVE_ENV_PER_TOKEN
700 return (char *)ap_resolve_env(p,result);
701 #else
702 return result;
703 #endif
706 AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
708 return ap_getword_conf(p, (const char **) line);
711 AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
713 const char *str = *line, *strend;
714 char *res;
715 char quote;
717 while (*str && apr_isspace(*str))
718 ++str;
720 if (!*str) {
721 *line = str;
722 return "";
725 if ((quote = *str) == '"' || quote == '\'') {
726 strend = str + 1;
727 while (*strend && *strend != quote) {
728 if (*strend == '\\' && strend[1] &&
729 (strend[1] == quote || strend[1] == '\\')) {
730 strend += 2;
732 else {
733 ++strend;
736 res = substring_conf(p, str + 1, strend - str - 1, quote);
738 if (*strend == quote)
739 ++strend;
741 else {
742 strend = str;
743 while (*strend && !apr_isspace(*strend))
744 ++strend;
746 res = substring_conf(p, str, strend - str, 0);
749 while (*strend && apr_isspace(*strend))
750 ++strend;
751 *line = strend;
752 return res;
755 /* Check a string for any ${ENV} environment variable
756 * construct and replace each them by the value of
757 * that environment variable, if it exists. If the
758 * environment value does not exist, leave the ${ENV}
759 * construct alone; it means something else.
761 AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
763 # define SMALL_EXPANSION 5
764 struct sll {
765 struct sll *next;
766 const char *string;
767 apr_size_t len;
768 } *result, *current, sresult[SMALL_EXPANSION];
769 char *res_buf, *cp;
770 const char *s, *e, *ep;
771 unsigned spc;
772 apr_size_t outlen;
774 s = ap_strchr_c(word, '$');
775 if (!s) {
776 return word;
779 /* well, actually something to do */
780 ep = word + strlen(word);
781 spc = 0;
782 result = current = &(sresult[spc++]);
783 current->next = NULL;
784 current->string = word;
785 current->len = s - word;
786 outlen = current->len;
788 do {
789 /* prepare next entry */
790 if (current->len) {
791 current->next = (spc < SMALL_EXPANSION)
792 ? &(sresult[spc++])
793 : (struct sll *)apr_palloc(p,
794 sizeof(*current->next));
795 current = current->next;
796 current->next = NULL;
797 current->len = 0;
800 if (*s == '$') {
801 if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) {
802 word = getenv(apr_pstrndup(p, s+2, e-s-2));
803 if (word) {
804 current->string = word;
805 current->len = strlen(word);
806 outlen += current->len;
808 else {
809 current->string = s;
810 current->len = e - s + 1;
811 outlen += current->len;
813 s = e + 1;
815 else {
816 current->string = s++;
817 current->len = 1;
818 ++outlen;
821 else {
822 word = s;
823 s = ap_strchr_c(s, '$');
824 current->string = word;
825 current->len = s ? s - word : ep - word;
826 outlen += current->len;
828 } while (s && *s);
830 /* assemble result */
831 res_buf = cp = apr_palloc(p, outlen + 1);
832 do {
833 if (result->len) {
834 memcpy(cp, result->string, result->len);
835 cp += result->len;
837 result = result->next;
838 } while (result);
839 res_buf[outlen] = '\0';
841 return res_buf;
844 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
846 #ifdef DEBUG
847 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
848 "Done with config file %s", cfp->name);
849 #endif
850 return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
853 static apr_status_t cfg_close(void *param)
855 apr_file_t *cfp = (apr_file_t *) param;
856 return (apr_file_close(cfp));
859 static int cfg_getch(void *param)
861 char ch;
862 apr_file_t *cfp = (apr_file_t *) param;
863 if (apr_file_getc(&ch, cfp) == APR_SUCCESS)
864 return ch;
865 return (int)EOF;
868 static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
870 apr_file_t *cfp = (apr_file_t *) param;
871 apr_status_t rv;
872 rv = apr_file_gets(buf, bufsiz, cfp);
873 if (rv == APR_SUCCESS) {
874 return buf;
876 return NULL;
879 /* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */
880 AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
881 apr_pool_t *p, const char *name)
883 ap_configfile_t *new_cfg;
884 apr_file_t *file = NULL;
885 apr_finfo_t finfo;
886 apr_status_t status;
887 #ifdef DEBUG
888 char buf[120];
889 #endif
891 if (name == NULL) {
892 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
893 "Internal error: pcfg_openfile() called with NULL filename");
894 return APR_EBADF;
897 status = apr_file_open(&file, name, APR_READ | APR_BUFFERED,
898 APR_OS_DEFAULT, p);
899 #ifdef DEBUG
900 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
901 "Opening config file %s (%s)",
902 name, (status != APR_SUCCESS) ?
903 apr_strerror(status, buf, sizeof(buf)) : "successful");
904 #endif
905 if (status != APR_SUCCESS)
906 return status;
908 status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
909 if (status != APR_SUCCESS)
910 return status;
912 if (finfo.filetype != APR_REG &&
913 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
914 strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
915 #else
916 strcmp(name, "/dev/null") != 0) {
917 #endif /* WIN32 || OS2 */
918 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
919 "Access to file %s denied by server: not a regular file",
920 name);
921 apr_file_close(file);
922 return APR_EBADF;
925 #ifdef WIN32
926 /* Some twisted character [no pun intended] at MS decided that a
927 * zero width joiner as the lead wide character would be ideal for
928 * describing Unicode text files. This was further convoluted to
929 * another MSism that the same character mapped into utf-8, EF BB BF
930 * would signify utf-8 text files.
932 * Since MS configuration files are all protecting utf-8 encoded
933 * Unicode path, file and resource names, we already have the correct
934 * WinNT encoding. But at least eat the stupid three bytes up front.
937 unsigned char buf[4];
938 apr_size_t len = 3;
939 status = apr_file_read(file, buf, &len);
940 if ((status != APR_SUCCESS) || (len < 3)
941 || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
942 apr_off_t zero = 0;
943 apr_file_seek(file, APR_SET, &zero);
946 #endif
948 new_cfg = apr_palloc(p, sizeof(*new_cfg));
949 new_cfg->param = file;
950 new_cfg->name = apr_pstrdup(p, name);
951 new_cfg->getch = (int (*)(void *)) cfg_getch;
952 new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
953 new_cfg->close = (int (*)(void *)) cfg_close;
954 new_cfg->line_number = 0;
955 *ret_cfg = new_cfg;
956 return APR_SUCCESS;
960 /* Allocate a ap_configfile_t handle with user defined functions and params */
961 AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p,
962 const char *descr,
963 void *param,
964 int(*getch)(void *param),
965 void *(*getstr) (void *buf, size_t bufsiz, void *param),
966 int(*close_func)(void *param))
968 ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg));
969 #ifdef DEBUG
970 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
971 "Opening config handler %s", descr);
972 #endif
973 new_cfg->param = param;
974 new_cfg->name = descr;
975 new_cfg->getch = getch;
976 new_cfg->getstr = getstr;
977 new_cfg->close = close_func;
978 new_cfg->line_number = 0;
979 return new_cfg;
982 /* Read one character from a configfile_t */
983 AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
985 register int ch = cfp->getch(cfp->param);
986 if (ch == LF)
987 ++cfp->line_number;
988 return ch;
991 /* Read one line from open ap_configfile_t, strip LF, increase line number */
992 /* If custom handler does not define a getstr() function, read char by char */
993 AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp)
995 /* If a "get string" function is defined, use it */
996 if (cfp->getstr != NULL) {
997 char *src, *dst;
998 char *cp;
999 char *cbuf = buf;
1000 size_t cbufsize = bufsize;
1002 while (1) {
1003 ++cfp->line_number;
1004 if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
1005 return 1;
1008 * check for line continuation,
1009 * i.e. match [^\\]\\[\r]\n only
1011 cp = cbuf;
1012 while (cp < cbuf+cbufsize && *cp != '\0')
1013 cp++;
1014 if (cp > cbuf && cp[-1] == LF) {
1015 cp--;
1016 if (cp > cbuf && cp[-1] == CR)
1017 cp--;
1018 if (cp > cbuf && cp[-1] == '\\') {
1019 cp--;
1020 if (!(cp > cbuf && cp[-1] == '\\')) {
1022 * line continuation requested -
1023 * then remove backslash and continue
1025 cbufsize -= (cp-cbuf);
1026 cbuf = cp;
1027 continue;
1029 else {
1031 * no real continuation because escaped -
1032 * then just remove escape character
1034 for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
1035 cp[0] = cp[1];
1039 break;
1043 * Leading and trailing white space is eliminated completely
1045 src = buf;
1046 while (apr_isspace(*src))
1047 ++src;
1048 /* blast trailing whitespace */
1049 dst = &src[strlen(src)];
1050 while (--dst >= src && apr_isspace(*dst))
1051 *dst = '\0';
1052 /* Zap leading whitespace by shifting */
1053 if (src != buf)
1054 for (dst = buf; (*dst++ = *src++) != '\0'; )
1057 #ifdef DEBUG_CFG_LINES
1058 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf);
1059 #endif
1060 return 0;
1061 } else {
1062 /* No "get string" function defined; read character by character */
1063 register int c;
1064 register size_t i = 0;
1066 buf[0] = '\0';
1067 /* skip leading whitespace */
1068 do {
1069 c = cfp->getch(cfp->param);
1070 } while (c == '\t' || c == ' ');
1072 if (c == EOF)
1073 return 1;
1075 if(bufsize < 2) {
1076 /* too small, assume caller is crazy */
1077 return 1;
1080 while (1) {
1081 if ((c == '\t') || (c == ' ')) {
1082 buf[i++] = ' ';
1083 while ((c == '\t') || (c == ' '))
1084 c = cfp->getch(cfp->param);
1086 if (c == CR) {
1087 /* silently ignore CR (_assume_ that a LF follows) */
1088 c = cfp->getch(cfp->param);
1090 if (c == LF) {
1091 /* increase line number and return on LF */
1092 ++cfp->line_number;
1094 if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
1096 * check for line continuation
1098 if (i > 0 && buf[i-1] == '\\') {
1099 i--;
1100 if (!(i > 0 && buf[i-1] == '\\')) {
1101 /* line is continued */
1102 c = cfp->getch(cfp->param);
1103 continue;
1105 /* else nothing needs be done because
1106 * then the backslash is escaped and
1107 * we just strip to a single one
1110 /* blast trailing whitespace */
1111 while (i > 0 && apr_isspace(buf[i - 1]))
1112 --i;
1113 buf[i] = '\0';
1114 #ifdef DEBUG_CFG_LINES
1115 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1116 "Read config: %s", buf);
1117 #endif
1118 return 0;
1120 buf[i] = c;
1121 ++i;
1122 c = cfp->getch(cfp->param);
1127 /* Size an HTTP header field list item, as separated by a comma.
1128 * The return value is a pointer to the beginning of the non-empty list item
1129 * within the original string (or NULL if there is none) and the address
1130 * of field is shifted to the next non-comma, non-whitespace character.
1131 * len is the length of the item excluding any beginning whitespace.
1133 AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
1135 const unsigned char *ptr = (const unsigned char *)*field;
1136 const unsigned char *token;
1137 int in_qpair, in_qstr, in_com;
1139 /* Find first non-comma, non-whitespace byte */
1141 while (*ptr == ',' || apr_isspace(*ptr))
1142 ++ptr;
1144 token = ptr;
1146 /* Find the end of this item, skipping over dead bits */
1148 for (in_qpair = in_qstr = in_com = 0;
1149 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1150 ++ptr) {
1152 if (in_qpair) {
1153 in_qpair = 0;
1155 else {
1156 switch (*ptr) {
1157 case '\\': in_qpair = 1; /* quoted-pair */
1158 break;
1159 case '"' : if (!in_com) /* quoted string delim */
1160 in_qstr = !in_qstr;
1161 break;
1162 case '(' : if (!in_qstr) /* comment (may nest) */
1163 ++in_com;
1164 break;
1165 case ')' : if (in_com) /* end comment */
1166 --in_com;
1167 break;
1168 default : break;
1173 if ((*len = (ptr - token)) == 0) {
1174 *field = (const char *)ptr;
1175 return NULL;
1178 /* Advance field pointer to the next non-comma, non-white byte */
1180 while (*ptr == ',' || apr_isspace(*ptr))
1181 ++ptr;
1183 *field = (const char *)ptr;
1184 return (const char *)token;
1187 /* Retrieve an HTTP header field list item, as separated by a comma,
1188 * while stripping insignificant whitespace and lowercasing anything not in
1189 * a quoted string or comment. The return value is a new string containing
1190 * the converted list item (or NULL if none) and the address pointed to by
1191 * field is shifted to the next non-comma, non-whitespace.
1193 AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
1195 const char *tok_start;
1196 const unsigned char *ptr;
1197 unsigned char *pos;
1198 char *token;
1199 int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
1201 /* Find the beginning and maximum length of the list item so that
1202 * we can allocate a buffer for the new string and reset the field.
1204 if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1205 return NULL;
1207 token = apr_palloc(p, tok_len + 1);
1209 /* Scan the token again, but this time copy only the good bytes.
1210 * We skip extra whitespace and any whitespace around a '=', '/',
1211 * or ';' and lowercase normal characters not within a comment,
1212 * quoted-string or quoted-pair.
1214 for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1215 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1216 ++ptr) {
1218 if (in_qpair) {
1219 in_qpair = 0;
1220 *pos++ = *ptr;
1222 else {
1223 switch (*ptr) {
1224 case '\\': in_qpair = 1;
1225 if (addspace == 1)
1226 *pos++ = ' ';
1227 *pos++ = *ptr;
1228 addspace = 0;
1229 break;
1230 case '"' : if (!in_com)
1231 in_qstr = !in_qstr;
1232 if (addspace == 1)
1233 *pos++ = ' ';
1234 *pos++ = *ptr;
1235 addspace = 0;
1236 break;
1237 case '(' : if (!in_qstr)
1238 ++in_com;
1239 if (addspace == 1)
1240 *pos++ = ' ';
1241 *pos++ = *ptr;
1242 addspace = 0;
1243 break;
1244 case ')' : if (in_com)
1245 --in_com;
1246 *pos++ = *ptr;
1247 addspace = 0;
1248 break;
1249 case ' ' :
1250 case '\t': if (addspace)
1251 break;
1252 if (in_com || in_qstr)
1253 *pos++ = *ptr;
1254 else
1255 addspace = 1;
1256 break;
1257 case '=' :
1258 case '/' :
1259 case ';' : if (!(in_com || in_qstr))
1260 addspace = -1;
1261 *pos++ = *ptr;
1262 break;
1263 default : if (addspace == 1)
1264 *pos++ = ' ';
1265 *pos++ = (in_com || in_qstr) ? *ptr
1266 : apr_tolower(*ptr);
1267 addspace = 0;
1268 break;
1272 *pos = '\0';
1274 return token;
1277 /* Find an item in canonical form (lowercase, no extra spaces) within
1278 * an HTTP field value list. Returns 1 if found, 0 if not found.
1279 * This would be much more efficient if we stored header fields as
1280 * an array of list items as they are received instead of a plain string.
1282 AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
1283 const char *tok)
1285 const unsigned char *pos;
1286 const unsigned char *ptr = (const unsigned char *)line;
1287 int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
1289 if (!line || !tok)
1290 return 0;
1292 do { /* loop for each item in line's list */
1294 /* Find first non-comma, non-whitespace byte */
1296 while (*ptr == ',' || apr_isspace(*ptr))
1297 ++ptr;
1299 if (*ptr)
1300 good = 1; /* until proven otherwise for this item */
1301 else
1302 break; /* no items left and nothing good found */
1304 /* We skip extra whitespace and any whitespace around a '=', '/',
1305 * or ';' and lowercase normal characters not within a comment,
1306 * quoted-string or quoted-pair.
1308 for (pos = (const unsigned char *)tok;
1309 *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1310 ++ptr) {
1312 if (in_qpair) {
1313 in_qpair = 0;
1314 if (good)
1315 good = (*pos++ == *ptr);
1317 else {
1318 switch (*ptr) {
1319 case '\\': in_qpair = 1;
1320 if (addspace == 1)
1321 good = good && (*pos++ == ' ');
1322 good = good && (*pos++ == *ptr);
1323 addspace = 0;
1324 break;
1325 case '"' : if (!in_com)
1326 in_qstr = !in_qstr;
1327 if (addspace == 1)
1328 good = good && (*pos++ == ' ');
1329 good = good && (*pos++ == *ptr);
1330 addspace = 0;
1331 break;
1332 case '(' : if (!in_qstr)
1333 ++in_com;
1334 if (addspace == 1)
1335 good = good && (*pos++ == ' ');
1336 good = good && (*pos++ == *ptr);
1337 addspace = 0;
1338 break;
1339 case ')' : if (in_com)
1340 --in_com;
1341 good = good && (*pos++ == *ptr);
1342 addspace = 0;
1343 break;
1344 case ' ' :
1345 case '\t': if (addspace || !good)
1346 break;
1347 if (in_com || in_qstr)
1348 good = (*pos++ == *ptr);
1349 else
1350 addspace = 1;
1351 break;
1352 case '=' :
1353 case '/' :
1354 case ';' : if (!(in_com || in_qstr))
1355 addspace = -1;
1356 good = good && (*pos++ == *ptr);
1357 break;
1358 default : if (!good)
1359 break;
1360 if (addspace == 1)
1361 good = (*pos++ == ' ');
1362 if (in_com || in_qstr)
1363 good = good && (*pos++ == *ptr);
1364 else
1365 good = good && (*pos++ == apr_tolower(*ptr));
1366 addspace = 0;
1367 break;
1371 if (good && *pos)
1372 good = 0; /* not good if only a prefix was matched */
1374 } while (*ptr && !good);
1376 return good;
1380 /* Retrieve a token, spacing over it and returning a pointer to
1381 * the first non-white byte afterwards. Note that these tokens
1382 * are delimited by semis and commas; and can also be delimited
1383 * by whitespace at the caller's option.
1386 AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
1387 int accept_white)
1389 const char *ptr = *accept_line;
1390 const char *tok_start;
1391 char *token;
1392 int tok_len;
1394 /* Find first non-white byte */
1396 while (*ptr && apr_isspace(*ptr))
1397 ++ptr;
1399 tok_start = ptr;
1401 /* find token end, skipping over quoted strings.
1402 * (comments are already gone).
1405 while (*ptr && (accept_white || !apr_isspace(*ptr))
1406 && *ptr != ';' && *ptr != ',') {
1407 if (*ptr++ == '"')
1408 while (*ptr)
1409 if (*ptr++ == '"')
1410 break;
1413 tok_len = ptr - tok_start;
1414 token = apr_pstrndup(p, tok_start, tok_len);
1416 /* Advance accept_line pointer to the next non-white byte */
1418 while (*ptr && apr_isspace(*ptr))
1419 ++ptr;
1421 *accept_line = ptr;
1422 return token;
1426 /* find http tokens, see the definition of token from RFC2068 */
1427 AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
1429 const unsigned char *start_token;
1430 const unsigned char *s;
1432 if (!line)
1433 return 0;
1435 s = (const unsigned char *)line;
1436 for (;;) {
1437 /* find start of token, skip all stop characters, note NUL
1438 * isn't a token stop, so we don't need to test for it
1440 while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1441 ++s;
1443 if (!*s) {
1444 return 0;
1446 start_token = s;
1447 /* find end of the token */
1448 while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1449 ++s;
1451 if (!strncasecmp((const char *)start_token, (const char *)tok,
1452 s - start_token)) {
1453 return 1;
1455 if (!*s) {
1456 return 0;
1462 AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
1463 const char *tok)
1465 int llen, tlen, lidx;
1467 if (!line)
1468 return 0;
1470 llen = strlen(line);
1471 tlen = strlen(tok);
1472 lidx = llen - tlen;
1474 if (lidx < 0 ||
1475 (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1476 return 0;
1478 return (strncasecmp(&line[lidx], tok, tlen) == 0);
1481 AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
1483 char *cmd;
1484 unsigned char *d;
1485 const unsigned char *s;
1487 cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */
1488 d = (unsigned char *)cmd;
1489 s = (const unsigned char *)str;
1490 for (; *s; ++s) {
1492 #if defined(OS2) || defined(WIN32)
1494 * Newlines to Win32/OS2 CreateProcess() are ill advised.
1495 * Convert them to spaces since they are effectively white
1496 * space to most applications
1498 if (*s == '\r' || *s == '\n') {
1499 *d++ = ' ';
1500 continue;
1502 #endif
1504 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1505 *d++ = '\\';
1507 *d++ = *s;
1509 *d = '\0';
1511 return cmd;
1514 static char x2c(const char *what)
1516 register char digit;
1518 #if !APR_CHARSET_EBCDIC
1519 digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10
1520 : (what[0] - '0'));
1521 digit *= 16;
1522 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10
1523 : (what[1] - '0'));
1524 #else /*APR_CHARSET_EBCDIC*/
1525 char xstr[5];
1526 xstr[0]='0';
1527 xstr[1]='x';
1528 xstr[2]=what[0];
1529 xstr[3]=what[1];
1530 xstr[4]='\0';
1531 digit = apr_xlate_conv_byte(ap_hdrs_from_ascii,
1532 0xFF & strtol(xstr, NULL, 16));
1533 #endif /*APR_CHARSET_EBCDIC*/
1534 return (digit);
1538 * Unescapes a URL, leaving reserved characters intact.
1539 * Returns 0 on success, non-zero on error
1540 * Failure is due to
1541 * bad % escape returns HTTP_BAD_REQUEST
1543 * decoding %00 or a forbidden character returns HTTP_NOT_FOUND
1546 static int unescape_url(char *url, const char *forbid, const char *reserved)
1548 register int badesc, badpath;
1549 char *x, *y;
1551 badesc = 0;
1552 badpath = 0;
1553 /* Initial scan for first '%'. Don't bother writing values before
1554 * seeing a '%' */
1555 y = strchr(url, '%');
1556 if (y == NULL) {
1557 return OK;
1559 for (x = y; *y; ++x, ++y) {
1560 if (*y != '%') {
1561 *x = *y;
1563 else {
1564 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1565 badesc = 1;
1566 *x = '%';
1568 else {
1569 char decoded;
1570 decoded = x2c(y + 1);
1571 if ((decoded == '\0')
1572 || (forbid && ap_strchr_c(forbid, decoded))) {
1573 badpath = 1;
1574 *x = decoded;
1575 y += 2;
1577 else if (reserved && ap_strchr_c(reserved, decoded)) {
1578 *x++ = *y++;
1579 *x++ = *y++;
1580 *x = *y;
1582 else {
1583 *x = decoded;
1584 y += 2;
1589 *x = '\0';
1590 if (badesc) {
1591 return HTTP_BAD_REQUEST;
1593 else if (badpath) {
1594 return HTTP_NOT_FOUND;
1596 else {
1597 return OK;
1600 AP_DECLARE(int) ap_unescape_url(char *url)
1602 /* Traditional */
1603 #ifdef CASE_BLIND_FILESYSTEM
1604 return unescape_url(url, "/\\", NULL);
1605 #else
1606 return unescape_url(url, "/", NULL);
1607 #endif
1609 AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
1611 /* AllowEncodedSlashes (corrected) */
1612 return unescape_url(url, NULL, "/");
1614 #ifdef NEW_APIS
1615 /* IFDEF these out until they've been thought through.
1616 * Just a germ of an API extension for now
1618 AP_DECLARE(int) ap_unescape_url_proxy(char *url)
1620 /* leave RFC1738 reserved characters intact, * so proxied URLs
1621 * don't get mangled. Where does that leave encoded '&' ?
1623 return unescape_url(url, NULL, "/;?");
1625 AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
1627 return unescape_url(url, NULL, reserved);
1629 #endif
1631 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
1632 apr_port_t port, const request_rec *r)
1634 if (ap_is_default_port(port, r)) {
1635 return apr_pstrdup(p, hostname);
1637 else {
1638 return apr_psprintf(p, "%s:%u", hostname, port);
1642 AP_DECLARE(int) ap_unescape_all(char *url)
1644 return unescape_url(url, NULL, NULL);
1647 /* c2x takes an unsigned, and expects the caller has guaranteed that
1648 * 0 <= what < 256... which usually means that you have to cast to
1649 * unsigned char first, because (unsigned)(char)(x) first goes through
1650 * signed extension to an int before the unsigned cast.
1652 * The reason for this assumption is to assist gcc code generation --
1653 * the unsigned char -> unsigned extension is already done earlier in
1654 * both uses of this code, so there's no need to waste time doing it
1655 * again.
1657 static const char c2x_table[] = "0123456789abcdef";
1659 static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
1660 unsigned char *where)
1662 #if APR_CHARSET_EBCDIC
1663 what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what);
1664 #endif /*APR_CHARSET_EBCDIC*/
1665 *where++ = prefix;
1666 *where++ = c2x_table[what >> 4];
1667 *where++ = c2x_table[what & 0xf];
1668 return where;
1672 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1673 * routine is (should be) OS independent.
1675 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1676 * cases if a ':' occurs before the first '/' in the URL, the URL should be
1677 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1678 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1679 * efficiency reasons, we don't use escape_path_segment(), which is provided for
1680 * reference. Again, RFC 1808 is where this stuff is defined.
1682 * If partial is set, os_escape_path() assumes that the path will be appended to
1683 * something with a '/' in it (and thus does not prefix "./").
1686 AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
1688 const unsigned char *s = (const unsigned char *)segment;
1689 unsigned char *d = (unsigned char *)copy;
1690 unsigned c;
1692 while ((c = *s)) {
1693 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1694 d = c2x(c, '%', d);
1696 else {
1697 *d++ = c;
1699 ++s;
1701 *d = '\0';
1702 return copy;
1705 AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
1707 return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
1710 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
1712 char *copy = apr_palloc(p, 3 * strlen(path) + 3);
1713 const unsigned char *s = (const unsigned char *)path;
1714 unsigned char *d = (unsigned char *)copy;
1715 unsigned c;
1717 if (!partial) {
1718 const char *colon = ap_strchr_c(path, ':');
1719 const char *slash = ap_strchr_c(path, '/');
1721 if (colon && (!slash || colon < slash)) {
1722 *d++ = '.';
1723 *d++ = '/';
1726 while ((c = *s)) {
1727 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1728 d = c2x(c, '%', d);
1730 else {
1731 *d++ = c;
1733 ++s;
1735 *d = '\0';
1736 return copy;
1739 /* ap_escape_uri is now a macro for os_escape_path */
1741 AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
1743 int i, j;
1744 char *x;
1746 /* first, count the number of extra characters */
1747 for (i = 0, j = 0; s[i] != '\0'; i++)
1748 if (s[i] == '<' || s[i] == '>')
1749 j += 3;
1750 else if (s[i] == '&')
1751 j += 4;
1752 else if (s[i] == '"')
1753 j += 5;
1754 else if (toasc && !apr_isascii(s[i]))
1755 j += 5;
1757 if (j == 0)
1758 return apr_pstrmemdup(p, s, i);
1760 x = apr_palloc(p, i + j + 1);
1761 for (i = 0, j = 0; s[i] != '\0'; i++, j++)
1762 if (s[i] == '<') {
1763 memcpy(&x[j], "&lt;", 4);
1764 j += 3;
1766 else if (s[i] == '>') {
1767 memcpy(&x[j], "&gt;", 4);
1768 j += 3;
1770 else if (s[i] == '&') {
1771 memcpy(&x[j], "&amp;", 5);
1772 j += 4;
1774 else if (s[i] == '"') {
1775 memcpy(&x[j], "&quot;", 6);
1776 j += 5;
1778 else if (toasc && !apr_isascii(s[i])) {
1779 char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]);
1780 memcpy(&x[j], esc, 6);
1781 j += 5;
1783 else
1784 x[j] = s[i];
1786 x[j] = '\0';
1787 return x;
1789 AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
1791 char *ret;
1792 unsigned char *d;
1793 const unsigned char *s;
1795 if (!str) {
1796 return NULL;
1799 ret = apr_palloc(p, 4 * strlen(str) + 1); /* Be safe */
1800 d = (unsigned char *)ret;
1801 s = (const unsigned char *)str;
1802 for (; *s; ++s) {
1804 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1805 *d++ = '\\';
1806 switch(*s) {
1807 case '\b':
1808 *d++ = 'b';
1809 break;
1810 case '\n':
1811 *d++ = 'n';
1812 break;
1813 case '\r':
1814 *d++ = 'r';
1815 break;
1816 case '\t':
1817 *d++ = 't';
1818 break;
1819 case '\v':
1820 *d++ = 'v';
1821 break;
1822 case '\\':
1823 case '"':
1824 *d++ = *s;
1825 break;
1826 default:
1827 c2x(*s, 'x', d);
1828 d += 3;
1831 else {
1832 *d++ = *s;
1835 *d = '\0';
1837 return ret;
1840 AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
1841 apr_size_t buflen)
1843 unsigned char *d, *ep;
1844 const unsigned char *s;
1846 if (!source || !buflen) { /* be safe */
1847 return 0;
1850 d = (unsigned char *)dest;
1851 s = (const unsigned char *)source;
1852 ep = d + buflen - 1;
1854 for (; d < ep && *s; ++s) {
1856 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1857 *d++ = '\\';
1858 if (d >= ep) {
1859 --d;
1860 break;
1863 switch(*s) {
1864 case '\b':
1865 *d++ = 'b';
1866 break;
1867 case '\n':
1868 *d++ = 'n';
1869 break;
1870 case '\r':
1871 *d++ = 'r';
1872 break;
1873 case '\t':
1874 *d++ = 't';
1875 break;
1876 case '\v':
1877 *d++ = 'v';
1878 break;
1879 case '\\':
1880 *d++ = *s;
1881 break;
1882 case '"': /* no need for this in error log */
1883 d[-1] = *s;
1884 break;
1885 default:
1886 if (d >= ep - 2) {
1887 ep = --d; /* break the for loop as well */
1888 break;
1890 c2x(*s, 'x', d);
1891 d += 3;
1894 else {
1895 *d++ = *s;
1898 *d = '\0';
1900 return (d - (unsigned char *)dest);
1903 AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
1905 apr_finfo_t finfo;
1907 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
1908 return 0; /* in error condition, just return no */
1910 return (finfo.filetype == APR_DIR);
1913 AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
1915 apr_finfo_t finfo;
1917 if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
1918 return 0; /* in error condition, just return no */
1920 return (finfo.filetype == APR_DIR);
1923 AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
1924 const char *src2)
1926 apr_size_t len1, len2;
1927 char *path;
1929 len1 = strlen(src1);
1930 len2 = strlen(src2);
1931 /* allocate +3 for '/' delimiter, trailing NULL and overallocate
1932 * one extra byte to allow the caller to add a trailing '/'
1934 path = (char *)apr_palloc(a, len1 + len2 + 3);
1935 if (len1 == 0) {
1936 *path = '/';
1937 memcpy(path + 1, src2, len2 + 1);
1939 else {
1940 char *next;
1941 memcpy(path, src1, len1);
1942 next = path + len1;
1943 if (next[-1] != '/') {
1944 *next++ = '/';
1946 memcpy(next, src2, len2 + 1);
1948 return path;
1952 * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
1954 AP_DECLARE(int) ap_is_url(const char *u)
1956 register int x;
1958 for (x = 0; u[x] != ':'; x++) {
1959 if ((!u[x]) ||
1960 ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) &&
1961 (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
1962 return 0;
1966 return (x ? 1 : 0); /* If the first character is ':', it's broken, too */
1969 AP_DECLARE(int) ap_ind(const char *s, char c)
1971 const char *p = ap_strchr_c(s, c);
1973 if (p == NULL)
1974 return -1;
1975 return p - s;
1978 AP_DECLARE(int) ap_rind(const char *s, char c)
1980 const char *p = ap_strrchr_c(s, c);
1982 if (p == NULL)
1983 return -1;
1984 return p - s;
1987 AP_DECLARE(void) ap_str_tolower(char *str)
1989 while (*str) {
1990 *str = apr_tolower(*str);
1991 ++str;
1996 * We must return a FQDN
1998 char *ap_get_local_host(apr_pool_t *a)
2000 #ifndef MAXHOSTNAMELEN
2001 #define MAXHOSTNAMELEN 256
2002 #endif
2003 char str[MAXHOSTNAMELEN + 1];
2004 char *server_hostname = NULL;
2005 apr_sockaddr_t *sockaddr;
2006 char *hostname;
2008 if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) {
2009 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
2010 "%s: apr_gethostname() failed to determine ServerName",
2011 ap_server_argv0);
2012 } else {
2013 str[sizeof(str) - 1] = '\0';
2014 if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) {
2015 if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) &&
2016 (ap_strchr_c(hostname, '.')) ) {
2017 server_hostname = apr_pstrdup(a, hostname);
2018 return server_hostname;
2019 } else if (ap_strchr_c(str, '.')) {
2020 server_hostname = apr_pstrdup(a, str);
2021 } else {
2022 apr_sockaddr_ip_get(&hostname, sockaddr);
2023 server_hostname = apr_pstrdup(a, hostname);
2025 } else {
2026 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a,
2027 "%s: apr_sockaddr_info_get() failed for %s",
2028 ap_server_argv0, str);
2032 if (!server_hostname)
2033 server_hostname = apr_pstrdup(a, "127.0.0.1");
2035 ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a,
2036 "%s: Could not reliably determine the server's fully qualified "
2037 "domain name, using %s for ServerName",
2038 ap_server_argv0, server_hostname);
2040 return server_hostname;
2043 /* simple 'pool' alloc()ing glue to apr_base64.c
2045 AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
2047 char *decoded;
2048 int l;
2050 decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded));
2051 l = apr_base64_decode(decoded, bufcoded);
2052 decoded[l] = '\0'; /* make binary sequence into string */
2054 return decoded;
2057 AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
2059 char *encoded;
2060 int l = strlen(string);
2062 encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l));
2063 l = apr_base64_encode(encoded, string, l);
2064 encoded[l] = '\0'; /* make binary sequence into string */
2066 return encoded;
2069 /* we want to downcase the type/subtype for comparison purposes
2070 * but nothing else because ;parameter=foo values are case sensitive.
2071 * XXX: in truth we want to downcase parameter names... but really,
2072 * apache has never handled parameters and such correctly. You
2073 * also need to compress spaces and such to be able to compare
2074 * properly. -djg
2076 AP_DECLARE(void) ap_content_type_tolower(char *str)
2078 char *semi;
2080 semi = strchr(str, ';');
2081 if (semi) {
2082 *semi = '\0';
2085 ap_str_tolower(str);
2087 if (semi) {
2088 *semi = ';';
2093 * Given a string, replace any bare " with \" .
2095 AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
2097 int newlen = 0;
2098 const char *inchr = instring;
2099 char *outchr, *outstring;
2102 * Look through the input string, jogging the length of the output
2103 * string up by an extra byte each time we find an unescaped ".
2105 while (*inchr != '\0') {
2106 newlen++;
2107 if (*inchr == '"') {
2108 newlen++;
2111 * If we find a slosh, and it's not the last byte in the string,
2112 * it's escaping something - advance past both bytes.
2114 if ((*inchr == '\\') && (inchr[1] != '\0')) {
2115 inchr++;
2116 newlen++;
2118 inchr++;
2120 outstring = apr_palloc(p, newlen + 1);
2121 inchr = instring;
2122 outchr = outstring;
2124 * Now copy the input string to the output string, inserting a slosh
2125 * in front of every " that doesn't already have one.
2127 while (*inchr != '\0') {
2128 if ((*inchr == '\\') && (inchr[1] != '\0')) {
2129 *outchr++ = *inchr++;
2130 *outchr++ = *inchr++;
2132 if (*inchr == '"') {
2133 *outchr++ = '\\';
2135 if (*inchr != '\0') {
2136 *outchr++ = *inchr++;
2139 *outchr = '\0';
2140 return outstring;
2144 * Given a string, append the PID deliminated by delim.
2145 * Usually used to create a pid-appended filepath name
2146 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not
2147 * a macro, to avoid unistd.h dependency
2149 AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
2150 const char *delim)
2152 return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
2153 delim, getpid());
2158 * Parse a given timeout parameter string into an apr_interval_time_t value.
2159 * The unit of the time interval is given as postfix string to the numeric
2160 * string. Currently the following units are understood:
2162 * ms : milliseconds
2163 * s : seconds
2164 * mi[n] : minutes
2165 * h : hours
2167 * If no unit is contained in the given timeout parameter the default_time_unit
2168 * will be used instead.
2169 * @param timeout_parameter The string containing the timeout parameter.
2170 * @param timeout The timeout value to be returned.
2171 * @param default_time_unit The default time unit to use if none is specified
2172 * in timeout_parameter.
2173 * @return Status value indicating whether the parsing was successful or not.
2175 AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
2176 const char *timeout_parameter,
2177 apr_interval_time_t *timeout,
2178 const char *default_time_unit)
2180 char *endp;
2181 const char *time_str;
2182 apr_int64_t tout;
2184 tout = apr_strtoi64(timeout_parameter, &endp, 10);
2185 if (errno) {
2186 return errno;
2188 if (!endp || !*endp) {
2189 time_str = default_time_unit;
2191 else {
2192 time_str = endp;
2195 switch (*time_str) {
2196 /* Time is in seconds */
2197 case 's':
2198 *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
2199 break;
2200 case 'h':
2201 /* Time is in hours */
2202 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
2203 break;
2204 case 'm':
2205 switch (*(++time_str)) {
2206 /* Time is in miliseconds */
2207 case 's':
2208 *timeout = (apr_interval_time_t) tout * 1000;
2209 break;
2210 /* Time is in minutes */
2211 case 'i':
2212 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
2213 break;
2214 default:
2215 return APR_EGENERAL;
2217 break;
2218 default:
2219 return APR_EGENERAL;
2221 return APR_SUCCESS;