]> granicus.if.org Git - apache/blob - server/util.c
Elaborate on low hanging XXX fruit
[apache] / server / util.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * util.c: string utility things
19  *
20  * 3/21/93 Rob McCool
21  * 1995-96 Many changes by the Apache Software Foundation
22  *
23  */
24
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
28  */
29
30 #include "apr.h"
31 #include "apr_strings.h"
32 #include "apr_lib.h"
33
34 #define APR_WANT_STDIO
35 #define APR_WANT_STRFUNC
36 #include "apr_want.h"
37
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
44
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"
53
54 #ifdef HAVE_PWD_H
55 #include <pwd.h>
56 #endif
57 #ifdef HAVE_GRP_H
58 #include <grp.h>
59 #endif
60
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.
64  */
65 #include "test_char.h"
66
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.
71  */
72 #define TEST_CHAR(c, f)        (test_char_table[(unsigned)(c)] & (f))
73
74 /* Win32/NetWare/OS2 need to check for both forward and back slashes
75  * in ap_getparents() and ap_escape_url.
76  */
77 #ifdef CASE_BLIND_FILESYSTEM
78 #define IS_SLASH(s) ((s == '/') || (s == '\\'))
79 #else
80 #define IS_SLASH(s) (s == '/')
81 #endif
82
83
84 /*
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.
87  */
88 AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype)
89 {
90     const char *semi;
91
92     if (intype == NULL) return NULL;
93
94     semi = ap_strchr_c(intype, ';');
95     if (semi == NULL) {
96         return apr_pstrdup(p, intype);
97     }
98     else {
99         while ((semi > intype) && apr_isspace(semi[-1])) {
100             semi--;
101         }
102         return apr_pstrndup(p, intype, semi - intype);
103     }
104 }
105
106 AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
107                               int gmt)
108 {
109     apr_size_t retcode;
110     char ts[MAX_STRING_LEN];
111     char tf[MAX_STRING_LEN];
112     apr_time_exp_t xt;
113
114     if (gmt) {
115         const char *f;
116         char *strp;
117
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.
122          */
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;
144             }
145         }
146         *strp = '\0';
147         fmt = tf;
148     }
149     else {
150         apr_time_exp_lt(&xt, t);
151     }
152
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);
157 }
158
159 /* Roy owes Rob beer. */
160 /* Rob owes Roy dinner. */
161
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.
164  *
165  * Well, okay, they still wouldn't make any sense.
166  */
167
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?
171  */
172 AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
173 {
174     int x, y;
175
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;
187             }
188             return -1;
189         }
190         else if ((expected[y] != '?') && (str[x] != expected[y]))
191             return 1;
192     }
193     return (str[x] != '\0');
194 }
195
196 AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
197 {
198     int x, y;
199
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;
211             }
212             return -1;
213         }
214         else if (expected[y] != '?'
215                  && apr_tolower(str[x]) != apr_tolower(expected[y]))
216             return 1;
217     }
218     return (str[x] != '\0');
219 }
220
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.
225  */
226 AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
227 {
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;
233     }
234     return 1;
235 }
236
237 AP_DECLARE(int) ap_is_matchexp(const char *str)
238 {
239     register int x;
240
241     for (x = 0; str[x]; x++)
242         if ((str[x] == '*') || (str[x] == '?'))
243             return 1;
244     return 0;
245 }
246
247 /*
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.
253  */
254
255 static apr_status_t regex_cleanup(void *preg)
256 {
257     ap_regfree((ap_regex_t *) preg);
258     return APR_SUCCESS;
259 }
260
261 AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern,
262                                      int cflags)
263 {
264     ap_regex_t *preg = apr_palloc(p, sizeof *preg);
265
266     if (ap_regcomp(preg, pattern, cflags)) {
267         return NULL;
268     }
269
270     apr_pool_cleanup_register(p, (void *) preg, regex_cleanup,
271                               apr_pool_cleanup_null);
272
273     return preg;
274 }
275
276 AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg)
277 {
278     ap_regfree(reg);
279     apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup);
280 }
281
282 /*
283  * Similar to standard strstr() but we ignore case in this version.
284  * Based on the strstr() implementation further below.
285  */
286 AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2)
287 {
288     char *p1, *p2;
289     if (*s2 == '\0') {
290         /* an empty s2 */
291         return((char *)s1);
292     }
293     while(1) {
294         for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++);
295         if (*s1 == '\0') {
296             return(NULL);
297         }
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);
305             }
306         }
307         if (*p2 == '\0') {
308             /* second string ended, a match */
309             break;
310         }
311         /* didn't find a match here, try starting at next character in s1 */
312         s1++;
313     }
314     return((char *)s1);
315 }
316
317 /*
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)
324  */
325 AP_DECLARE(const char *) ap_stripprefix(const char *bigstring,
326                                         const char *prefix)
327 {
328     const char *p1;
329
330     if (*prefix == '\0')
331         return bigstring;
332
333     p1 = bigstring;
334     while (*p1 && *prefix) {
335         if (*p1++ != *prefix++)
336             return bigstring;
337     }
338     if (*prefix == '\0')
339         return p1;
340
341     /* hit the end of bigstring! */
342     return bigstring;
343 }
344
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.
349  *
350  * input should be the string with the $-expressions, source should be the
351  * string that was matched against.
352  *
353  * It returns the substituted string, or NULL on error.
354  *
355  * Parts of this code are based on Henry Spencer's regsub(), from his
356  * AT&T V8 regexp package.
357  */
358
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[])
362 {
363     const char *src = input;
364     char *dest, *dst;
365     char c;
366     size_t no;
367     int len;
368
369     if (!source)
370         return NULL;
371     if (!nmatch)
372         return apr_pstrdup(p, src);
373
374     /* First pass, find the size */
375
376     len = 0;
377
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;
385
386         if (no > 9) {                /* Ordinary character. */
387             if (c == '\\' && (*src == '$' || *src == '&'))
388                 c = *src++;
389             len++;
390         }
391         else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
392             len += pmatch[no].rm_eo - pmatch[no].rm_so;
393         }
394
395     }
396
397     dest = dst = apr_pcalloc(p, len + 1);
398
399     /* Now actually fill in the string */
400
401     src = input;
402
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;
410
411         if (no > 9) {                /* Ordinary character. */
412             if (c == '\\' && (*src == '$' || *src == '&'))
413                 c = *src++;
414             *dst++ = c;
415         }
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;
420         }
421
422     }
423     *dst = '\0';
424
425     return dest;
426 }
427
428 /*
429  * Parse .. so we don't compromise security
430  */
431 AP_DECLARE(void) ap_getparents(char *name)
432 {
433     char *next;
434     int l, w, first_dot;
435
436     /* Four paseses, as per RFC 1808 */
437     /* a) remove ./ path segments */
438     for (next = name; *next && (*next != '.'); next++) {
439     }
440
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++];
448     }
449
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';
456
457     /* c) remove all xx/../ segments. (including leading ../ and /../) */
458     l = first_dot;
459
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;
464
465             l = l - 2;
466             if (l >= 0) {
467                 while (l >= 0 && !IS_SLASH(name[l]))
468                     l--;
469                 l++;
470             }
471             else
472                 l = 0;
473             n = l;
474             while ((name[n] = name[m]))
475                 (++n, ++m);
476         }
477         else
478             ++l;
479     }
480
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++;
491         }
492         else
493             l = 0;
494         name[l] = '\0';
495     }
496 }
497
498 AP_DECLARE(void) ap_no2slash(char *name)
499 {
500     char *d, *s;
501
502     s = d = name;
503
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
509
510     while (*s) {
511         if ((*d++ = *s) == '/') {
512             do {
513                 ++s;
514             } while (*s == '/');
515         }
516         else {
517             ++s;
518         }
519     }
520     *d = '\0';
521 }
522
523
524 /*
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
529  *
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
534
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/
541  *
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
547  */
548 AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
549 {
550     if (n < 1) {
551         *d = '/';
552         *++d = '\0';
553         return (d);
554     }
555
556     for (;;) {
557         if (*s == '\0' || (*s == '/' && (--n) == 0)) {
558             *d = '/';
559             break;
560         }
561         *d++ = *s++;
562     }
563     *++d = 0;
564     return (d);
565 }
566
567
568 /*
569  * return the parent directory name including trailing / of the file s
570  */
571 AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
572 {
573     const char *last_slash = ap_strrchr_c(s, '/');
574     char *d;
575     int l;
576
577     if (last_slash == NULL) {
578         return apr_pstrdup(p, "");
579     }
580     l = (last_slash - s) + 1;
581     d = apr_pstrmemdup(p, s, l);
582
583     return (d);
584 }
585
586
587 AP_DECLARE(int) ap_count_dirs(const char *path)
588 {
589     register int x, n;
590
591     for (x = 0, n = 0; path[x]; x++)
592         if (path[x] == '/')
593             n++;
594     return n;
595 }
596
597 AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop)
598 {
599     return ap_getword(atrans, (const char **) line, stop);
600 }
601
602 AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop)
603 {
604     const char *pos = *line;
605     int len;
606     char *res;
607
608     while ((*pos != stop) && *pos) {
609         ++pos;
610     }
611
612     len = pos - *line;
613     res = apr_pstrmemdup(atrans, *line, len);
614
615     if (stop) {
616         while (*pos == stop) {
617             ++pos;
618         }
619     }
620     *line = pos;
621
622     return res;
623 }
624
625 AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line)
626 {
627     return ap_getword_white(atrans, (const char **) line);
628 }
629
630 AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line)
631 {
632     const char *pos = *line;
633     int len;
634     char *res;
635
636     while (!apr_isspace(*pos) && *pos) {
637         ++pos;
638     }
639
640     len = pos - *line;
641     res = apr_pstrmemdup(atrans, *line, len);
642
643     while (apr_isspace(*pos)) {
644         ++pos;
645     }
646
647     *line = pos;
648
649     return res;
650 }
651
652 AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line,
653                                        char stop)
654 {
655     return ap_getword_nulls(atrans, (const char **) line, stop);
656 }
657
658 AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line,
659                                     char stop)
660 {
661     const char *pos = ap_strchr_c(*line, stop);
662     char *res;
663
664     if (!pos) {
665         res = apr_pstrdup(atrans, *line);
666         *line += strlen(*line);
667         return res;
668     }
669
670     res = apr_pstrndup(atrans, *line, pos - *line);
671
672     ++pos;
673
674     *line = pos;
675
676     return res;
677 }
678
679 /* Get a word, (new) config-file style --- quoted strings and backslashes
680  * all honored
681  */
682
683 static char *substring_conf(apr_pool_t *p, const char *start, int len,
684                             char quote)
685 {
686     char *result = apr_palloc(p, len + 2);
687     char *resp = result;
688     int i;
689
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];
696     }
697
698     *resp++ = '\0';
699 #if RESOLVE_ENV_PER_TOKEN
700     return (char *)ap_resolve_env(p,result);
701 #else
702     return result;
703 #endif
704 }
705
706 AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line)
707 {
708     return ap_getword_conf(p, (const char **) line);
709 }
710
711 AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line)
712 {
713     const char *str = *line, *strend;
714     char *res;
715     char quote;
716
717     while (*str && apr_isspace(*str))
718         ++str;
719
720     if (!*str) {
721         *line = str;
722         return "";
723     }
724
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;
731             }
732             else {
733                 ++strend;
734             }
735         }
736         res = substring_conf(p, str + 1, strend - str - 1, quote);
737
738         if (*strend == quote)
739             ++strend;
740     }
741     else {
742         strend = str;
743         while (*strend && !apr_isspace(*strend))
744             ++strend;
745
746         res = substring_conf(p, str, strend - str, 0);
747     }
748
749     while (*strend && apr_isspace(*strend))
750         ++strend;
751     *line = strend;
752     return res;
753 }
754
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.
760  */
761 AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word)
762 {
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;
773
774     s = ap_strchr_c(word, '$');
775     if (!s) {
776         return word;
777     }
778
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;
787
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;
798         }
799
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;
807                 }
808                 else {
809                     current->string = s;
810                     current->len = e - s + 1;
811                     outlen += current->len;
812                 }
813                 s = e + 1;
814             }
815             else {
816                 current->string = s++;
817                 current->len = 1;
818                 ++outlen;
819             }
820         }
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;
827         }
828     } while (s && *s);
829
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;
836         }
837         result = result->next;
838     } while (result);
839     res_buf[outlen] = '\0';
840
841     return res_buf;
842 }
843
844 AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp)
845 {
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);
851 }
852
853 static apr_status_t cfg_close(void *param)
854 {
855     apr_file_t *cfp = (apr_file_t *) param;
856     return (apr_file_close(cfp));
857 }
858
859 static int cfg_getch(void *param)
860 {
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;
866 }
867
868 static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
869 {
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;
875     }
876     return NULL;
877 }
878
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)
882 {
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
890
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;
895     }
896
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;
907
908     status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file);
909     if (status != APR_SUCCESS)
910         return status;
911
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;
923     }
924
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.
931      *
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.
935      */
936     {
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);
944         }
945     }
946 #endif
947
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;
957 }
958
959
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))
967 {
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;
980 }
981
982 /* Read one character from a configfile_t */
983 AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp)
984 {
985     register int ch = cfp->getch(cfp->param);
986     if (ch == LF)
987         ++cfp->line_number;
988     return ch;
989 }
990
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)
994 {
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;
1001
1002         while (1) {
1003             ++cfp->line_number;
1004             if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
1005                 return 1;
1006
1007             /*
1008              *  check for line continuation,
1009              *  i.e. match [^\\]\\[\r]\n only
1010              */
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] == '\\')) {
1021                         /*
1022                          * line continuation requested -
1023                          * then remove backslash and continue
1024                          */
1025                         cbufsize -= (cp-cbuf);
1026                         cbuf = cp;
1027                         continue;
1028                     }
1029                     else {
1030                         /*
1031                          * no real continuation because escaped -
1032                          * then just remove escape character
1033                          */
1034                         for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
1035                             cp[0] = cp[1];
1036                     }
1037                 }
1038             }
1039             break;
1040         }
1041
1042         /*
1043          * Leading and trailing white space is eliminated completely
1044          */
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'; )
1055                 ;
1056
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;
1065
1066         buf[0] = '\0';
1067         /* skip leading whitespace */
1068         do {
1069             c = cfp->getch(cfp->param);
1070         } while (c == '\t' || c == ' ');
1071
1072         if (c == EOF)
1073             return 1;
1074
1075         if(bufsize < 2) {
1076             /* too small, assume caller is crazy */
1077             return 1;
1078         }
1079
1080         while (1) {
1081             if ((c == '\t') || (c == ' ')) {
1082                 buf[i++] = ' ';
1083                 while ((c == '\t') || (c == ' '))
1084                     c = cfp->getch(cfp->param);
1085             }
1086             if (c == CR) {
1087                 /* silently ignore CR (_assume_ that a LF follows) */
1088                 c = cfp->getch(cfp->param);
1089             }
1090             if (c == LF) {
1091                 /* increase line number and return on LF */
1092                 ++cfp->line_number;
1093             }
1094             if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
1095                 /*
1096                  *  check for line continuation
1097                  */
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;
1104                     }
1105                     /* else nothing needs be done because
1106                      * then the backslash is escaped and
1107                      * we just strip to a single one
1108                      */
1109                 }
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;
1119             }
1120             buf[i] = c;
1121             ++i;
1122             c = cfp->getch(cfp->param);
1123         }
1124     }
1125 }
1126
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.
1132  */
1133 AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len)
1134 {
1135     const unsigned char *ptr = (const unsigned char *)*field;
1136     const unsigned char *token;
1137     int in_qpair, in_qstr, in_com;
1138
1139     /* Find first non-comma, non-whitespace byte */
1140
1141     while (*ptr == ',' || apr_isspace(*ptr))
1142         ++ptr;
1143
1144     token = ptr;
1145
1146     /* Find the end of this item, skipping over dead bits */
1147
1148     for (in_qpair = in_qstr = in_com = 0;
1149          *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1150          ++ptr) {
1151
1152         if (in_qpair) {
1153             in_qpair = 0;
1154         }
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;
1169             }
1170         }
1171     }
1172
1173     if ((*len = (ptr - token)) == 0) {
1174         *field = (const char *)ptr;
1175         return NULL;
1176     }
1177
1178     /* Advance field pointer to the next non-comma, non-white byte */
1179
1180     while (*ptr == ',' || apr_isspace(*ptr))
1181         ++ptr;
1182
1183     *field = (const char *)ptr;
1184     return (const char *)token;
1185 }
1186
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.
1192  */
1193 AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field)
1194 {
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;
1200
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.
1203      */
1204     if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1205         return NULL;
1206     }
1207     token = apr_palloc(p, tok_len + 1);
1208
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.
1213      */
1214     for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1215          *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1216          ++ptr) {
1217
1218         if (in_qpair) {
1219             in_qpair = 0;
1220             *pos++ = *ptr;
1221         }
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;
1269             }
1270         }
1271     }
1272     *pos = '\0';
1273
1274     return token;
1275 }
1276
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.
1281  */
1282 AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line,
1283                                   const char *tok)
1284 {
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;
1288
1289     if (!line || !tok)
1290         return 0;
1291
1292     do {  /* loop for each item in line's list */
1293
1294         /* Find first non-comma, non-whitespace byte */
1295
1296         while (*ptr == ',' || apr_isspace(*ptr))
1297             ++ptr;
1298
1299         if (*ptr)
1300             good = 1;  /* until proven otherwise for this item */
1301         else
1302             break;     /* no items left and nothing good found */
1303
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.
1307          */
1308         for (pos = (const unsigned char *)tok;
1309              *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1310              ++ptr) {
1311
1312             if (in_qpair) {
1313                 in_qpair = 0;
1314                 if (good)
1315                     good = (*pos++ == *ptr);
1316             }
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;
1368                 }
1369             }
1370         }
1371         if (good && *pos)
1372             good = 0;          /* not good if only a prefix was matched */
1373
1374     } while (*ptr && !good);
1375
1376     return good;
1377 }
1378
1379
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.
1384  */
1385
1386 AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line,
1387                                 int accept_white)
1388 {
1389     const char *ptr = *accept_line;
1390     const char *tok_start;
1391     char *token;
1392     int tok_len;
1393
1394     /* Find first non-white byte */
1395
1396     while (*ptr && apr_isspace(*ptr))
1397         ++ptr;
1398
1399     tok_start = ptr;
1400
1401     /* find token end, skipping over quoted strings.
1402      * (comments are already gone).
1403      */
1404
1405     while (*ptr && (accept_white || !apr_isspace(*ptr))
1406            && *ptr != ';' && *ptr != ',') {
1407         if (*ptr++ == '"')
1408             while (*ptr)
1409                 if (*ptr++ == '"')
1410                     break;
1411     }
1412
1413     tok_len = ptr - tok_start;
1414     token = apr_pstrndup(p, tok_start, tok_len);
1415
1416     /* Advance accept_line pointer to the next non-white byte */
1417
1418     while (*ptr && apr_isspace(*ptr))
1419         ++ptr;
1420
1421     *accept_line = ptr;
1422     return token;
1423 }
1424
1425
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)
1428 {
1429     const unsigned char *start_token;
1430     const unsigned char *s;
1431
1432     if (!line)
1433         return 0;
1434
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
1439          */
1440         while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1441             ++s;
1442         }
1443         if (!*s) {
1444             return 0;
1445         }
1446         start_token = s;
1447         /* find end of the token */
1448         while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1449             ++s;
1450         }
1451         if (!strncasecmp((const char *)start_token, (const char *)tok,
1452                          s - start_token)) {
1453             return 1;
1454         }
1455         if (!*s) {
1456             return 0;
1457         }
1458     }
1459 }
1460
1461
1462 AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
1463                                    const char *tok)
1464 {
1465     int llen, tlen, lidx;
1466
1467     if (!line)
1468         return 0;
1469
1470     llen = strlen(line);
1471     tlen = strlen(tok);
1472     lidx = llen - tlen;
1473
1474     if (lidx < 0 ||
1475         (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1476         return 0;
1477
1478     return (strncasecmp(&line[lidx], tok, tlen) == 0);
1479 }
1480
1481 AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
1482 {
1483     char *cmd;
1484     unsigned char *d;
1485     const unsigned char *s;
1486
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) {
1491
1492 #if defined(OS2) || defined(WIN32)
1493         /*
1494          * Newlines to Win32/OS2 CreateProcess() are ill advised.
1495          * Convert them to spaces since they are effectively white
1496          * space to most applications
1497          */
1498         if (*s == '\r' || *s == '\n') {
1499              *d++ = ' ';
1500              continue;
1501          }
1502 #endif
1503
1504         if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1505             *d++ = '\\';
1506         }
1507         *d++ = *s;
1508     }
1509     *d = '\0';
1510
1511     return cmd;
1512 }
1513
1514 static char x2c(const char *what)
1515 {
1516     register char digit;
1517
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);
1535 }
1536
1537 /*
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
1542  *
1543  *   decoding %00 or a forbidden character returns HTTP_NOT_FOUND
1544  */
1545
1546 static int unescape_url(char *url, const char *forbid, const char *reserved)
1547 {
1548     register int badesc, badpath;
1549     char *x, *y;
1550
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;
1558     }
1559     for (x = y; *y; ++x, ++y) {
1560         if (*y != '%') {
1561             *x = *y;
1562         }
1563         else {
1564             if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) {
1565                 badesc = 1;
1566                 *x = '%';
1567             }
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;
1576                 }
1577                 else if (reserved && ap_strchr_c(reserved, decoded)) {
1578                     *x++ = *y++;
1579                     *x++ = *y++;
1580                     *x = *y;
1581                 }
1582                 else {
1583                     *x = decoded;
1584                     y += 2;
1585                 }
1586             }
1587         }
1588     }
1589     *x = '\0';
1590     if (badesc) {
1591         return HTTP_BAD_REQUEST;
1592     }
1593     else if (badpath) {
1594         return HTTP_NOT_FOUND;
1595     }
1596     else {
1597         return OK;
1598     }
1599 }
1600 AP_DECLARE(int) ap_unescape_url(char *url)
1601 {
1602     /* Traditional */
1603 #ifdef CASE_BLIND_FILESYSTEM
1604     return unescape_url(url, "/\\", NULL);
1605 #else
1606     return unescape_url(url, "/", NULL);
1607 #endif
1608 }
1609 AP_DECLARE(int) ap_unescape_url_keep2f(char *url)
1610 {
1611     /* AllowEncodedSlashes (corrected) */
1612     return unescape_url(url, NULL, "/");
1613 }
1614 #ifdef NEW_APIS
1615 /* IFDEF these out until they've been thought through.
1616  * Just a germ of an API extension for now
1617  */
1618 AP_DECLARE(int) ap_unescape_url_proxy(char *url)
1619 {
1620     /* leave RFC1738 reserved characters intact, * so proxied URLs
1621      * don't get mangled.  Where does that leave encoded '&' ?
1622      */
1623     return unescape_url(url, NULL, "/;?");
1624 }
1625 AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
1626 {
1627     return unescape_url(url, NULL, reserved);
1628 }
1629 #endif
1630
1631 AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
1632                                        apr_port_t port, const request_rec *r)
1633 {
1634     if (ap_is_default_port(port, r)) {
1635         return apr_pstrdup(p, hostname);
1636     }
1637     else {
1638         return apr_psprintf(p, "%s:%u", hostname, port);
1639     }
1640 }
1641
1642 AP_DECLARE(int) ap_unescape_all(char *url)
1643 {
1644     return unescape_url(url, NULL, NULL);
1645 }
1646
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.
1651  *
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.
1656  */
1657 static const char c2x_table[] = "0123456789abcdef";
1658
1659 static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix,
1660                                      unsigned char *where)
1661 {
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;
1669 }
1670
1671 /*
1672  * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1673  * routine is (should be) OS independent.
1674  *
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.
1681  *
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 "./").
1684  */
1685
1686 AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment)
1687 {
1688     const unsigned char *s = (const unsigned char *)segment;
1689     unsigned char *d = (unsigned char *)copy;
1690     unsigned c;
1691
1692     while ((c = *s)) {
1693         if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1694             d = c2x(c, '%', d);
1695         }
1696         else {
1697             *d++ = c;
1698         }
1699         ++s;
1700     }
1701     *d = '\0';
1702     return copy;
1703 }
1704
1705 AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment)
1706 {
1707     return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment);
1708 }
1709
1710 AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial)
1711 {
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;
1716
1717     if (!partial) {
1718         const char *colon = ap_strchr_c(path, ':');
1719         const char *slash = ap_strchr_c(path, '/');
1720
1721         if (colon && (!slash || colon < slash)) {
1722             *d++ = '.';
1723             *d++ = '/';
1724         }
1725     }
1726     while ((c = *s)) {
1727         if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1728             d = c2x(c, '%', d);
1729         }
1730         else {
1731             *d++ = c;
1732         }
1733         ++s;
1734     }
1735     *d = '\0';
1736     return copy;
1737 }
1738
1739 /* ap_escape_uri is now a macro for os_escape_path */
1740
1741 AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
1742 {
1743     int i, j;
1744     char *x;
1745
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;
1756
1757     if (j == 0)
1758         return apr_pstrmemdup(p, s, i);
1759
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;
1765         }
1766         else if (s[i] == '>') {
1767             memcpy(&x[j], "&gt;", 4);
1768             j += 3;
1769         }
1770         else if (s[i] == '&') {
1771             memcpy(&x[j], "&amp;", 5);
1772             j += 4;
1773         }
1774         else if (s[i] == '"') {
1775             memcpy(&x[j], "&quot;", 6);
1776             j += 5;
1777         }
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;
1782         }
1783         else
1784             x[j] = s[i];
1785
1786     x[j] = '\0';
1787     return x;
1788 }
1789 AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str)
1790 {
1791     char *ret;
1792     unsigned char *d;
1793     const unsigned char *s;
1794
1795     if (!str) {
1796         return NULL;
1797     }
1798
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) {
1803
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;
1829             }
1830         }
1831         else {
1832             *d++ = *s;
1833         }
1834     }
1835     *d = '\0';
1836
1837     return ret;
1838 }
1839
1840 AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source,
1841                                                apr_size_t buflen)
1842 {
1843     unsigned char *d, *ep;
1844     const unsigned char *s;
1845
1846     if (!source || !buflen) { /* be safe */
1847         return 0;
1848     }
1849
1850     d = (unsigned char *)dest;
1851     s = (const unsigned char *)source;
1852     ep = d + buflen - 1;
1853
1854     for (; d < ep && *s; ++s) {
1855
1856         if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1857             *d++ = '\\';
1858             if (d >= ep) {
1859                 --d;
1860                 break;
1861             }
1862
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;
1889                 }
1890                 c2x(*s, 'x', d);
1891                 d += 3;
1892             }
1893         }
1894         else {
1895             *d++ = *s;
1896         }
1897     }
1898     *d = '\0';
1899
1900     return (d - (unsigned char *)dest);
1901 }
1902
1903 AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path)
1904 {
1905     apr_finfo_t finfo;
1906
1907     if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS)
1908         return 0;                /* in error condition, just return no */
1909
1910     return (finfo.filetype == APR_DIR);
1911 }
1912
1913 AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path)
1914 {
1915     apr_finfo_t finfo;
1916
1917     if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS)
1918         return 0;                /* in error condition, just return no */
1919
1920     return (finfo.filetype == APR_DIR);
1921 }
1922
1923 AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1,
1924                                   const char *src2)
1925 {
1926     apr_size_t len1, len2;
1927     char *path;
1928
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 '/'
1933       */
1934     path = (char *)apr_palloc(a, len1 + len2 + 3);
1935     if (len1 == 0) {
1936         *path = '/';
1937         memcpy(path + 1, src2, len2 + 1);
1938     }
1939     else {
1940         char *next;
1941         memcpy(path, src1, len1);
1942         next = path + len1;
1943         if (next[-1] != '/') {
1944             *next++ = '/';
1945         }
1946         memcpy(next, src2, len2 + 1);
1947     }
1948     return path;
1949 }
1950
1951 /*
1952  * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
1953  */
1954 AP_DECLARE(int) ap_is_url(const char *u)
1955 {
1956     register int x;
1957
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;
1963         }
1964     }
1965
1966     return (x ? 1 : 0);                /* If the first character is ':', it's broken, too */
1967 }
1968
1969 AP_DECLARE(int) ap_ind(const char *s, char c)
1970 {
1971     const char *p = ap_strchr_c(s, c);
1972
1973     if (p == NULL)
1974         return -1;
1975     return p - s;
1976 }
1977
1978 AP_DECLARE(int) ap_rind(const char *s, char c)
1979 {
1980     const char *p = ap_strrchr_c(s, c);
1981
1982     if (p == NULL)
1983         return -1;
1984     return p - s;
1985 }
1986
1987 AP_DECLARE(void) ap_str_tolower(char *str)
1988 {
1989     while (*str) {
1990         *str = apr_tolower(*str);
1991         ++str;
1992     }
1993 }
1994
1995 /*
1996  * We must return a FQDN
1997  */
1998 char *ap_get_local_host(apr_pool_t *a)
1999 {
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;
2007
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);
2024             }
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);
2029         }
2030     }
2031
2032     if (!server_hostname)
2033         server_hostname = apr_pstrdup(a, "127.0.0.1");
2034
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);
2039
2040     return server_hostname;
2041 }
2042
2043 /* simple 'pool' alloc()ing glue to apr_base64.c
2044  */
2045 AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
2046 {
2047     char *decoded;
2048     int l;
2049
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 */
2053
2054     return decoded;
2055 }
2056
2057 AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
2058 {
2059     char *encoded;
2060     int l = strlen(string);
2061
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 */
2065
2066     return encoded;
2067 }
2068
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
2075  */
2076 AP_DECLARE(void) ap_content_type_tolower(char *str)
2077 {
2078     char *semi;
2079
2080     semi = strchr(str, ';');
2081     if (semi) {
2082         *semi = '\0';
2083     }
2084
2085     ap_str_tolower(str);
2086
2087     if (semi) {
2088         *semi = ';';
2089     }
2090 }
2091
2092 /*
2093  * Given a string, replace any bare " with \" .
2094  */
2095 AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
2096 {
2097     int newlen = 0;
2098     const char *inchr = instring;
2099     char *outchr, *outstring;
2100
2101     /*
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 ".
2104      */
2105     while (*inchr != '\0') {
2106         newlen++;
2107         if (*inchr == '"') {
2108             newlen++;
2109         }
2110         /*
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.
2113          */
2114         if ((*inchr == '\\') && (inchr[1] != '\0')) {
2115             inchr++;
2116             newlen++;
2117         }
2118         inchr++;
2119     }
2120     outstring = apr_palloc(p, newlen + 1);
2121     inchr = instring;
2122     outchr = outstring;
2123     /*
2124      * Now copy the input string to the output string, inserting a slosh
2125      * in front of every " that doesn't already have one.
2126      */
2127     while (*inchr != '\0') {
2128         if ((*inchr == '\\') && (inchr[1] != '\0')) {
2129             *outchr++ = *inchr++;
2130             *outchr++ = *inchr++;
2131         }
2132         if (*inchr == '"') {
2133             *outchr++ = '\\';
2134         }
2135         if (*inchr != '\0') {
2136             *outchr++ = *inchr++;
2137         }
2138     }
2139     *outchr = '\0';
2140     return outstring;
2141 }
2142
2143 /*
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
2148  */
2149 AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
2150                                     const char *delim)
2151 {
2152     return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string,
2153                         delim, getpid());
2154
2155 }
2156
2157 /**
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:
2161  *
2162  * ms    : milliseconds
2163  * s     : seconds
2164  * mi[n] : minutes
2165  * h     : hours
2166  *
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.
2174  */
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)
2179 {
2180     char *endp;
2181     const char *time_str;
2182     apr_int64_t tout;
2183
2184     tout = apr_strtoi64(timeout_parameter, &endp, 10);
2185     if (errno) {
2186         return errno;
2187     }
2188     if (!endp || !*endp) {
2189         time_str = default_time_unit;
2190     }
2191     else {
2192         time_str = endp;
2193     }
2194
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 milliseconds */
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;
2216         }
2217         break;
2218     default:
2219         return APR_EGENERAL;
2220     }
2221     return APR_SUCCESS;
2222 }
2223