1 /* $MirOS: src/usr.sbin/httpd/src/main/util.c,v 1.8 2008/03/19 23:07:21 tg Exp $ */
2 
3 /* ====================================================================
4  * The Apache Software License, Version 1.1
5  *
6  * Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
7  * reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. The end-user documentation included with the redistribution,
22  *    if any, must include the following acknowledgment:
23  *       "This product includes software developed by the
24  *        Apache Software Foundation (http://www.apache.org/)."
25  *    Alternately, this acknowledgment may appear in the software itself,
26  *    if and wherever such third-party acknowledgments normally appear.
27  *
28  * 4. The names "Apache" and "Apache Software Foundation" must
29  *    not be used to endorse or promote products derived from this
30  *    software without prior written permission. For written
31  *    permission, please contact apache@apache.org.
32  *
33  * 5. Products derived from this software may not be called "Apache",
34  *    nor may "Apache" appear in their name, without prior written
35  *    permission of the Apache Software Foundation.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
41  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
42  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
44  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
46  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
47  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
48  * SUCH DAMAGE.
49  * ====================================================================
50  *
51  * This software consists of voluntary contributions made by many
52  * individuals on behalf of the Apache Software Foundation.  For more
53  * information on the Apache Software Foundation, please see
54  * <http://www.apache.org/>.
55  *
56  * Portions of this software are based upon public domain software
57  * originally written at the National Center for Supercomputing Applications,
58  * University of Illinois, Urbana-Champaign.
59  */
60 
61 /*
62  * util.c: string utility things
63  *
64  * 3/21/93 Rob McCool
65  * 1995-96 Many changes by the Apache Group
66  *
67  */
68 
69 /* Debugging aid:
70  * #define DEBUG            to trace all cfg_open*()/cfg_closefile() calls
71  * #define DEBUG_CFG_LINES  to trace every line read from the config files
72  */
73 
74 #include "httpd.h"
75 #include "http_conf_globals.h"	/* for user_id & group_id */
76 #include "http_log.h"
77 
78 /* A bunch of functions in util.c scan strings looking for certain characters.
79  * To make that more efficient we encode a lookup table.  The test_char_table
80  * is generated automatically by gen_test_char.c.
81  */
82 #include "test_char.h"
83 
84 __RCSID("$MirOS: src/usr.sbin/httpd/src/main/util.c,v 1.8 2008/03/19 23:07:21 tg Exp $");
85 
86 /* we assume the folks using this ensure 0 <= c < 256... which means
87  * you need a cast to (unsigned char) first, you can't just plug a
88  * char in here and get it to work, because if char is signed then it
89  * will first be sign extended.
90  */
91 #define TEST_CHAR(c, f)	(test_char_table[(unsigned)(c)] & (f))
92 
ap_util_init(void)93 void ap_util_init(void)
94 {
95     /* nothing to do... previously there was run-time initialization of
96      * test_char_table here
97      */
98 }
99 
100 
101 API_VAR_EXPORT const char ap_month_snames[12][4] =
102 {
103     "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
104 };
105 API_VAR_EXPORT const char ap_day_snames[7][4] =
106 {
107     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
108 };
109 
ap_get_time(void)110 API_EXPORT(char *) ap_get_time(void)
111 {
112     time_t t;
113     char *time_string;
114 
115     t = time(NULL);
116     time_string = ctime(&t);
117     time_string[strlen(time_string) - 1] = '\0';
118     return (time_string);
119 }
120 
121 /*
122  * Examine a field value (such as a media-/content-type) string and return
123  * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
124  */
ap_field_noparam(pool * p,const char * intype)125 API_EXPORT(char *) ap_field_noparam(pool *p, const char *intype)
126 {
127     const char *semi;
128 
129     if (intype == NULL) return NULL;
130 
131     semi = strchr(intype, ';');
132     if (semi == NULL) {
133 	return ap_pstrdup(p, intype);
134     }
135     else {
136 	while ((semi > intype) && ap_isspace(semi[-1])) {
137 	    semi--;
138 	}
139 	return ap_pstrndup(p, intype, semi - intype);
140     }
141 }
142 
ap_ht_time(pool * p,time_t t,const char * fmt,int gmt)143 API_EXPORT(char *) ap_ht_time(pool *p, time_t t, const char *fmt, int gmt)
144 {
145     char ts[MAX_STRING_LEN];
146     char tf[MAX_STRING_LEN];
147     struct tm *tms;
148 
149     tms = (gmt ? gmtime(&t) : localtime(&t));
150     if(gmt) {
151 	/* Convert %Z to "GMT" and %z to "+0000";
152 	 * on hosts that do not have a time zone string in struct tm,
153 	 * strftime must assume its argument is local time.
154 	 */
155 	const char *f;
156 	char *strp;
157 	for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f)
158 	    ; f++, strp++) {
159 	    if (*f != '%') continue;
160 	    switch (f[1]) {
161 	    case '%':
162 		*++strp = *++f;
163 		break;
164 	    case 'Z':
165 		*strp++ = 'G';
166 		*strp++ = 'M';
167 		*strp = 'T';
168 		f++;
169 		break;
170 	    case 'z': /* common extension */
171 		*strp++ = '+';
172 		*strp++ = '0';
173 		*strp++ = '0';
174 		*strp++ = '0';
175 		*strp = '0';
176 		f++;
177 		break;
178 	    }
179 	}
180 	*strp = '\0';
181 	fmt = tf;
182     }
183 
184     /* check return code? */
185     strftime(ts, MAX_STRING_LEN, fmt, tms);
186     ts[MAX_STRING_LEN - 1] = '\0';
187     return ap_pstrdup(p, ts);
188 }
189 
ap_gm_timestr_822(pool * p,time_t sec)190 API_EXPORT(char *) ap_gm_timestr_822(pool *p, time_t sec)
191 {
192     struct tm *tms;
193 
194     tms = gmtime(&sec);
195 
196     /* RFC date format; as strftime '%a, %d %b %Y %T GMT' */
197     return ap_psprintf(p,
198 		"%s, %.2d %s %qd %.2d:%.2d:%.2d GMT", ap_day_snames[tms->tm_wday],
199 		tms->tm_mday, ap_month_snames[tms->tm_mon], (int64_t)tms->tm_year + 1900,
200 		tms->tm_hour, tms->tm_min, tms->tm_sec);
201 }
202 
203 /* What a pain in the ass. */
ap_get_gmtoff(int * tz)204 API_EXPORT(struct tm *) ap_get_gmtoff(int *tz)
205 {
206     time_t tt = time(NULL);
207     struct tm *t;
208 
209     t = localtime(&tt);
210     *tz = (int) (t->tm_gmtoff / 60);
211     return t;
212 }
213 
214 /* Roy owes Rob beer. */
215 /* Rob owes Roy dinner. */
216 
217 /* These legacy comments would make a lot more sense if Roy hadn't
218  * replaced the old later_than() routine with util_date.c.
219  *
220  * Well, okay, they still wouldn't make any sense.
221  */
222 
223 /* Match = 0, NoMatch = 1, Abort = -1
224  * Based loosely on sections of wildmat.c by Rich Salz
225  * Hmmm... shouldn't this really go component by component?
226  */
ap_strcmp_match(const char * str,const char * exp)227 API_EXPORT(int) ap_strcmp_match(const char *str, const char *exp)
228 {
229     int x, y;
230 
231     for (x = 0, y = 0; exp[y]; ++y, ++x) {
232 	if ((!str[x]) && (exp[y] != '*'))
233 	    return -1;
234 	if (exp[y] == '*') {
235 	    while (exp[++y] == '*');
236 	    if (!exp[y])
237 		return 0;
238 	    while (str[x]) {
239 		int ret;
240 		if ((ret = ap_strcmp_match(&str[x++], &exp[y])) != 1)
241 		    return ret;
242 	    }
243 	    return -1;
244 	}
245 	else if ((exp[y] != '?') && (str[x] != exp[y]))
246 	    return 1;
247     }
248     return (str[x] != '\0');
249 }
250 
ap_strcasecmp_match(const char * str,const char * exp)251 API_EXPORT(int) ap_strcasecmp_match(const char *str, const char *exp)
252 {
253     int x, y;
254 
255     for (x = 0, y = 0; exp[y]; ++y, ++x) {
256 	if ((!str[x]) && (exp[y] != '*'))
257 	    return -1;
258 	if (exp[y] == '*') {
259 	    while (exp[++y] == '*');
260 	    if (!exp[y])
261 		return 0;
262 	    while (str[x]) {
263 		int ret;
264 		if ((ret = ap_strcasecmp_match(&str[x++], &exp[y])) != 1)
265 		    return ret;
266 	    }
267 	    return -1;
268 	}
269 	else if ((exp[y] != '?') && (ap_tolower(str[x]) != ap_tolower(exp[y])))
270 	    return 1;
271     }
272     return (str[x] != '\0');
273 }
274 
ap_is_matchexp(const char * str)275 API_EXPORT(int) ap_is_matchexp(const char *str)
276 {
277     register int x;
278 
279     for (x = 0; str[x]; x++)
280 	if ((str[x] == '*') || (str[x] == '?'))
281 	    return 1;
282     return 0;
283 }
284 
285 /*
286  * Similar to standard strstr() but we ignore case in this version.
287  * Based on the strstr() implementation further below.
288  */
ap_strcasestr(const char * s1,const char * s2)289 API_EXPORT(char *) ap_strcasestr(const char *s1, const char *s2)
290 {
291     char *p1, *p2;
292     if (*s2 == '\0') {
293 	/* an empty s2 */
294         return((char *)s1);
295     }
296     while(1) {
297 	for ( ; (*s1 != '\0') && (ap_tolower(*s1) != ap_tolower(*s2)); s1++);
298 	if (*s1 == '\0') return(NULL);
299 	/* found first character of s2, see if the rest matches */
300         p1 = (char *)s1;
301         p2 = (char *)s2;
302         while (ap_tolower(*++p1) == ap_tolower(*++p2)) {
303             if (*p1 == '\0') {
304                 /* both strings ended together */
305                 return((char *)s1);
306             }
307         }
308         if (*p2 == '\0') {
309             /* second string ended, a match */
310             break;
311         }
312 	/* didn't find a match here, try starting at next character in s1 */
313         s1++;
314     }
315     return((char *)s1);
316 }
317 
318 /*
319  * Returns an offsetted pointer in bigstring immediately after
320  * prefix. Returns bigstring if bigstring doesn't start with
321  * prefix or if prefix is longer than bigstring while still matching.
322  * NOTE: pointer returned is relative to bigstring, so we
323  * can use standard pointer comparisons in the calling function
324  * (eg: test if ap_stripprefix(a,b) == a)
325  */
ap_stripprefix(const char * bigstring,const char * prefix)326 API_EXPORT(char *) ap_stripprefix(const char *bigstring, const char *prefix)
327 {
328     char *p1;
329     if (*prefix == '\0') {
330         return( (char *)bigstring);
331     }
332     p1 = (char *)bigstring;
333     while(*p1 && *prefix) {
334         if (*p1++ != *prefix++)
335             return( (char *)bigstring);
336     }
337     if (*prefix == '\0')
338         return(p1);
339     else /* hit the end of bigstring! */
340         return( (char *)bigstring);
341 }
342 
343 /*
344  * Apache stub function for the regex libraries regexec() to make sure the
345  * whole regex(3) API is available through the Apache (exported) namespace.
346  * This is especially important for the DSO situations of modules.
347  * DO NOT MAKE A MACRO OUT OF THIS FUNCTION!
348  */
ap_regexec(const regex_t * preg,const char * string,size_t nmatch,regmatch_t pmatch[],int eflags)349 API_EXPORT(int) ap_regexec(const regex_t *preg, const char *string,
350                            size_t nmatch, regmatch_t pmatch[], int eflags)
351 {
352     return regexec(preg, string, nmatch, pmatch, eflags);
353 }
354 
ap_regerror(int errcode,const regex_t * preg,char * errbuf,size_t errbuf_size)355 API_EXPORT(size_t) ap_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
356 {
357     return regerror(errcode, preg, errbuf, errbuf_size);
358 }
359 
360 
361 /* This function substitutes for $0-$9, filling in regular expression
362  * submatches. Pass it the same nmatch and pmatch arguments that you
363  * passed ap_regexec(). pmatch should not be greater than the maximum number
364  * of subexpressions - i.e. one more than the re_nsub member of regex_t.
365  *
366  * input should be the string with the $-expressions, source should be the
367  * string that was matched against.
368  *
369  * It returns the substituted string, or NULL on error.
370  *
371  * Parts of this code are based on Henry Spencer's regsub(), from his
372  * AT&T V8 regexp package.
373  */
374 
ap_pregsub(pool * p,const char * input,const char * source,size_t nmatch,regmatch_t pmatch[])375 API_EXPORT(char *) ap_pregsub(pool *p, const char *input, const char *source,
376 			   size_t nmatch, regmatch_t pmatch[])
377 {
378     const char *src = input;
379     char *dest, *dst;
380     char c;
381     size_t no;
382     int len;
383 
384     if (!source)
385 	return NULL;
386     if (!nmatch)
387 	return ap_pstrdup(p, src);
388 
389     /* First pass, find the size */
390 
391     len = 0;
392 
393     while ((c = *src++) != '\0') {
394 	if (c == '&')
395 	    no = 0;
396 	else if (c == '$' && isdigit((unsigned char)*src))
397 	    no = *src++ - '0';
398 	else
399 	    no = 10;
400 
401 	if (no > 9) {		/* Ordinary character. */
402 	    if (c == '\\' && (*src == '$' || *src == '&'))
403 		c = *src++;
404 	    len++;
405 	}
406 	else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
407 	    len += pmatch[no].rm_eo - pmatch[no].rm_so;
408 	}
409 
410     }
411 
412     dest = dst = ap_pcalloc(p, len + 1);
413 
414     /* Now actually fill in the string */
415 
416     src = input;
417 
418     while ((c = *src++) != '\0') {
419 	if (c == '&')
420 	    no = 0;
421 	else if (c == '$' && isdigit((unsigned char)*src))
422 	    no = *src++ - '0';
423 	else
424 	    no = 10;
425 
426 	if (no > 9) {		/* Ordinary character. */
427 	    if (c == '\\' && (*src == '$' || *src == '&'))
428 		c = *src++;
429 	    *dst++ = c;
430 	}
431 	else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) {
432 	    len = pmatch[no].rm_eo - pmatch[no].rm_so;
433 	    memcpy(dst, source + pmatch[no].rm_so, len);
434 	    dst += len;
435 	}
436 
437     }
438     *dst = '\0';
439 
440     return dest;
441 }
442 
443 /*
444  * Parse .. so we don't compromise security
445  */
ap_getparents(char * name)446 API_EXPORT(void) ap_getparents(char *name)
447 {
448     int l, w;
449 
450     /* Four passes, as per RFC 1808 */
451     /* a) remove ./ path segments */
452 
453     for (l = 0, w = 0; name[l] != '\0';) {
454 	if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/'))
455 	    l += 2;
456 	else
457 	    name[w++] = name[l++];
458     }
459 
460     /* b) remove trailing . path, segment */
461     if (w == 1 && name[0] == '.')
462 	w--;
463     else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/')
464 	w--;
465     name[w] = '\0';
466 
467     /* c) remove all xx/../ segments. (including leading ../ and /../) */
468     l = 0;
469 
470     while (name[l] != '\0') {
471 	if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' &&
472 	    (l == 0 || name[l - 1] == '/')) {
473 	    register int m = l + 3, n;
474 
475 	    l = l - 2;
476 	    if (l >= 0) {
477 		while (l >= 0 && name[l] != '/')
478 		    l--;
479 		l++;
480 	    }
481 	    else
482 		l = 0;
483 	    n = l;
484 	    while ((name[n] = name[m]))
485 		(++n, ++m);
486 	}
487 	else
488 	    ++l;
489     }
490 
491     /* d) remove trailing xx/.. segment. */
492     if (l == 2 && name[0] == '.' && name[1] == '.')
493 	name[0] = '\0';
494     else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') {
495 	l = l - 4;
496 	if (l >= 0) {
497 	    while (l >= 0 && name[l] != '/')
498 		l--;
499 	    l++;
500 	}
501 	else
502 	    l = 0;
503 	name[l] = '\0';
504     }
505 }
506 
ap_no2slash(char * name)507 API_EXPORT(void) ap_no2slash(char *name)
508 {
509     char *d, *s;
510 
511     s = d = name;
512 
513     while (*s) {
514 	if ((*d++ = *s) == '/') {
515 	    do {
516 		++s;
517 	    } while (*s == '/');
518 	}
519 	else {
520 	    ++s;
521 	}
522     }
523     *d = '\0';
524 }
525 
526 
527 /*
528  * copy at most n leading directories of s into d
529  * d should be at least as large as s plus 1 extra byte
530  * assumes n > 0
531  * the return value is the ever useful pointer to the trailing \0 of d
532  *
533  * examples:
534  *    /a/b, 1  ==> /
535  *    /a/b, 2  ==> /a/
536  *    /a/b, 3  ==> /a/b/
537  *    /a/b, 4  ==> /a/b/
538  *
539  * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments,
540  * so that if n == 0, "/" is returned in d with n == 1
541  * and s == "e:/test.html", "e:/" is returned in d
542  * *** See also directory_walk in src/main/http_request.c
543  */
ap_make_dirstr_prefix(char * d,const char * s,int n)544 API_EXPORT(char *) ap_make_dirstr_prefix(char *d, const char *s, int n)
545 {
546     for (;;) {
547 	*d = *s;
548 	if (*d == '\0') {
549 	    *d = '/';
550 	    break;
551 	}
552 	if (*d == '/' && (--n) == 0)
553 	    break;
554 	++d;
555 	++s;
556     }
557     *++d = 0;
558     return (d);
559 }
560 
561 
562 /*
563  * return the parent directory name including trailing / of the file s
564  */
ap_make_dirstr_parent(pool * p,const char * s)565 API_EXPORT(char *) ap_make_dirstr_parent(pool *p, const char *s)
566 {
567     char *last_slash = strrchr(s, '/');
568     char *d;
569     int l;
570 
571     if (last_slash == NULL) {
572 	/* XXX: well this is really broken if this happens */
573 	return (ap_pstrdup(p, "/"));
574     }
575     l = (last_slash - s) + 1;
576     d = ap_palloc(p, l + 1);
577     memcpy(d, s, l);
578     d[l] = 0;
579     return (d);
580 }
581 
582 
583 /*
584  * This function is deprecated.  Use one of the preceding two functions
585  * which are faster.
586  */
ap_make_dirstr(pool * p,const char * s,int n)587 API_EXPORT(char *) ap_make_dirstr(pool *p, const char *s, int n)
588 {
589     register int x, f;
590     char *res;
591 
592     for (x = 0, f = 0; s[x]; x++) {
593 	if (s[x] == '/')
594 	    if ((++f) == n) {
595 		res = ap_palloc(p, x + 2);
596 		memcpy(res, s, x);
597 		res[x] = '/';
598 		res[x + 1] = '\0';
599 		return res;
600 	    }
601     }
602 
603     if (s[strlen(s) - 1] == '/')
604 	return ap_pstrdup(p, s);
605     else
606 	return ap_pstrcat(p, s, "/", NULL);
607 }
608 
ap_count_dirs(const char * path)609 API_EXPORT(int) ap_count_dirs(const char *path)
610 {
611     register int x, n;
612 
613     for (x = 0, n = 0; path[x]; x++)
614 	if (path[x] == '/')
615 	    n++;
616     return n;
617 }
618 
619 
ap_chdir_file(const char * file)620 API_EXPORT(void) ap_chdir_file(const char *file)
621 {
622     const char *x;
623     char buf[HUGE_STRING_LEN];
624 
625     x = strrchr(file, '/');
626     if (x == NULL) {
627 	chdir(file);
628     }
629     else if (x - file < sizeof(buf) - 1) {
630 	memcpy(buf, file, x - file);
631 	buf[x - file] = '\0';
632 	chdir(buf);
633     }
634     /* XXX: well, this is a silly function, no method of reporting an
635      * error... ah well. */
636 }
637 
ap_getword_nc(pool * atrans,char ** line,char stop)638 API_EXPORT(char *) ap_getword_nc(pool *atrans, char **line, char stop)
639 {
640     return ap_getword(atrans, (const char **) line, stop);
641 }
642 
ap_getword(pool * atrans,const char ** line,char stop)643 API_EXPORT(char *) ap_getword(pool *atrans, const char **line, char stop)
644 {
645     char *pos = strchr(*line, stop);
646     char *res;
647 
648     if (!pos) {
649 	res = ap_pstrdup(atrans, *line);
650 	*line += strlen(*line);
651 	return res;
652     }
653 
654     res = ap_pstrndup(atrans, *line, pos - *line);
655 
656     while (*pos == stop) {
657 	++pos;
658     }
659 
660     *line = pos;
661 
662     return res;
663 }
664 
ap_getword_white_nc(pool * atrans,char ** line)665 API_EXPORT(char *) ap_getword_white_nc(pool *atrans, char **line)
666 {
667     return ap_getword_white(atrans, (const char **) line);
668 }
669 
ap_getword_white(pool * atrans,const char ** line)670 API_EXPORT(char *) ap_getword_white(pool *atrans, const char **line)
671 {
672     int pos = -1, x;
673     char *res;
674 
675     for (x = 0; (*line)[x]; x++) {
676 	if (ap_isspace((*line)[x])) {
677 	    pos = x;
678 	    break;
679 	}
680     }
681 
682     if (pos == -1) {
683 	res = ap_pstrdup(atrans, *line);
684 	*line += strlen(*line);
685 	return res;
686     }
687 
688     res = ap_palloc(atrans, pos + 1);
689     ap_cpystrn(res, *line, pos + 1);
690 
691     while (ap_isspace((*line)[pos]))
692 	++pos;
693 
694     *line += pos;
695 
696     return res;
697 }
698 
ap_getword_nulls_nc(pool * atrans,char ** line,char stop)699 API_EXPORT(char *) ap_getword_nulls_nc(pool *atrans, char **line, char stop)
700 {
701     return ap_getword_nulls(atrans, (const char **) line, stop);
702 }
703 
ap_getword_nulls(pool * atrans,const char ** line,char stop)704 API_EXPORT(char *) ap_getword_nulls(pool *atrans, const char **line, char stop)
705 {
706     char *pos = strchr(*line, stop);
707     char *res;
708 
709     if (!pos) {
710 	res = ap_pstrdup(atrans, *line);
711 	*line += strlen(*line);
712 	return res;
713     }
714 
715     res = ap_pstrndup(atrans, *line, pos - *line);
716 
717     ++pos;
718 
719     *line = pos;
720 
721     return res;
722 }
723 
724 /* Get a word, (new) config-file style --- quoted strings and backslashes
725  * all honored
726  */
727 
substring_conf(pool * p,const char * start,int len,char quote)728 static char *substring_conf(pool *p, const char *start, int len, char quote)
729 {
730     char *result = ap_palloc(p, len + 2);
731     char *resp = result;
732     int i;
733 
734     for (i = 0; i < len; ++i) {
735 	if (start[i] == '\\' && (start[i + 1] == '\\'
736 				 || (quote && start[i + 1] == quote)))
737 	    *resp++ = start[++i];
738 	else
739 	    *resp++ = start[i];
740     }
741 
742     *resp++ = '\0';
743     return result;
744 }
745 
ap_getword_conf_nc(pool * p,char ** line)746 API_EXPORT(char *) ap_getword_conf_nc(pool *p, char **line)
747 {
748     return ap_getword_conf(p, (const char **) line);
749 }
750 
ap_getword_conf(pool * p,const char ** line)751 API_EXPORT(char *) ap_getword_conf(pool *p, const char **line)
752 {
753     const char *str = *line, *strend;
754     char *res;
755     char quote;
756 
757     while (ap_isspace(*str))
758 	++str;
759 
760     if (!*str) {
761 	*line = str;
762 	return "";
763     }
764 
765     if ((quote = *str) == '"' || quote == '\'') {
766 	strend = str + 1;
767 	while (*strend && *strend != quote) {
768 	    if (*strend == '\\' && strend[1] && strend[1] == quote)
769 		strend += 2;
770 	    else
771 		++strend;
772 	}
773 	res = substring_conf(p, str + 1, strend - str - 1, quote);
774 
775 	if (*strend == quote)
776 	    ++strend;
777     }
778     else {
779 	if (*str == '#')
780 	    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, NULL,
781 			 "Apache does not support line-end comments. Consider using quotes around argument: \"%s\"", str);
782 	strend = str;
783 	while (*strend && !ap_isspace(*strend))
784 	    ++strend;
785 
786 	res = substring_conf(p, str, strend - str, 0);
787     }
788 
789     while (ap_isspace(*strend))
790 	++strend;
791     *line = strend;
792     return res;
793 }
794 
ap_cfg_closefile(configfile_t * cfp)795 API_EXPORT(int) ap_cfg_closefile(configfile_t *cfp)
796 {
797 #ifdef DEBUG
798     ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL,
799         "Done with config file %s", cfp->name);
800 #endif
801     return (cfp->close == NULL) ? 0 : cfp->close(cfp->param);
802 }
803 
804 /* Common structure that holds the file and pool for ap_pcfg_openfile */
805 typedef struct {
806     struct pool *pool;
807     FILE *file;
808 } poolfile_t;
809 
cfg_close(void * param)810 static int cfg_close(void *param)
811 {
812     poolfile_t *cfp = (poolfile_t *) param;
813     return (ap_pfclose(cfp->pool, cfp->file));
814 }
815 
cfg_getch(void * param)816 static int cfg_getch(void *param)
817 {
818     poolfile_t *cfp = (poolfile_t *) param;
819     return (fgetc(cfp->file));
820 }
821 
cfg_getstr(void * buf,size_t bufsiz,void * param)822 static void *cfg_getstr(void *buf, size_t bufsiz, void *param)
823 {
824     poolfile_t *cfp = (poolfile_t *) param;
825     return (fgets(buf, bufsiz, cfp->file));
826 }
827 
828 /* Open a configfile_t as FILE, return open configfile_t struct pointer */
ap_pcfg_openfile(pool * p,const char * name)829 API_EXPORT(configfile_t *) ap_pcfg_openfile(pool *p, const char *name)
830 {
831     configfile_t *new_cfg;
832     poolfile_t *new_pfile;
833     FILE *file;
834     struct stat stbuf;
835     int saved_errno;
836 
837     if (name == NULL) {
838         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
839                "Internal error: pcfg_openfile() called with NULL filename");
840         return NULL;
841     }
842 
843     if (!ap_os_is_filename_valid(name)) {
844         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
845                     "Access to config file %s denied: not a valid filename",
846                     name);
847 	errno = EACCES;
848         return NULL;
849     }
850 
851     file = ap_pfopen(p, name, "r");
852 #ifdef DEBUG
853     saved_errno = errno;
854     ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL,
855                 "Opening config file %s (%s)",
856                 name, (file == NULL) ? strerror(errno) : "successful");
857     errno = saved_errno;
858 #endif
859     if (file == NULL)
860         return NULL;
861 
862     if (fstat(fileno(file), &stbuf) == 0 &&
863         !S_ISREG(stbuf.st_mode) &&
864         strcmp(name, "/dev/null") != 0) {
865 	saved_errno = errno;
866         ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, NULL,
867                     "Access to file %s denied by server: not a regular file",
868                     name);
869         ap_pfclose(p, file);
870 	errno = saved_errno;
871         return NULL;
872     }
873 
874     new_cfg = ap_palloc(p, sizeof(*new_cfg));
875     new_pfile = ap_palloc(p, sizeof(*new_pfile));
876     new_pfile->file = file;
877     new_pfile->pool = p;
878     new_cfg->param = new_pfile;
879     new_cfg->name = ap_pstrdup(p, name);
880     new_cfg->getch = (int (*)(void *)) cfg_getch;
881     new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr;
882     new_cfg->close = (int (*)(void *)) cfg_close;
883     new_cfg->line_number = 0;
884     return new_cfg;
885 }
886 
887 
888 /* Allocate a configfile_t handle with user defined functions and params */
ap_pcfg_open_custom(pool * p,const char * descr,void * param,int (* getch)(void * param),void * (* getstr)(void * buf,size_t bufsiz,void * param),int (* close_func)(void * param))889 API_EXPORT(configfile_t *) ap_pcfg_open_custom(pool *p, const char *descr,
890     void *param,
891     int(*getch)(void *param),
892     void *(*getstr) (void *buf, size_t bufsiz, void *param),
893     int(*close_func)(void *param))
894 {
895     configfile_t *new_cfg = ap_palloc(p, sizeof(*new_cfg));
896 #ifdef DEBUG
897     ap_log_error(APLOG_MARK, APLOG_DEBUG | APLOG_NOERRNO, NULL, "Opening config handler %s", descr);
898 #endif
899     new_cfg->param = param;
900     new_cfg->name = descr;
901     new_cfg->getch = getch;
902     new_cfg->getstr = getstr;
903     new_cfg->close = close_func;
904     new_cfg->line_number = 0;
905     return new_cfg;
906 }
907 
908 
909 /* Read one character from a configfile_t */
ap_cfg_getc(configfile_t * cfp)910 API_EXPORT(int) ap_cfg_getc(configfile_t *cfp)
911 {
912     register int ch = cfp->getch(cfp->param);
913     if (ch == LF)
914 	++cfp->line_number;
915     return ch;
916 }
917 
918 
919 /* Read one line from open configfile_t, strip LF, increase line number */
920 /* If custom handler does not define a getstr() function, read char by char */
ap_cfg_getline(char * buf,size_t bufsize,configfile_t * cfp)921 API_EXPORT(int) ap_cfg_getline(char *buf, size_t bufsize, configfile_t *cfp)
922 {
923     /* If a "get string" function is defined, use it */
924     if (cfp->getstr != NULL) {
925 	char *src, *dst;
926 	char *cp;
927 	char *cbuf = buf;
928 	size_t cbufsize = bufsize;
929 
930 	while (1) {
931 	    ++cfp->line_number;
932 	    if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL)
933 		return 1;
934 
935 	    /*
936 	     *  check for line continuation,
937 	     *  i.e. match [^\\]\\[\r]\n only
938 	     */
939 	    cp = cbuf;
940 	    while (cp < cbuf+cbufsize && *cp != '\0')
941 		cp++;
942 	    if (cp > cbuf && cp[-1] == LF) {
943 		cp--;
944 		if (cp > cbuf && cp[-1] == CR)
945 		    cp--;
946 		if (cp > cbuf && cp[-1] == '\\') {
947 		    cp--;
948 		    if (!(cp > cbuf && cp[-1] == '\\')) {
949 			/*
950 			 * line continuation requested -
951 			 * then remove backslash and continue
952 			 */
953 			cbufsize -= (cp-cbuf);
954 			cbuf = cp;
955 			continue;
956 		    }
957 		    else {
958 			/*
959 			 * no real continuation because escaped -
960 			 * then just remove escape character
961 			 */
962 			for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++)
963 			    cp[0] = cp[1];
964 		    }
965 		}
966 	    }
967 	    break;
968 	}
969 
970 	/*
971 	 * Leading and trailing white space is eliminated completely
972 	 */
973 	src = buf;
974 	while (ap_isspace(*src))
975 	    ++src;
976 	/* blast trailing whitespace */
977 	dst = &src[strlen(src)];
978 	while (--dst >= src && ap_isspace(*dst))
979 	    *dst = '\0';
980         /* Zap leading whitespace by shifting */
981         if (src != buf)
982 	    for (dst = buf; (*dst++ = *src++) != '\0'; )
983 	        ;
984 
985 #ifdef DEBUG_CFG_LINES
986 	ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
987 #endif
988 	return 0;
989     } else {
990 	/* No "get string" function defined; read character by character */
991 	register int c;
992 	register size_t i = 0;
993 
994 	buf[0] = '\0';
995 	/* skip leading whitespace */
996 	do {
997 	    c = cfp->getch(cfp->param);
998 	} while (c == '\t' || c == ' ');
999 
1000 	if (c == EOF)
1001 	    return 1;
1002 
1003 	if(bufsize < 2) {
1004 	    /* too small, assume caller is crazy */
1005 	    return 1;
1006 	}
1007 
1008 	while (1) {
1009 	    if ((c == '\t') || (c == ' ')) {
1010 		buf[i++] = ' ';
1011 		while ((c == '\t') || (c == ' '))
1012 		    c = cfp->getch(cfp->param);
1013 	    }
1014 	    if (c == CR) {
1015 		/* silently ignore CR (_assume_ that a LF follows) */
1016 		c = cfp->getch(cfp->param);
1017 	    }
1018 	    if (c == LF) {
1019 		/* increase line number and return on LF */
1020 		++cfp->line_number;
1021 	    }
1022 	    if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) {
1023 		/*
1024 		 *  check for line continuation
1025 		 */
1026 		if (i > 0 && buf[i-1] == '\\') {
1027 		    i--;
1028 		    if (!(i > 0 && buf[i-1] == '\\')) {
1029 			/* line is continued */
1030 			c = cfp->getch(cfp->param);
1031 			continue;
1032 		    }
1033 		    /* else nothing needs be done because
1034 		     * then the backslash is escaped and
1035 		     * we just strip to a single one
1036 		     */
1037 		}
1038 		/* blast trailing whitespace */
1039 		while (i > 0 && ap_isspace(buf[i - 1]))
1040 		    --i;
1041 		buf[i] = '\0';
1042 #ifdef DEBUG_CFG_LINES
1043 		ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, NULL, "Read config: %s", buf);
1044 #endif
1045 		return 0;
1046 	    }
1047 	    buf[i] = c;
1048 	    ++i;
1049 	    c = cfp->getch(cfp->param);
1050 	}
1051     }
1052 }
1053 
1054 /* Size an HTTP header field list item, as separated by a comma.
1055  * The return value is a pointer to the beginning of the non-empty list item
1056  * within the original string (or NULL if there is none) and the address
1057  * of field is shifted to the next non-comma, non-whitespace character.
1058  * len is the length of the item excluding any beginning whitespace.
1059  */
ap_size_list_item(const char ** field,int * len)1060 API_EXPORT(const char *) ap_size_list_item(const char **field, int *len)
1061 {
1062     const unsigned char *ptr = (const unsigned char *)*field;
1063     const unsigned char *token;
1064     int in_qpair, in_qstr, in_com;
1065 
1066     /* Find first non-comma, non-whitespace byte */
1067 
1068     while (*ptr == ',' || ap_isspace(*ptr))
1069         ++ptr;
1070 
1071     token = ptr;
1072 
1073     /* Find the end of this item, skipping over dead bits */
1074 
1075     for (in_qpair = in_qstr = in_com = 0;
1076          *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1077          ++ptr) {
1078 
1079         if (in_qpair) {
1080             in_qpair = 0;
1081         }
1082         else {
1083             switch (*ptr) {
1084                 case '\\': in_qpair = 1;      /* quoted-pair         */
1085                            break;
1086                 case '"' : if (!in_com)       /* quoted string delim */
1087                                in_qstr = !in_qstr;
1088                            break;
1089                 case '(' : if (!in_qstr)      /* comment (may nest)  */
1090                                ++in_com;
1091                            break;
1092                 case ')' : if (in_com)        /* end comment         */
1093                                --in_com;
1094                            break;
1095                 default  : break;
1096             }
1097         }
1098     }
1099 
1100     if ((*len = (ptr - token)) == 0) {
1101         *field = (const char *)ptr;
1102         return NULL;
1103     }
1104 
1105     /* Advance field pointer to the next non-comma, non-white byte */
1106 
1107     while (*ptr == ',' || ap_isspace(*ptr))
1108 	++ptr;
1109 
1110     *field = (const char *)ptr;
1111     return (const char *)token;
1112 }
1113 
1114 /* Retrieve an HTTP header field list item, as separated by a comma,
1115  * while stripping insignificant whitespace and lowercasing anything not in
1116  * a quoted string or comment.  The return value is a new string containing
1117  * the converted list item (or NULL if none) and the address pointed to by
1118  * field is shifted to the next non-comma, non-whitespace.
1119  */
ap_get_list_item(pool * p,const char ** field)1120 API_EXPORT(char *) ap_get_list_item(pool *p, const char **field)
1121 {
1122     const char *tok_start;
1123     const unsigned char *ptr;
1124     unsigned char *pos;
1125     char *token;
1126     int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
1127 
1128     /* Find the beginning and maximum length of the list item so that
1129      * we can allocate a buffer for the new string and reset the field.
1130      */
1131     if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) {
1132         return NULL;
1133     }
1134     token = ap_palloc(p, tok_len + 1);
1135 
1136     /* Scan the token again, but this time copy only the good bytes.
1137      * We skip extra whitespace and any whitespace around a '=', '/',
1138      * or ';' and lowercase normal characters not within a comment,
1139      * quoted-string or quoted-pair.
1140      */
1141     for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token;
1142          *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1143          ++ptr) {
1144 
1145         if (in_qpair) {
1146             in_qpair = 0;
1147             *pos++ = *ptr;
1148         }
1149         else {
1150             switch (*ptr) {
1151                 case '\\': in_qpair = 1;
1152                            if (addspace == 1)
1153                                *pos++ = ' ';
1154                            *pos++ = *ptr;
1155                            addspace = 0;
1156                            break;
1157                 case '"' : if (!in_com)
1158                                in_qstr = !in_qstr;
1159                            if (addspace == 1)
1160                                *pos++ = ' ';
1161                            *pos++ = *ptr;
1162                            addspace = 0;
1163                            break;
1164                 case '(' : if (!in_qstr)
1165                                ++in_com;
1166                            if (addspace == 1)
1167                                *pos++ = ' ';
1168                            *pos++ = *ptr;
1169                            addspace = 0;
1170                            break;
1171                 case ')' : if (in_com)
1172                                --in_com;
1173                            *pos++ = *ptr;
1174                            addspace = 0;
1175                            break;
1176                 case ' ' :
1177                 case '\t': if (addspace)
1178                                break;
1179                            if (in_com || in_qstr)
1180                                *pos++ = *ptr;
1181                            else
1182                                addspace = 1;
1183                            break;
1184                 case '=' :
1185                 case '/' :
1186                 case ';' : if (!(in_com || in_qstr))
1187                                addspace = -1;
1188                            *pos++ = *ptr;
1189                            break;
1190                 default  : if (addspace == 1)
1191                                *pos++ = ' ';
1192                            *pos++ = (in_com || in_qstr) ? *ptr
1193                                                         : ap_tolower(*ptr);
1194                            addspace = 0;
1195                            break;
1196             }
1197         }
1198     }
1199     *pos = '\0';
1200 
1201     return token;
1202 }
1203 
1204 /* Find an item in canonical form (lowercase, no extra spaces) within
1205  * an HTTP field value list.  Returns 1 if found, 0 if not found.
1206  * This would be much more efficient if we stored header fields as
1207  * an array of list items as they are received instead of a plain string.
1208  */
ap_find_list_item(pool * p,const char * line,const char * tok)1209 API_EXPORT(int) ap_find_list_item(pool *p, const char *line, const char *tok)
1210 {
1211     const unsigned char *pos;
1212     const unsigned char *ptr = (const unsigned char *)line;
1213     int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
1214 
1215     if (!line || !tok)
1216         return 0;
1217 
1218     do {  /* loop for each item in line's list */
1219 
1220         /* Find first non-comma, non-whitespace byte */
1221 
1222         while (*ptr == ',' || ap_isspace(*ptr))
1223             ++ptr;
1224 
1225         if (*ptr)
1226             good = 1;  /* until proven otherwise for this item */
1227         else
1228             break;     /* no items left and nothing good found */
1229 
1230         /* We skip extra whitespace and any whitespace around a '=', '/',
1231          * or ';' and lowercase normal characters not within a comment,
1232          * quoted-string or quoted-pair.
1233          */
1234         for (pos = (const unsigned char *)tok;
1235              *ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
1236              ++ptr) {
1237 
1238             if (in_qpair) {
1239                 in_qpair = 0;
1240                 if (good)
1241                     good = (*pos++ == *ptr);
1242             }
1243             else {
1244                 switch (*ptr) {
1245                     case '\\': in_qpair = 1;
1246                                if (addspace == 1)
1247                                    good = good && (*pos++ == ' ');
1248                                good = good && (*pos++ == *ptr);
1249                                addspace = 0;
1250                                break;
1251                     case '"' : if (!in_com)
1252                                    in_qstr = !in_qstr;
1253                                if (addspace == 1)
1254                                    good = good && (*pos++ == ' ');
1255                                good = good && (*pos++ == *ptr);
1256                                addspace = 0;
1257                                break;
1258                     case '(' : if (!in_qstr)
1259                                    ++in_com;
1260                                if (addspace == 1)
1261                                    good = good && (*pos++ == ' ');
1262                                good = good && (*pos++ == *ptr);
1263                                addspace = 0;
1264                                break;
1265                     case ')' : if (in_com)
1266                                    --in_com;
1267                                good = good && (*pos++ == *ptr);
1268                                addspace = 0;
1269                                break;
1270                     case ' ' :
1271                     case '\t': if (addspace || !good)
1272                                    break;
1273                                if (in_com || in_qstr)
1274                                    good = (*pos++ == *ptr);
1275                                else
1276                                    addspace = 1;
1277                                break;
1278                     case '=' :
1279                     case '/' :
1280                     case ';' : if (!(in_com || in_qstr))
1281                                    addspace = -1;
1282                                good = good && (*pos++ == *ptr);
1283                                break;
1284                     default  : if (!good)
1285                                    break;
1286                                if (addspace == 1)
1287                                    good = (*pos++ == ' ');
1288                                if (in_com || in_qstr)
1289                                    good = good && (*pos++ == *ptr);
1290                                else
1291                                    good = good && (*pos++ == ap_tolower(*ptr));
1292                                addspace = 0;
1293                                break;
1294                 }
1295             }
1296         }
1297         if (good && *pos)
1298             good = 0;          /* not good if only a prefix was matched */
1299 
1300     } while (*ptr && !good);
1301 
1302     return good;
1303 }
1304 
1305 
1306 /* Retrieve a token, spacing over it and returning a pointer to
1307  * the first non-white byte afterwards.  Note that these tokens
1308  * are delimited by semis and commas; and can also be delimited
1309  * by whitespace at the caller's option.
1310  */
1311 
ap_get_token(pool * p,const char ** accept_line,int accept_white)1312 API_EXPORT(char *) ap_get_token(pool *p, const char **accept_line, int accept_white)
1313 {
1314     const char *ptr = *accept_line;
1315     const char *tok_start;
1316     char *token;
1317     int tok_len;
1318 
1319     /* Find first non-white byte */
1320 
1321     while (ap_isspace(*ptr))
1322 	++ptr;
1323 
1324     tok_start = ptr;
1325 
1326     /* find token end, skipping over quoted strings.
1327      * (comments are already gone).
1328      */
1329 
1330     while (*ptr && (accept_white || !ap_isspace(*ptr))
1331 	   && *ptr != ';' && *ptr != ',') {
1332 	if (*ptr++ == '"')
1333 	    while (*ptr)
1334 		if (*ptr++ == '"')
1335 		    break;
1336     }
1337 
1338     tok_len = ptr - tok_start;
1339     token = ap_pstrndup(p, tok_start, tok_len);
1340 
1341     /* Advance accept_line pointer to the next non-white byte */
1342 
1343     while (ap_isspace(*ptr))
1344 	++ptr;
1345 
1346     *accept_line = ptr;
1347     return token;
1348 }
1349 
1350 
1351 /* find http tokens, see the definition of token from RFC2068 */
ap_find_token(pool * p,const char * line,const char * tok)1352 API_EXPORT(int) ap_find_token(pool *p, const char *line, const char *tok)
1353 {
1354     const unsigned char *start_token;
1355     const unsigned char *s;
1356 
1357     if (!line)
1358 	return 0;
1359 
1360     s = (const unsigned char *)line;
1361     for (;;) {
1362 	/* find start of token, skip all stop characters, note NUL
1363 	 * isn't a token stop, so we don't need to test for it
1364 	 */
1365 	while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1366 	    ++s;
1367 	}
1368 	if (!*s) {
1369 	    return 0;
1370 	}
1371 	start_token = s;
1372 	/* find end of the token */
1373 	while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
1374 	    ++s;
1375 	}
1376 	if (!strncasecmp((const char *)start_token, (const char *)tok, s - start_token)) {
1377 	    return 1;
1378 	}
1379 	if (!*s) {
1380 	    return 0;
1381 	}
1382     }
1383 }
1384 
1385 
ap_find_last_token(pool * p,const char * line,const char * tok)1386 API_EXPORT(int) ap_find_last_token(pool *p, const char *line, const char *tok)
1387 {
1388     int llen, tlen, lidx;
1389 
1390     if (!line)
1391 	return 0;
1392 
1393     llen = strlen(line);
1394     tlen = strlen(tok);
1395     lidx = llen - tlen;
1396 
1397     if ((lidx < 0) ||
1398 	((lidx > 0) && !(ap_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
1399 	return 0;
1400 
1401     return (strncasecmp(&line[lidx], tok, tlen) == 0);
1402 }
1403 
1404 /* c2x takes an unsigned, and expects the caller has guaranteed that
1405  * 0 <= what < 256... which usually means that you have to cast to
1406  * unsigned char first, because (unsigned)(char)(x) first goes through
1407  * signed extension to an int before the unsigned cast.
1408  *
1409  * The reason for this assumption is to assist gcc code generation --
1410  * the unsigned char -> unsigned extension is already done earlier in
1411  * both uses of this code, so there's no need to waste time doing it
1412  * again.
1413  */
1414 static const char c2x_table[] = "0123456789abcdef";
1415 
c2x(unsigned what,unsigned char * where)1416 static ap_inline unsigned char *c2x(unsigned what, unsigned char *where)
1417 {
1418     *where++ = '%';
1419     *where++ = c2x_table[what >> 4];
1420     *where++ = c2x_table[what & 0xf];
1421     return where;
1422 }
1423 
1424 /* escape a string for logging */
ap_escape_logitem(pool * p,const char * str)1425 API_EXPORT(char *) ap_escape_logitem(pool *p, const char *str)
1426 {
1427     char *ret;
1428     unsigned char *d;
1429     const unsigned char *s;
1430 
1431     if (str == NULL)
1432         return NULL;
1433 
1434     ret = ap_palloc(p, 4 * strlen(str) + 1);	/* Be safe */
1435     d = (unsigned char *)ret;
1436     s = (const unsigned char *)str;
1437     for (; *s; ++s) {
1438 
1439 	if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1440 	    *d++ = '\\';
1441             switch(*s) {
1442             case '\b':
1443                 *d++ = 'b';
1444 	        break;
1445             case '\n':
1446                 *d++ = 'n';
1447 	        break;
1448             case '\r':
1449                 *d++ = 'r';
1450 	        break;
1451             case '\t':
1452                 *d++ = 't';
1453 	        break;
1454             case '\v':
1455                 *d++ = 'v';
1456 	        break;
1457             case '\\':
1458             case '"':
1459                 *d++ = *s;
1460 	        break;
1461 	    default:
1462                 c2x(*s, d);
1463 	        *d = 'x';
1464 		d += 3;
1465 	    }
1466 	}
1467 	else
1468             *d++ = *s;
1469     }
1470     *d = '\0';
1471 
1472     return ret;
1473 }
1474 
ap_escape_errorlog_item(char * dest,const char * source,size_t buflen)1475 API_EXPORT(size_t) ap_escape_errorlog_item(char *dest, const char *source,
1476                                            size_t buflen)
1477 {
1478     unsigned char *d, *ep;
1479     const unsigned char *s;
1480 
1481     if (!source || !buflen) { /* be safe */
1482         return 0;
1483     }
1484 
1485     d = (unsigned char *)dest;
1486     s = (const unsigned char *)source;
1487     ep = d + buflen - 1;
1488 
1489     for (; d < ep && *s; ++s) {
1490 
1491         if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
1492             *d++ = '\\';
1493             if (d >= ep) {
1494                 --d;
1495                 break;
1496             }
1497 
1498             switch(*s) {
1499             case '\b':
1500                 *d++ = 'b';
1501                 break;
1502             case '\n':
1503                 *d++ = 'n';
1504                 break;
1505             case '\r':
1506                 *d++ = 'r';
1507                 break;
1508             case '\t':
1509                 *d++ = 't';
1510                 break;
1511             case '\v':
1512                 *d++ = 'v';
1513                 break;
1514             case '\\':
1515                 *d++ = *s;
1516                 break;
1517             case '"': /* no need for this in error log */
1518                 d[-1] = *s;
1519                 break;
1520             default:
1521                 if (d >= ep - 2) {
1522                     ep = --d; /* break the for loop as well */
1523                     break;
1524                 }
1525                 c2x(*s, d);
1526                 *d = 'x';
1527                 d += 3;
1528             }
1529         }
1530         else {
1531             *d++ = *s;
1532         }
1533     }
1534     *d = '\0';
1535 
1536     return (d - (unsigned char *)dest);
1537 }
1538 
ap_escape_shell_cmd(pool * p,const char * str)1539 API_EXPORT(char *) ap_escape_shell_cmd(pool *p, const char *str)
1540 {
1541     char *cmd;
1542     unsigned char *d;
1543     const unsigned char *s;
1544 
1545     cmd = ap_palloc(p, 2 * strlen(str) + 1);	/* Be safe */
1546     d = (unsigned char *)cmd;
1547     s = (const unsigned char *)str;
1548     for (; *s; ++s) {
1549 
1550 	if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) {
1551 	    *d++ = '\\';
1552 	}
1553 	*d++ = *s;
1554     }
1555     *d = '\0';
1556 
1557     return cmd;
1558 }
1559 
x2c(const char * what)1560 static char x2c(const char *what)
1561 {
1562     register char digit;
1563 
1564     digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 : (what[0] - '0'));
1565     digit *= 16;
1566     digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 : (what[1] - '0'));
1567     return (digit);
1568 }
1569 
1570 /*
1571  * Unescapes a URL.
1572  * Returns 0 on success, non-zero on error
1573  * Failure is due to
1574  *   bad % escape       returns BAD_REQUEST
1575  *
1576  *   decoding %00 -> \0  (the null character)
1577  *   decoding %2f -> /   (a special character)
1578  *                      returns NOT_FOUND
1579  */
ap_unescape_url(char * url)1580 API_EXPORT(int) ap_unescape_url(char *url)
1581 {
1582     register int x, y, badesc, badpath;
1583 
1584     badesc = 0;
1585     badpath = 0;
1586     for (x = 0, y = 0; url[y]; ++x, ++y) {
1587 	if (url[y] != '%')
1588 	    url[x] = url[y];
1589 	else {
1590 	    if (!ap_isxdigit(url[y + 1]) || !ap_isxdigit(url[y + 2])) {
1591 		badesc = 1;
1592 		url[x] = '%';
1593 	    }
1594 	    else {
1595 		url[x] = x2c(&url[y + 1]);
1596 		y += 2;
1597 		if (url[x] == '/' || url[x] == '\0')
1598 		    badpath = 1;
1599 	    }
1600 	}
1601     }
1602     url[x] = '\0';
1603     if (badesc)
1604 	return BAD_REQUEST;
1605     else if (badpath)
1606 	return NOT_FOUND;
1607     else
1608 	return OK;
1609 }
1610 
ap_construct_server(pool * p,const char * hostname,unsigned port,const request_rec * r)1611 API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname,
1612 				    unsigned port, const request_rec *r)
1613 {
1614     if (ap_is_default_port(port, r))
1615 	return ap_pstrdup(p, hostname);
1616     else {
1617 	return ap_psprintf(p, "%s:%u", hostname, port);
1618     }
1619 }
1620 
1621 /*
1622  * escape_path_segment() escapes a path segment, as defined in RFC 1808. This
1623  * routine is (should be) OS independent.
1624  *
1625  * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
1626  * cases if a ':' occurs before the first '/' in the URL, the URL should be
1627  * prefixed with "./" (or the ':' escaped). In the case of Unix, this means
1628  * leaving '/' alone, but otherwise doing what escape_path_segment() does. For
1629  * efficiency reasons, we don't use escape_path_segment(), which is provided for
1630  * reference. Again, RFC 1808 is where this stuff is defined.
1631  *
1632  * If partial is set, os_escape_path() assumes that the path will be appended to
1633  * something with a '/' in it (and thus does not prefix "./").
1634  */
1635 
ap_escape_path_segment(pool * p,const char * segment)1636 API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *segment)
1637 {
1638     char *copy = ap_palloc(p, 3 * strlen(segment) + 1);
1639     const unsigned char *s = (const unsigned char *)segment;
1640     unsigned char *d = (unsigned char *)copy;
1641     unsigned c;
1642 
1643     while ((c = *s)) {
1644 	if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) {
1645 	    d = c2x(c, d);
1646 	}
1647 	else {
1648 	    *d++ = c;
1649 	}
1650 	++s;
1651     }
1652     *d = '\0';
1653     return copy;
1654 }
1655 
ap_os_escape_path(pool * p,const char * path,int partial)1656 API_EXPORT(char *) ap_os_escape_path(pool *p, const char *path, int partial)
1657 {
1658     char *copy = ap_palloc(p, 3 * strlen(path) + 3);
1659     const unsigned char *s = (const unsigned char *)path;
1660     unsigned char *d = (unsigned char *)copy;
1661     unsigned c;
1662 
1663     if (!partial) {
1664 	char *colon = strchr(path, ':');
1665 	char *slash = strchr(path, '/');
1666 
1667 	if (colon && (!slash || colon < slash)) {
1668 	    *d++ = '.';
1669 	    *d++ = '/';
1670 	}
1671     }
1672     while ((c = *s)) {
1673 	if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) {
1674 	    d = c2x(c, d);
1675 	}
1676 	else {
1677 	    *d++ = c;
1678 	}
1679 	++s;
1680     }
1681     *d = '\0';
1682     return copy;
1683 }
1684 
1685 /* ap_escape_uri is now a macro for os_escape_path */
1686 
ap_escape_html(pool * p,const char * s)1687 API_EXPORT(char *) ap_escape_html(pool *p, const char *s)
1688 {
1689     int i, j;
1690     char *x;
1691 
1692     /* first, count the number of extra characters */
1693     for (i = 0, j = 0; s[i] != '\0'; i++)
1694 	if (s[i] == '<' || s[i] == '>')
1695 	    j += 3;
1696 	else if (s[i] == '&')
1697 	    j += 4;
1698 	else if (s[i] == '"')
1699 	    j += 5;
1700 
1701     if (j == 0)
1702 	return ap_pstrndup(p, s, i);
1703 
1704     x = ap_palloc(p, i + j + 1);
1705     for (i = 0, j = 0; s[i] != '\0'; i++, j++)
1706 	if (s[i] == '<') {
1707 	    memcpy(&x[j], "&lt;", 4);
1708 	    j += 3;
1709 	}
1710 	else if (s[i] == '>') {
1711 	    memcpy(&x[j], "&gt;", 4);
1712 	    j += 3;
1713 	}
1714 	else if (s[i] == '&') {
1715 	    memcpy(&x[j], "&amp;", 5);
1716 	    j += 4;
1717 	}
1718 	else if (s[i] == '"') {
1719 	    memcpy(&x[j], "&quot;", 6);
1720 	    j += 5;
1721 	}
1722 	else
1723 	    x[j] = s[i];
1724 
1725     x[j] = '\0';
1726     return x;
1727 }
1728 
ap_is_directory(const char * path)1729 API_EXPORT(int) ap_is_directory(const char *path)
1730 {
1731     struct stat finfo;
1732 
1733     if (stat(path, &finfo) == -1)
1734 	return 0;		/* in error condition, just return no */
1735 
1736     return (S_ISDIR(finfo.st_mode));
1737 }
1738 
1739 /*
1740  * see ap_is_directory() except this one is symlink aware, so it
1741  * checks for a "real" directory
1742  */
ap_is_rdirectory(const char * path)1743 API_EXPORT(int) ap_is_rdirectory(const char *path)
1744 {
1745     struct stat finfo;
1746 
1747     if (lstat(path, &finfo) == -1)
1748 	return 0;		/* in error condition, just return no */
1749 
1750     return ((!(S_ISLNK(finfo.st_mode))) && (S_ISDIR(finfo.st_mode)));
1751 }
1752 
ap_make_full_path(pool * a,const char * src1,const char * src2)1753 API_EXPORT(char *) ap_make_full_path(pool *a, const char *src1,
1754 				  const char *src2)
1755 {
1756     register int x;
1757 
1758     x = strlen(src1);
1759     if (x == 0)
1760 	return ap_pstrcat(a, "/", src2, NULL);
1761 
1762     if (src1[x - 1] != '/')
1763 	return ap_pstrcat(a, src1, "/", src2, NULL);
1764     else
1765 	return ap_pstrcat(a, src1, src2, NULL);
1766 }
1767 
1768 /*
1769  * Check for an absoluteURI syntax (see section 3.2 in RFC2068).
1770  */
ap_is_url(const char * u)1771 API_EXPORT(int) ap_is_url(const char *u)
1772 {
1773     register int x;
1774 
1775     for (x = 0; u[x] != ':'; x++) {
1776 	if ((!u[x]) ||
1777 	    ((!ap_isalpha(u[x])) && (!isdigit((unsigned char)u[x])) &&
1778 	     (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) {
1779 	    return 0;
1780 	}
1781     }
1782 
1783     return (x ? 1 : 0);		/* If the first character is ':', it's broken, too */
1784 }
1785 
ap_can_exec(const struct stat * finfo)1786 API_EXPORT(int) ap_can_exec(const struct stat *finfo)
1787 {
1788     if (ap_user_id == finfo->st_uid)
1789 	if (finfo->st_mode & S_IXUSR)
1790 	    return 1;
1791     if (ap_group_id == finfo->st_gid)
1792 	if (finfo->st_mode & S_IXGRP)
1793 	    return 1;
1794     return ((finfo->st_mode & S_IXOTH) != 0);
1795 }
1796 
ap_ind(const char * s,char c)1797 API_EXPORT(int) ap_ind(const char *s, char c)
1798 {
1799     register int x;
1800 
1801     for (x = 0; s[x]; x++)
1802 	if (s[x] == c)
1803 	    return x;
1804 
1805     return -1;
1806 }
1807 
ap_rind(const char * s,char c)1808 API_EXPORT(int) ap_rind(const char *s, char c)
1809 {
1810     register int x;
1811 
1812     for (x = strlen(s) - 1; x != -1; x--)
1813 	if (s[x] == c)
1814 	    return x;
1815 
1816     return -1;
1817 }
1818 
ap_str_tolower(char * str)1819 API_EXPORT(void) ap_str_tolower(char *str)
1820 {
1821     while (*str) {
1822 	*str = ap_tolower(*str);
1823 	++str;
1824     }
1825 }
1826 
ap_uname2id(const char * name)1827 API_EXPORT(uid_t) ap_uname2id(const char *name)
1828 {
1829     struct passwd *ent;
1830 
1831     if (name[0] == '#')
1832 	return (atoi(&name[1]));
1833 
1834     if (!(ent = getpwnam(name))) {
1835 	fprintf(stderr, "%s: bad user name %s\n", ap_server_argv0, name);
1836 	exit(1);
1837     }
1838     return (ent->pw_uid);
1839 }
1840 
ap_gname2id(const char * name)1841 API_EXPORT(gid_t) ap_gname2id(const char *name)
1842 {
1843     struct group *ent;
1844 
1845     if (name[0] == '#')
1846 	return (atoi(&name[1]));
1847 
1848     if (!(ent = getgrnam(name))) {
1849 	fprintf(stderr, "%s: bad group name %s\n", ap_server_argv0, name);
1850 	exit(1);
1851     }
1852     return (ent->gr_gid);
1853 }
1854 
1855 
1856 /*
1857  * Parses a host of the form <address>[:port]
1858  * :port is permitted if 'port' is not NULL
1859  */
ap_get_virthost_addr(char * w,unsigned short * ports)1860 API_EXPORT(struct sockaddr *) ap_get_virthost_addr(char *w, unsigned short *ports)
1861 {
1862     static struct sockaddr_storage ss;
1863     struct addrinfo hints, *res;
1864     char *p, *r;
1865     char *host;
1866     char *port = "0";
1867     int error;
1868     char servbuf[NI_MAXSERV];
1869 
1870     if (w == NULL)
1871 	w = "*";
1872     p = r = NULL;
1873     if (*w == '['){
1874 	if ((r = strrchr(w+1, ']'))) {
1875 	    *r = '\0';
1876 	    p = r + 1;
1877 	    switch(*p){
1878 	    case ':':
1879 	      p++;
1880 	      /* nobreak; */
1881 	    case '\0':
1882 	      w++;
1883 	      break;
1884 	    default:
1885 	      p = NULL;
1886 	    }
1887 	}
1888     }
1889     else{
1890 	p = strchr(w, ':');
1891 	if (p != NULL && strchr(p+1, ':') != NULL)
1892 	    p = NULL;
1893     }
1894     if (ports != NULL) {
1895 	if (p != NULL && *p && strcmp(p + 1, "*") != 0)
1896 	    port = p + 1;
1897     }
1898 
1899     memset(&hints, 0, sizeof(hints));
1900     hints.ai_socktype = SOCK_STREAM;
1901     if (p != NULL)
1902 	*p = '\0';
1903     if (strcmp(w, "*") == 0) {
1904 	host = NULL;
1905 	hints.ai_flags = AI_PASSIVE;
1906 	hints.ai_family = ap_default_family;
1907     } else {
1908 	host = w;
1909 	hints.ai_family = PF_UNSPEC;
1910     }
1911 
1912     error = getaddrinfo(host, port, &hints, &res);
1913 
1914     if (error || !res) {
1915 	fprintf(stderr, "ap_get_vitrhost_addr(): getaddrinfo(%s):%s --- exiting!\n", w, gai_strerror(error));
1916 	exit(1);
1917     }
1918 
1919     if (res->ai_next) {
1920 	fprintf(stderr, "ap_get_vitrhost_addr(): Host %s has multiple addresses ---\n", w);
1921 	fprintf(stderr, "you must choose one explicitly for use as\n");
1922 	fprintf(stderr, "a virtual host.  Exiting!!!\n");
1923 	exit(1);
1924     }
1925 
1926     if (r != NULL)
1927 	*r = ']';
1928     if (p != NULL)
1929 	*p = ':';
1930 
1931     memcpy(&ss, res->ai_addr, res->ai_addrlen);
1932     if (getnameinfo(res->ai_addr, res->ai_addrlen,
1933 		    NULL, 0, servbuf, sizeof(servbuf),
1934 		    NI_NUMERICSERV)){
1935 	fprintf(stderr, "ap_get_virthost_addr(): getnameinfo() failed --- Exiting!!!\n");
1936 	exit(1);
1937     }
1938     if (ports) *ports = atoi(servbuf);
1939     freeaddrinfo(res);
1940     return (struct sockaddr *)&ss;
1941 }
1942 
1943 
ap_get_local_host(pool * a)1944 API_EXPORT(char *) ap_get_local_host(pool *a)
1945 {
1946 #ifndef MAXHOSTNAMELEN
1947 #define MAXHOSTNAMELEN 256
1948 #endif
1949     char str[MAXHOSTNAMELEN];
1950     char *server_hostname = NULL;
1951     struct addrinfo hints, *res;
1952     int error;
1953 
1954     if (gethostname(str, sizeof(str) - 1) != 0) {
1955 	ap_log_error(APLOG_MARK, APLOG_WARNING, NULL,
1956 	             "%s: gethostname() failed to determine ServerName\n",
1957                      ap_server_argv0);
1958     }
1959     else
1960     {
1961 	str[sizeof(str) - 1] = '\0';
1962 	memset(&hints, 0, sizeof(hints));
1963 	hints.ai_family = PF_UNSPEC;
1964 	hints.ai_flags = AI_CANONNAME;
1965 	res = NULL;
1966 	error = getaddrinfo(str, NULL, &hints, &res);
1967 	if (error == 0 && res) {
1968 	    /* Since we found a fdqn, return it with no logged message. */
1969 	    server_hostname = ap_pstrdup(a, res->ai_canonname);
1970 	    freeaddrinfo(res);
1971 	    return server_hostname;
1972 	} else {
1973 	    /* Recovery - return the default servername by IP: */
1974 	    server_hostname = ap_pstrdup(a, str);
1975 	    /* We will drop through to report the IP-named server */
1976 	}
1977     }
1978 
1979     /* If we don't have an fqdn or IP, fall back to the loopback addr */
1980     if (!server_hostname)
1981         server_hostname = ap_pstrdup(a, "127.0.0.1");
1982 
1983     ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_NOERRNO, NULL,
1984 	         "%s: Could not determine the server's fully qualified "
1985                  "domain name, using %s for ServerName",
1986                  ap_server_argv0, server_hostname);
1987 
1988     if (res)
1989 	freeaddrinfo(res);
1990     return server_hostname;
1991 }
1992 
1993 /* simple 'pool' alloc()ing glue to ap_base64.c
1994  */
ap_pbase64decode(pool * p,const char * bufcoded)1995 API_EXPORT(char *) ap_pbase64decode(pool *p, const char *bufcoded)
1996 {
1997     char *decoded;
1998     int l;
1999 
2000     decoded = (char *) ap_palloc(p, 1 + ap_base64decode_len(bufcoded));
2001     l = ap_base64decode(decoded, bufcoded);
2002     decoded[l] = '\0'; /* make binary sequence into string */
2003 
2004     return decoded;
2005 }
2006 
ap_pbase64encode(pool * p,char * string)2007 API_EXPORT(char *) ap_pbase64encode(pool *p, char *string)
2008 {
2009     char *encoded;
2010     int l = strlen(string);
2011 
2012     encoded = (char *) ap_palloc(p, 1 + ap_base64encode_len(l));
2013     l = ap_base64encode(encoded, string, l);
2014     encoded[l] = '\0'; /* make binary sequence into string */
2015 
2016     return encoded;
2017 }
2018 
2019 /* deprecated names for the above two functions, here for compatibility
2020  */
ap_uudecode(pool * p,const char * bufcoded)2021 API_EXPORT(char *) ap_uudecode(pool *p, const char *bufcoded)
2022 {
2023     return ap_pbase64decode(p, bufcoded);
2024 }
2025 
ap_uuencode(pool * p,char * string)2026 API_EXPORT(char *) ap_uuencode(pool *p, char *string)
2027 {
2028     return ap_pbase64encode(p, string);
2029 }
2030 
2031 
2032 /* we want to downcase the type/subtype for comparison purposes
2033  * but nothing else because ;parameter=foo values are case sensitive.
2034  * XXX: in truth we want to downcase parameter names... but really,
2035  * apache has never handled parameters and such correctly.  You
2036  * also need to compress spaces and such to be able to compare
2037  * properly. -djg
2038  */
ap_content_type_tolower(char * str)2039 API_EXPORT(void) ap_content_type_tolower(char *str)
2040 {
2041     char *semi;
2042 
2043     semi = strchr(str, ';');
2044     if (semi) {
2045 	*semi = '\0';
2046     }
2047     while (*str) {
2048 	*str = ap_tolower(*str);
2049 	++str;
2050     }
2051     if (semi) {
2052 	*semi = ';';
2053     }
2054 }
2055 
2056 /*
2057  * Given a string, replace any bare " with \" .
2058  */
ap_escape_quotes(pool * p,const char * instring)2059 API_EXPORT(char *) ap_escape_quotes (pool *p, const char *instring)
2060 {
2061     int newlen = 0;
2062     const char *inchr = instring;
2063     char *outchr, *outstring;
2064 
2065     /*
2066      * Look through the input string, jogging the length of the output
2067      * string up by an extra byte each time we find an unescaped ".
2068      */
2069     while (*inchr != '\0') {
2070 	newlen++;
2071         if (*inchr == '"') {
2072 	    newlen++;
2073 	}
2074 	/*
2075 	 * If we find a slosh, and it's not the last byte in the string,
2076 	 * it's escaping something - advance past both bytes.
2077 	 */
2078 	if ((*inchr == '\\') && (inchr[1] != '\0')) {
2079 	    inchr++;
2080 	    newlen++;
2081 	}
2082 	inchr++;
2083     }
2084     outstring = ap_palloc(p, newlen + 1);
2085     inchr = instring;
2086     outchr = outstring;
2087     /*
2088      * Now copy the input string to the output string, inserting a slosh
2089      * in front of every " that doesn't already have one.
2090      */
2091     while (*inchr != '\0') {
2092 	if ((*inchr == '\\') && (inchr[1] != '\0')) {
2093 	    *outchr++ = *inchr++;
2094 	    *outchr++ = *inchr++;
2095 	}
2096 	if (*inchr == '"') {
2097 	    *outchr++ = '\\';
2098 	}
2099 	if (*inchr != '\0') {
2100 	    *outchr++ = *inchr++;
2101 	}
2102     }
2103     *outchr = '\0';
2104     return outstring;
2105 }
2106 
2107 /* dest = src with whitespace removed
2108  * length of dest assumed >= length of src
2109  */
ap_remove_spaces(char * dest,char * src)2110 API_EXPORT(void) ap_remove_spaces(char *dest, char *src)
2111 {
2112     while (*src) {
2113         if (!ap_isspace(*src))
2114             *dest++ = *src;
2115         src++;
2116     }
2117     *dest = 0;
2118 }
2119 
2120 #ifdef NEED_GETADDRINFO
2121 #include "getaddrinfo.c"
2122 #endif
2123 
2124 #ifdef NEED_GETNAMEINFO
2125 #include "getnameinfo.c"
2126 #endif
2127