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], "<", 4);
1708 j += 3;
1709 }
1710 else if (s[i] == '>') {
1711 memcpy(&x[j], ">", 4);
1712 j += 3;
1713 }
1714 else if (s[i] == '&') {
1715 memcpy(&x[j], "&", 5);
1716 j += 4;
1717 }
1718 else if (s[i] == '"') {
1719 memcpy(&x[j], """, 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