1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* 18 * util.c: string utility things 19 * 20 * 3/21/93 Rob McCool 21 * 1995-96 Many changes by the Apache Software Foundation 22 * 23 */ 24 25/* Debugging aid: 26 * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls 27 * #define DEBUG_CFG_LINES to trace every line read from the config files 28 */ 29 30#include "apr.h" 31#include "apr_strings.h" 32#include "apr_lib.h" 33 34#define APR_WANT_STDIO 35#define APR_WANT_STRFUNC 36#include "apr_want.h" 37 38#if APR_HAVE_UNISTD_H 39#include <unistd.h> 40#endif 41#if APR_HAVE_NETDB_H 42#include <netdb.h> /* for gethostbyname() */ 43#endif 44 45#define CORE_PRIVATE 46 47#include "ap_config.h" 48#include "apr_base64.h" 49#include "httpd.h" 50#include "http_main.h" 51#include "http_log.h" 52#include "http_protocol.h" 53#include "http_config.h" 54#include "util_ebcdic.h" 55 56#ifdef HAVE_PWD_H 57#include <pwd.h> 58#endif 59#ifdef HAVE_GRP_H 60#include <grp.h> 61#endif 62 63/* A bunch of functions in util.c scan strings looking for certain characters. 64 * To make that more efficient we encode a lookup table. The test_char_table 65 * is generated automatically by gen_test_char.c. 66 */ 67#include "test_char.h" 68 69/* we assume the folks using this ensure 0 <= c < 256... which means 70 * you need a cast to (unsigned char) first, you can't just plug a 71 * char in here and get it to work, because if char is signed then it 72 * will first be sign extended. 73 */ 74#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f)) 75 76/* Win32/NetWare/OS2 need to check for both forward and back slashes 77 * in ap_getparents() and ap_escape_url. 78 */ 79#ifdef CASE_BLIND_FILESYSTEM 80#define IS_SLASH(s) ((s == '/') || (s == '\\')) 81#else 82#define IS_SLASH(s) (s == '/') 83#endif 84 85/* same as APR_SIZE_MAX which doesn't appear until APR 1.3 */ 86#define UTIL_SIZE_MAX (~((apr_size_t)0)) 87 88/* 89 * Examine a field value (such as a media-/content-type) string and return 90 * it sans any parameters; e.g., strip off any ';charset=foo' and the like. 91 */ 92AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype) 93{ 94 const char *semi; 95 96 if (intype == NULL) return NULL; 97 98 semi = ap_strchr_c(intype, ';'); 99 if (semi == NULL) { 100 return apr_pstrdup(p, intype); 101 } 102 else { 103 while ((semi > intype) && apr_isspace(semi[-1])) { 104 semi--; 105 } 106 return apr_pstrndup(p, intype, semi - intype); 107 } 108} 109 110AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt, 111 int gmt) 112{ 113 apr_size_t retcode; 114 char ts[MAX_STRING_LEN]; 115 char tf[MAX_STRING_LEN]; 116 apr_time_exp_t xt; 117 118 if (gmt) { 119 const char *f; 120 char *strp; 121 122 apr_time_exp_gmt(&xt, t); 123 /* Convert %Z to "GMT" and %z to "+0000"; 124 * on hosts that do not have a time zone string in struct tm, 125 * strftime must assume its argument is local time. 126 */ 127 for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f) 128 ; f++, strp++) { 129 if (*f != '%') continue; 130 switch (f[1]) { 131 case '%': 132 *++strp = *++f; 133 break; 134 case 'Z': 135 *strp++ = 'G'; 136 *strp++ = 'M'; 137 *strp = 'T'; 138 f++; 139 break; 140 case 'z': /* common extension */ 141 *strp++ = '+'; 142 *strp++ = '0'; 143 *strp++ = '0'; 144 *strp++ = '0'; 145 *strp = '0'; 146 f++; 147 break; 148 } 149 } 150 *strp = '\0'; 151 fmt = tf; 152 } 153 else { 154 apr_time_exp_lt(&xt, t); 155 } 156 157 /* check return code? */ 158 apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt); 159 ts[MAX_STRING_LEN - 1] = '\0'; 160 return apr_pstrdup(p, ts); 161} 162 163/* Roy owes Rob beer. */ 164/* Rob owes Roy dinner. */ 165 166/* These legacy comments would make a lot more sense if Roy hadn't 167 * replaced the old later_than() routine with util_date.c. 168 * 169 * Well, okay, they still wouldn't make any sense. 170 */ 171 172/* Match = 0, NoMatch = 1, Abort = -1 173 * Based loosely on sections of wildmat.c by Rich Salz 174 * Hmmm... shouldn't this really go component by component? 175 */ 176AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) 177{ 178 int x, y; 179 180 for (x = 0, y = 0; expected[y]; ++y, ++x) { 181 if ((!str[x]) && (expected[y] != '*')) 182 return -1; 183 if (expected[y] == '*') { 184 while (expected[++y] == '*'); 185 if (!expected[y]) 186 return 0; 187 while (str[x]) { 188 int ret; 189 if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1) 190 return ret; 191 } 192 return -1; 193 } 194 else if ((expected[y] != '?') && (str[x] != expected[y])) 195 return 1; 196 } 197 return (str[x] != '\0'); 198} 199 200AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected) 201{ 202 int x, y; 203 204 for (x = 0, y = 0; expected[y]; ++y, ++x) { 205 if (!str[x] && expected[y] != '*') 206 return -1; 207 if (expected[y] == '*') { 208 while (expected[++y] == '*'); 209 if (!expected[y]) 210 return 0; 211 while (str[x]) { 212 int ret; 213 if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1) 214 return ret; 215 } 216 return -1; 217 } 218 else if (expected[y] != '?' 219 && apr_tolower(str[x]) != apr_tolower(expected[y])) 220 return 1; 221 } 222 return (str[x] != '\0'); 223} 224 225/* We actually compare the canonical root to this root, (but we don't 226 * waste time checking the case), since every use of this function in 227 * httpd-2.1 tests if the path is 'proper', meaning we've already passed 228 * it through apr_filepath_merge, or we haven't. 229 */ 230AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir) 231{ 232 const char *newpath; 233 const char *ourdir = dir; 234 if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS 235 || strncmp(newpath, ourdir, strlen(newpath)) != 0) { 236 return 0; 237 } 238 return 1; 239} 240 241AP_DECLARE(int) ap_is_matchexp(const char *str) 242{ 243 register int x; 244 245 for (x = 0; str[x]; x++) 246 if ((str[x] == '*') || (str[x] == '?')) 247 return 1; 248 return 0; 249} 250 251/* 252 * Here's a pool-based interface to the POSIX-esque ap_regcomp(). 253 * Note that we return ap_regex_t instead of being passed one. 254 * The reason is that if you use an already-used ap_regex_t structure, 255 * the memory that you've already allocated gets forgotten, and 256 * regfree() doesn't clear it. So we don't allow it. 257 */ 258 259static apr_status_t regex_cleanup(void *preg) 260{ 261 ap_regfree((ap_regex_t *) preg); 262 return APR_SUCCESS; 263} 264 265AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern, 266 int cflags) 267{ 268 ap_regex_t *preg = apr_palloc(p, sizeof *preg); 269 270 if (ap_regcomp(preg, pattern, cflags)) { 271 return NULL; 272 } 273 274 apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, 275 apr_pool_cleanup_null); 276 277 return preg; 278} 279 280AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg) 281{ 282 ap_regfree(reg); 283 apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup); 284} 285 286/* 287 * Similar to standard strstr() but we ignore case in this version. 288 * Based on the strstr() implementation further below. 289 */ 290AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2) 291{ 292 char *p1, *p2; 293 if (*s2 == '\0') { 294 /* an empty s2 */ 295 return((char *)s1); 296 } 297 while(1) { 298 for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++); 299 if (*s1 == '\0') { 300 return(NULL); 301 } 302 /* found first character of s2, see if the rest matches */ 303 p1 = (char *)s1; 304 p2 = (char *)s2; 305 for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) { 306 if (*p1 == '\0') { 307 /* both strings ended together */ 308 return((char *)s1); 309 } 310 } 311 if (*p2 == '\0') { 312 /* second string ended, a match */ 313 break; 314 } 315 /* didn't find a match here, try starting at next character in s1 */ 316 s1++; 317 } 318 return((char *)s1); 319} 320 321/* 322 * Returns an offsetted pointer in bigstring immediately after 323 * prefix. Returns bigstring if bigstring doesn't start with 324 * prefix or if prefix is longer than bigstring while still matching. 325 * NOTE: pointer returned is relative to bigstring, so we 326 * can use standard pointer comparisons in the calling function 327 * (eg: test if ap_stripprefix(a,b) == a) 328 */ 329AP_DECLARE(const char *) ap_stripprefix(const char *bigstring, 330 const char *prefix) 331{ 332 const char *p1; 333 334 if (*prefix == '\0') 335 return bigstring; 336 337 p1 = bigstring; 338 while (*p1 && *prefix) { 339 if (*p1++ != *prefix++) 340 return bigstring; 341 } 342 if (*prefix == '\0') 343 return p1; 344 345 /* hit the end of bigstring! */ 346 return bigstring; 347} 348 349/* This function substitutes for $0-$9, filling in regular expression 350 * submatches. Pass it the same nmatch and pmatch arguments that you 351 * passed ap_regexec(). pmatch should not be greater than the maximum number 352 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t. 353 * 354 * input should be the string with the $-expressions, source should be the 355 * string that was matched against. 356 * 357 * It returns the substituted string, or NULL on error. 358 * 359 * Parts of this code are based on Henry Spencer's regsub(), from his 360 * AT&T V8 regexp package. 361 */ 362 363static apr_status_t regsub_core(apr_pool_t *p, char **result, 364 const char *input, 365 const char *source, apr_size_t nmatch, 366 ap_regmatch_t pmatch[], apr_size_t maxlen) 367{ 368 const char *src = input; 369 char *dst; 370 char c; 371 apr_size_t no; 372 apr_size_t len = 0; 373 374 AP_DEBUG_ASSERT(result && p); 375 if (!source || nmatch>AP_MAX_REG_MATCH) 376 return APR_EINVAL; 377 if (!nmatch) { 378 len = strlen(src); 379 if (maxlen > 0 && len >= maxlen) 380 return APR_ENOMEM; 381 *result = apr_pstrmemdup(p, src, len); 382 return APR_SUCCESS; 383 } 384 385 /* First pass, find the size */ 386 while ((c = *src++) != '\0') { 387 if (c == '&') 388 no = 0; 389 else if (c == '$' && apr_isdigit(*src)) 390 no = *src++ - '0'; 391 else 392 no = AP_MAX_REG_MATCH; 393 394 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */ 395 if (c == '\\' && (*src == '$' || *src == '&')) 396 src++; 397 len++; 398 } 399 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { 400 if (UTIL_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so) { 401 ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, 402 "integer overflow or out of memory condition." ); 403 return APR_ENOMEM; 404 } 405 len += pmatch[no].rm_eo - pmatch[no].rm_so; 406 } 407 408 } 409 410 if (len >= maxlen && maxlen > 0) 411 return APR_ENOMEM; 412 413 *result = dst = apr_palloc(p, len + 1); 414 415 /* Now actually fill in the string */ 416 417 src = input; 418 419 while ((c = *src++) != '\0') { 420 if (c == '&') 421 no = 0; 422 else if (c == '$' && apr_isdigit(*src)) 423 no = *src++ - '0'; 424 else 425 no = AP_MAX_REG_MATCH; 426 427 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */ 428 if (c == '\\' && (*src == '$' || *src == '&')) 429 c = *src++; 430 *dst++ = c; 431 } 432 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { 433 len = pmatch[no].rm_eo - pmatch[no].rm_so; 434 memcpy(dst, source + pmatch[no].rm_so, len); 435 dst += len; 436 } 437 438 } 439 *dst = '\0'; 440 441 return APR_SUCCESS; 442} 443 444#ifndef AP_PREGSUB_MAXLEN 445/* No API control so far in this released branch, so make it large */ 446#define AP_PREGSUB_MAXLEN (64 * 1024 * 1024) 447#endif 448AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, 449 const char *source, size_t nmatch, 450 ap_regmatch_t pmatch[]) 451{ 452 char *result; 453 apr_status_t rc = regsub_core(p, &result, input, source, nmatch, 454 pmatch, AP_PREGSUB_MAXLEN); 455 if (rc != APR_SUCCESS) 456 result = NULL; 457 return result; 458} 459 460AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result, 461 const char *input, const char *source, 462 apr_size_t nmatch, ap_regmatch_t pmatch[], 463 apr_size_t maxlen) 464{ 465 apr_status_t rc = regsub_core(p, result, input, source, nmatch, 466 pmatch, maxlen); 467 if (rc != APR_SUCCESS) 468 *result = NULL; 469 return rc; 470} 471 472/* 473 * Parse .. so we don't compromise security 474 */ 475AP_DECLARE(void) ap_getparents(char *name) 476{ 477 char *next; 478 int l, w, first_dot; 479 480 /* Four paseses, as per RFC 1808 */ 481 /* a) remove ./ path segments */ 482 for (next = name; *next && (*next != '.'); next++) { 483 } 484 485 l = w = first_dot = next - name; 486 while (name[l] != '\0') { 487 if (name[l] == '.' && IS_SLASH(name[l + 1]) 488 && (l == 0 || IS_SLASH(name[l - 1]))) 489 l += 2; 490 else 491 name[w++] = name[l++]; 492 } 493 494 /* b) remove trailing . path, segment */ 495 if (w == 1 && name[0] == '.') 496 w--; 497 else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2])) 498 w--; 499 name[w] = '\0'; 500 501 /* c) remove all xx/../ segments. (including leading ../ and /../) */ 502 l = first_dot; 503 504 while (name[l] != '\0') { 505 if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2]) 506 && (l == 0 || IS_SLASH(name[l - 1]))) { 507 register int m = l + 3, n; 508 509 l = l - 2; 510 if (l >= 0) { 511 while (l >= 0 && !IS_SLASH(name[l])) 512 l--; 513 l++; 514 } 515 else 516 l = 0; 517 n = l; 518 while ((name[n] = name[m])) 519 (++n, ++m); 520 } 521 else 522 ++l; 523 } 524 525 /* d) remove trailing xx/.. segment. */ 526 if (l == 2 && name[0] == '.' && name[1] == '.') 527 name[0] = '\0'; 528 else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' 529 && IS_SLASH(name[l - 3])) { 530 l = l - 4; 531 if (l >= 0) { 532 while (l >= 0 && !IS_SLASH(name[l])) 533 l--; 534 l++; 535 } 536 else 537 l = 0; 538 name[l] = '\0'; 539 } 540} 541 542AP_DECLARE(void) ap_no2slash(char *name) 543{ 544 char *d, *s; 545 546 s = d = name; 547 548#ifdef HAVE_UNC_PATHS 549 /* Check for UNC names. Leave leading two slashes. */ 550 if (s[0] == '/' && s[1] == '/') 551 *d++ = *s++; 552#endif 553 554 while (*s) { 555 if ((*d++ = *s) == '/') { 556 do { 557 ++s; 558 } while (*s == '/'); 559 } 560 else { 561 ++s; 562 } 563 } 564 *d = '\0'; 565} 566 567 568/* 569 * copy at most n leading directories of s into d 570 * d should be at least as large as s plus 1 extra byte 571 * assumes n > 0 572 * the return value is the ever useful pointer to the trailing \0 of d 573 * 574 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments, 575 * so that if n == 0, "/" is returned in d with n == 1 576 * and s == "e:/test.html", "e:/" is returned in d 577 * *** See also directory_walk in modules/http/http_request.c 578 579 * examples: 580 * /a/b, 0 ==> / (true for all platforms) 581 * /a/b, 1 ==> / 582 * /a/b, 2 ==> /a/ 583 * /a/b, 3 ==> /a/b/ 584 * /a/b, 4 ==> /a/b/ 585 * 586 * c:/a/b 0 ==> / 587 * c:/a/b 1 ==> c:/ 588 * c:/a/b 2 ==> c:/a/ 589 * c:/a/b 3 ==> c:/a/b 590 * c:/a/b 4 ==> c:/a/b 591 */ 592AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n) 593{ 594 if (n < 1) { 595 *d = '/'; 596 *++d = '\0'; 597 return (d); 598 } 599 600 for (;;) { 601 if (*s == '\0' || (*s == '/' && (--n) == 0)) { 602 *d = '/'; 603 break; 604 } 605 *d++ = *s++; 606 } 607 *++d = 0; 608 return (d); 609} 610 611 612/* 613 * return the parent directory name including trailing / of the file s 614 */ 615AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s) 616{ 617 const char *last_slash = ap_strrchr_c(s, '/'); 618 char *d; 619 int l; 620 621 if (last_slash == NULL) { 622 return apr_pstrdup(p, ""); 623 } 624 l = (last_slash - s) + 1; 625 d = apr_palloc(p, l + 1); 626 memcpy(d, s, l); 627 d[l] = 0; 628 return (d); 629} 630 631 632AP_DECLARE(int) ap_count_dirs(const char *path) 633{ 634 register int x, n; 635 636 for (x = 0, n = 0; path[x]; x++) 637 if (path[x] == '/') 638 n++; 639 return n; 640} 641 642AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop) 643{ 644 return ap_getword(atrans, (const char **) line, stop); 645} 646 647AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop) 648{ 649 const char *pos = *line; 650 int len; 651 char *res; 652 653 while ((*pos != stop) && *pos) { 654 ++pos; 655 } 656 657 len = pos - *line; 658 res = (char *)apr_palloc(atrans, len + 1); 659 memcpy(res, *line, len); 660 res[len] = 0; 661 662 if (stop) { 663 while (*pos == stop) { 664 ++pos; 665 } 666 } 667 *line = pos; 668 669 return res; 670} 671 672AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line) 673{ 674 return ap_getword_white(atrans, (const char **) line); 675} 676 677AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line) 678{ 679 const char *pos = *line; 680 int len; 681 char *res; 682 683 while (!apr_isspace(*pos) && *pos) { 684 ++pos; 685 } 686 687 len = pos - *line; 688 res = (char *)apr_palloc(atrans, len + 1); 689 memcpy(res, *line, len); 690 res[len] = 0; 691 692 while (apr_isspace(*pos)) { 693 ++pos; 694 } 695 696 *line = pos; 697 698 return res; 699} 700 701AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line, 702 char stop) 703{ 704 return ap_getword_nulls(atrans, (const char **) line, stop); 705} 706 707AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line, 708 char stop) 709{ 710 const char *pos = ap_strchr_c(*line, stop); 711 char *res; 712 713 if (!pos) { 714 res = apr_pstrdup(atrans, *line); 715 *line += strlen(*line); 716 return res; 717 } 718 719 res = apr_pstrndup(atrans, *line, pos - *line); 720 721 ++pos; 722 723 *line = pos; 724 725 return res; 726} 727 728/* Get a word, (new) config-file style --- quoted strings and backslashes 729 * all honored 730 */ 731 732static char *substring_conf(apr_pool_t *p, const char *start, int len, 733 char quote) 734{ 735 char *result = apr_palloc(p, len + 2); 736 char *resp = result; 737 int i; 738 739 for (i = 0; i < len; ++i) { 740 if (start[i] == '\\' && (start[i + 1] == '\\' 741 || (quote && start[i + 1] == quote))) 742 *resp++ = start[++i]; 743 else 744 *resp++ = start[i]; 745 } 746 747 *resp++ = '\0'; 748#if RESOLVE_ENV_PER_TOKEN 749 return (char *)ap_resolve_env(p,result); 750#else 751 return result; 752#endif 753} 754 755AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line) 756{ 757 return ap_getword_conf(p, (const char **) line); 758} 759 760AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) 761{ 762 const char *str = *line, *strend; 763 char *res; 764 char quote; 765 766 while (*str && apr_isspace(*str)) 767 ++str; 768 769 if (!*str) { 770 *line = str; 771 return ""; 772 } 773 774 if ((quote = *str) == '"' || quote == '\'') { 775 strend = str + 1; 776 while (*strend && *strend != quote) { 777 if (*strend == '\\' && strend[1] && 778 (strend[1] == quote || strend[1] == '\\')) { 779 strend += 2; 780 } 781 else { 782 ++strend; 783 } 784 } 785 res = substring_conf(p, str + 1, strend - str - 1, quote); 786 787 if (*strend == quote) 788 ++strend; 789 } 790 else { 791 strend = str; 792 while (*strend && !apr_isspace(*strend)) 793 ++strend; 794 795 res = substring_conf(p, str, strend - str, 0); 796 } 797 798 while (*strend && apr_isspace(*strend)) 799 ++strend; 800 *line = strend; 801 return res; 802} 803 804/* Check a string for any ${ENV} environment variable 805 * construct and replace each them by the value of 806 * that environment variable, if it exists. If the 807 * environment value does not exist, leave the ${ENV} 808 * construct alone; it means something else. 809 */ 810AP_DECLARE(const char *) ap_resolve_env(apr_pool_t *p, const char * word) 811{ 812# define SMALL_EXPANSION 5 813 struct sll { 814 struct sll *next; 815 const char *string; 816 apr_size_t len; 817 } *result, *current, sresult[SMALL_EXPANSION]; 818 char *res_buf, *cp; 819 const char *s, *e, *ep; 820 unsigned spc; 821 apr_size_t outlen; 822 823 s = ap_strchr_c(word, '$'); 824 if (!s) { 825 return word; 826 } 827 828 /* well, actually something to do */ 829 ep = word + strlen(word); 830 spc = 0; 831 result = current = &(sresult[spc++]); 832 current->next = NULL; 833 current->string = word; 834 current->len = s - word; 835 outlen = current->len; 836 837 do { 838 /* prepare next entry */ 839 if (current->len) { 840 current->next = (spc < SMALL_EXPANSION) 841 ? &(sresult[spc++]) 842 : (struct sll *)apr_palloc(p, 843 sizeof(*current->next)); 844 current = current->next; 845 current->next = NULL; 846 current->len = 0; 847 } 848 849 if (*s == '$') { 850 if (s[1] == '{' && (e = ap_strchr_c(s, '}'))) { 851 word = getenv(apr_pstrndup(p, s+2, e-s-2)); 852 if (word) { 853 current->string = word; 854 current->len = strlen(word); 855 outlen += current->len; 856 } 857 else { 858 current->string = s; 859 current->len = e - s + 1; 860 outlen += current->len; 861 } 862 s = e + 1; 863 } 864 else { 865 current->string = s++; 866 current->len = 1; 867 ++outlen; 868 } 869 } 870 else { 871 word = s; 872 s = ap_strchr_c(s, '$'); 873 current->string = word; 874 current->len = s ? s - word : ep - word; 875 outlen += current->len; 876 } 877 } while (s && *s); 878 879 /* assemble result */ 880 res_buf = cp = apr_palloc(p, outlen + 1); 881 do { 882 if (result->len) { 883 memcpy(cp, result->string, result->len); 884 cp += result->len; 885 } 886 result = result->next; 887 } while (result); 888 res_buf[outlen] = '\0'; 889 890 return res_buf; 891} 892 893AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) 894{ 895#ifdef DEBUG 896 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 897 "Done with config file %s", cfp->name); 898#endif 899 return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); 900} 901 902static apr_status_t cfg_close(void *param) 903{ 904 apr_file_t *cfp = (apr_file_t *) param; 905 return (apr_file_close(cfp)); 906} 907 908static int cfg_getch(void *param) 909{ 910 char ch; 911 apr_file_t *cfp = (apr_file_t *) param; 912 if (apr_file_getc(&ch, cfp) == APR_SUCCESS) 913 return ch; 914 return (int)EOF; 915} 916 917static void *cfg_getstr(void *buf, size_t bufsiz, void *param) 918{ 919 apr_file_t *cfp = (apr_file_t *) param; 920 apr_status_t rv; 921 rv = apr_file_gets(buf, bufsiz, cfp); 922 if (rv == APR_SUCCESS) { 923 return buf; 924 } 925 return NULL; 926} 927 928/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ 929AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, 930 apr_pool_t *p, const char *name) 931{ 932 ap_configfile_t *new_cfg; 933 apr_file_t *file = NULL; 934 apr_finfo_t finfo; 935 apr_status_t status; 936#ifdef DEBUG 937 char buf[120]; 938#endif 939 940 if (name == NULL) { 941 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 942 "Internal error: pcfg_openfile() called with NULL filename"); 943 return APR_EBADF; 944 } 945 946 status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, 947 APR_OS_DEFAULT, p); 948#ifdef DEBUG 949 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 950 "Opening config file %s (%s)", 951 name, (status != APR_SUCCESS) ? 952 apr_strerror(status, buf, sizeof(buf)) : "successful"); 953#endif 954 if (status != APR_SUCCESS) 955 return status; 956 957 status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); 958 if (status != APR_SUCCESS) 959 return status; 960 961 if (finfo.filetype != APR_REG && 962#if defined(WIN32) || defined(OS2) || defined(NETWARE) 963 strcasecmp(apr_filepath_name_get(name), "nul") != 0) { 964#else 965 strcmp(name, "/dev/null") != 0) { 966#endif /* WIN32 || OS2 */ 967 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, 968 "Access to file %s denied by server: not a regular file", 969 name); 970 apr_file_close(file); 971 return APR_EBADF; 972 } 973 974#ifdef WIN32 975 /* Some twisted character [no pun intended] at MS decided that a 976 * zero width joiner as the lead wide character would be ideal for 977 * describing Unicode text files. This was further convoluted to 978 * another MSism that the same character mapped into utf-8, EF BB BF 979 * would signify utf-8 text files. 980 * 981 * Since MS configuration files are all protecting utf-8 encoded 982 * Unicode path, file and resource names, we already have the correct 983 * WinNT encoding. But at least eat the stupid three bytes up front. 984 */ 985 { 986 unsigned char buf[4]; 987 apr_size_t len = 3; 988 status = apr_file_read(file, buf, &len); 989 if ((status != APR_SUCCESS) || (len < 3) 990 || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { 991 apr_off_t zero = 0; 992 apr_file_seek(file, APR_SET, &zero); 993 } 994 } 995#endif 996 997 new_cfg = apr_palloc(p, sizeof(*new_cfg)); 998 new_cfg->param = file; 999 new_cfg->name = apr_pstrdup(p, name); 1000 new_cfg->getch = (int (*)(void *)) cfg_getch; 1001 new_cfg->getstr = (void *(*)(void *, size_t, void *)) cfg_getstr; 1002 new_cfg->close = (int (*)(void *)) cfg_close; 1003 new_cfg->line_number = 0; 1004 *ret_cfg = new_cfg; 1005 return APR_SUCCESS; 1006} 1007 1008 1009/* Allocate a ap_configfile_t handle with user defined functions and params */ 1010AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom(apr_pool_t *p, 1011 const char *descr, 1012 void *param, 1013 int(*getch)(void *param), 1014 void *(*getstr) (void *buf, size_t bufsiz, void *param), 1015 int(*close_func)(void *param)) 1016{ 1017 ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg)); 1018#ifdef DEBUG 1019 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 1020 "Opening config handler %s", descr); 1021#endif 1022 new_cfg->param = param; 1023 new_cfg->name = descr; 1024 new_cfg->getch = getch; 1025 new_cfg->getstr = getstr; 1026 new_cfg->close = close_func; 1027 new_cfg->line_number = 0; 1028 return new_cfg; 1029} 1030 1031/* Read one character from a configfile_t */ 1032AP_DECLARE(int) ap_cfg_getc(ap_configfile_t *cfp) 1033{ 1034 register int ch = cfp->getch(cfp->param); 1035 if (ch == LF) 1036 ++cfp->line_number; 1037 return ch; 1038} 1039 1040/* Read one line from open ap_configfile_t, strip LF, increase line number */ 1041/* If custom handler does not define a getstr() function, read char by char */ 1042AP_DECLARE(int) ap_cfg_getline(char *buf, size_t bufsize, ap_configfile_t *cfp) 1043{ 1044 /* If a "get string" function is defined, use it */ 1045 if (cfp->getstr != NULL) { 1046 char *src, *dst; 1047 char *cp; 1048 char *cbuf = buf; 1049 size_t cbufsize = bufsize; 1050 1051 while (1) { 1052 ++cfp->line_number; 1053 if (cfp->getstr(cbuf, cbufsize, cfp->param) == NULL) 1054 return 1; 1055 1056 /* 1057 * check for line continuation, 1058 * i.e. match [^\\]\\[\r]\n only 1059 */ 1060 cp = cbuf; 1061 while (cp < cbuf+cbufsize && *cp != '\0') 1062 cp++; 1063 if (cp > cbuf && cp[-1] == LF) { 1064 cp--; 1065 if (cp > cbuf && cp[-1] == CR) 1066 cp--; 1067 if (cp > cbuf && cp[-1] == '\\') { 1068 cp--; 1069 if (!(cp > cbuf && cp[-1] == '\\')) { 1070 /* 1071 * line continuation requested - 1072 * then remove backslash and continue 1073 */ 1074 cbufsize -= (cp-cbuf); 1075 cbuf = cp; 1076 continue; 1077 } 1078 else { 1079 /* 1080 * no real continuation because escaped - 1081 * then just remove escape character 1082 */ 1083 for ( ; cp < cbuf+cbufsize && *cp != '\0'; cp++) 1084 cp[0] = cp[1]; 1085 } 1086 } 1087 } 1088 break; 1089 } 1090 1091 /* 1092 * Leading and trailing white space is eliminated completely 1093 */ 1094 src = buf; 1095 while (apr_isspace(*src)) 1096 ++src; 1097 /* blast trailing whitespace */ 1098 dst = &src[strlen(src)]; 1099 while (--dst >= src && apr_isspace(*dst)) 1100 *dst = '\0'; 1101 /* Zap leading whitespace by shifting */ 1102 if (src != buf) 1103 for (dst = buf; (*dst++ = *src++) != '\0'; ) 1104 ; 1105 1106#ifdef DEBUG_CFG_LINES 1107 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, "Read config: %s", buf); 1108#endif 1109 return 0; 1110 } else { 1111 /* No "get string" function defined; read character by character */ 1112 register int c; 1113 register size_t i = 0; 1114 1115 buf[0] = '\0'; 1116 /* skip leading whitespace */ 1117 do { 1118 c = cfp->getch(cfp->param); 1119 } while (c == '\t' || c == ' '); 1120 1121 if (c == EOF) 1122 return 1; 1123 1124 if(bufsize < 2) { 1125 /* too small, assume caller is crazy */ 1126 return 1; 1127 } 1128 1129 while (1) { 1130 if ((c == '\t') || (c == ' ')) { 1131 buf[i++] = ' '; 1132 while ((c == '\t') || (c == ' ')) 1133 c = cfp->getch(cfp->param); 1134 } 1135 if (c == CR) { 1136 /* silently ignore CR (_assume_ that a LF follows) */ 1137 c = cfp->getch(cfp->param); 1138 } 1139 if (c == LF) { 1140 /* increase line number and return on LF */ 1141 ++cfp->line_number; 1142 } 1143 if (c == EOF || c == 0x4 || c == LF || i >= (bufsize - 2)) { 1144 /* 1145 * check for line continuation 1146 */ 1147 if (i > 0 && buf[i-1] == '\\') { 1148 i--; 1149 if (!(i > 0 && buf[i-1] == '\\')) { 1150 /* line is continued */ 1151 c = cfp->getch(cfp->param); 1152 continue; 1153 } 1154 /* else nothing needs be done because 1155 * then the backslash is escaped and 1156 * we just strip to a single one 1157 */ 1158 } 1159 /* blast trailing whitespace */ 1160 while (i > 0 && apr_isspace(buf[i - 1])) 1161 --i; 1162 buf[i] = '\0'; 1163#ifdef DEBUG_CFG_LINES 1164 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, 1165 "Read config: %s", buf); 1166#endif 1167 return 0; 1168 } 1169 buf[i] = c; 1170 ++i; 1171 c = cfp->getch(cfp->param); 1172 } 1173 } 1174} 1175 1176/* Size an HTTP header field list item, as separated by a comma. 1177 * The return value is a pointer to the beginning of the non-empty list item 1178 * within the original string (or NULL if there is none) and the address 1179 * of field is shifted to the next non-comma, non-whitespace character. 1180 * len is the length of the item excluding any beginning whitespace. 1181 */ 1182AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len) 1183{ 1184 const unsigned char *ptr = (const unsigned char *)*field; 1185 const unsigned char *token; 1186 int in_qpair, in_qstr, in_com; 1187 1188 /* Find first non-comma, non-whitespace byte */ 1189 1190 while (*ptr == ',' || apr_isspace(*ptr)) 1191 ++ptr; 1192 1193 token = ptr; 1194 1195 /* Find the end of this item, skipping over dead bits */ 1196 1197 for (in_qpair = in_qstr = in_com = 0; 1198 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1199 ++ptr) { 1200 1201 if (in_qpair) { 1202 in_qpair = 0; 1203 } 1204 else { 1205 switch (*ptr) { 1206 case '\\': in_qpair = 1; /* quoted-pair */ 1207 break; 1208 case '"' : if (!in_com) /* quoted string delim */ 1209 in_qstr = !in_qstr; 1210 break; 1211 case '(' : if (!in_qstr) /* comment (may nest) */ 1212 ++in_com; 1213 break; 1214 case ')' : if (in_com) /* end comment */ 1215 --in_com; 1216 break; 1217 default : break; 1218 } 1219 } 1220 } 1221 1222 if ((*len = (ptr - token)) == 0) { 1223 *field = (const char *)ptr; 1224 return NULL; 1225 } 1226 1227 /* Advance field pointer to the next non-comma, non-white byte */ 1228 1229 while (*ptr == ',' || apr_isspace(*ptr)) 1230 ++ptr; 1231 1232 *field = (const char *)ptr; 1233 return (const char *)token; 1234} 1235 1236/* Retrieve an HTTP header field list item, as separated by a comma, 1237 * while stripping insignificant whitespace and lowercasing anything not in 1238 * a quoted string or comment. The return value is a new string containing 1239 * the converted list item (or NULL if none) and the address pointed to by 1240 * field is shifted to the next non-comma, non-whitespace. 1241 */ 1242AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field) 1243{ 1244 const char *tok_start; 1245 const unsigned char *ptr; 1246 unsigned char *pos; 1247 char *token; 1248 int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0; 1249 1250 /* Find the beginning and maximum length of the list item so that 1251 * we can allocate a buffer for the new string and reset the field. 1252 */ 1253 if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { 1254 return NULL; 1255 } 1256 token = apr_palloc(p, tok_len + 1); 1257 1258 /* Scan the token again, but this time copy only the good bytes. 1259 * We skip extra whitespace and any whitespace around a '=', '/', 1260 * or ';' and lowercase normal characters not within a comment, 1261 * quoted-string or quoted-pair. 1262 */ 1263 for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token; 1264 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1265 ++ptr) { 1266 1267 if (in_qpair) { 1268 in_qpair = 0; 1269 *pos++ = *ptr; 1270 } 1271 else { 1272 switch (*ptr) { 1273 case '\\': in_qpair = 1; 1274 if (addspace == 1) 1275 *pos++ = ' '; 1276 *pos++ = *ptr; 1277 addspace = 0; 1278 break; 1279 case '"' : if (!in_com) 1280 in_qstr = !in_qstr; 1281 if (addspace == 1) 1282 *pos++ = ' '; 1283 *pos++ = *ptr; 1284 addspace = 0; 1285 break; 1286 case '(' : if (!in_qstr) 1287 ++in_com; 1288 if (addspace == 1) 1289 *pos++ = ' '; 1290 *pos++ = *ptr; 1291 addspace = 0; 1292 break; 1293 case ')' : if (in_com) 1294 --in_com; 1295 *pos++ = *ptr; 1296 addspace = 0; 1297 break; 1298 case ' ' : 1299 case '\t': if (addspace) 1300 break; 1301 if (in_com || in_qstr) 1302 *pos++ = *ptr; 1303 else 1304 addspace = 1; 1305 break; 1306 case '=' : 1307 case '/' : 1308 case ';' : if (!(in_com || in_qstr)) 1309 addspace = -1; 1310 *pos++ = *ptr; 1311 break; 1312 default : if (addspace == 1) 1313 *pos++ = ' '; 1314 *pos++ = (in_com || in_qstr) ? *ptr 1315 : apr_tolower(*ptr); 1316 addspace = 0; 1317 break; 1318 } 1319 } 1320 } 1321 *pos = '\0'; 1322 1323 return token; 1324} 1325 1326/* Find an item in canonical form (lowercase, no extra spaces) within 1327 * an HTTP field value list. Returns 1 if found, 0 if not found. 1328 * This would be much more efficient if we stored header fields as 1329 * an array of list items as they are received instead of a plain string. 1330 */ 1331AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line, 1332 const char *tok) 1333{ 1334 const unsigned char *pos; 1335 const unsigned char *ptr = (const unsigned char *)line; 1336 int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0; 1337 1338 if (!line || !tok) 1339 return 0; 1340 1341 do { /* loop for each item in line's list */ 1342 1343 /* Find first non-comma, non-whitespace byte */ 1344 1345 while (*ptr == ',' || apr_isspace(*ptr)) 1346 ++ptr; 1347 1348 if (*ptr) 1349 good = 1; /* until proven otherwise for this item */ 1350 else 1351 break; /* no items left and nothing good found */ 1352 1353 /* We skip extra whitespace and any whitespace around a '=', '/', 1354 * or ';' and lowercase normal characters not within a comment, 1355 * quoted-string or quoted-pair. 1356 */ 1357 for (pos = (const unsigned char *)tok; 1358 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1359 ++ptr) { 1360 1361 if (in_qpair) { 1362 in_qpair = 0; 1363 if (good) 1364 good = (*pos++ == *ptr); 1365 } 1366 else { 1367 switch (*ptr) { 1368 case '\\': in_qpair = 1; 1369 if (addspace == 1) 1370 good = good && (*pos++ == ' '); 1371 good = good && (*pos++ == *ptr); 1372 addspace = 0; 1373 break; 1374 case '"' : if (!in_com) 1375 in_qstr = !in_qstr; 1376 if (addspace == 1) 1377 good = good && (*pos++ == ' '); 1378 good = good && (*pos++ == *ptr); 1379 addspace = 0; 1380 break; 1381 case '(' : if (!in_qstr) 1382 ++in_com; 1383 if (addspace == 1) 1384 good = good && (*pos++ == ' '); 1385 good = good && (*pos++ == *ptr); 1386 addspace = 0; 1387 break; 1388 case ')' : if (in_com) 1389 --in_com; 1390 good = good && (*pos++ == *ptr); 1391 addspace = 0; 1392 break; 1393 case ' ' : 1394 case '\t': if (addspace || !good) 1395 break; 1396 if (in_com || in_qstr) 1397 good = (*pos++ == *ptr); 1398 else 1399 addspace = 1; 1400 break; 1401 case '=' : 1402 case '/' : 1403 case ';' : if (!(in_com || in_qstr)) 1404 addspace = -1; 1405 good = good && (*pos++ == *ptr); 1406 break; 1407 default : if (!good) 1408 break; 1409 if (addspace == 1) 1410 good = (*pos++ == ' '); 1411 if (in_com || in_qstr) 1412 good = good && (*pos++ == *ptr); 1413 else 1414 good = good && (*pos++ == apr_tolower(*ptr)); 1415 addspace = 0; 1416 break; 1417 } 1418 } 1419 } 1420 if (good && *pos) 1421 good = 0; /* not good if only a prefix was matched */ 1422 1423 } while (*ptr && !good); 1424 1425 return good; 1426} 1427 1428 1429/* Retrieve a token, spacing over it and returning a pointer to 1430 * the first non-white byte afterwards. Note that these tokens 1431 * are delimited by semis and commas; and can also be delimited 1432 * by whitespace at the caller's option. 1433 */ 1434 1435AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, 1436 int accept_white) 1437{ 1438 const char *ptr = *accept_line; 1439 const char *tok_start; 1440 char *token; 1441 int tok_len; 1442 1443 /* Find first non-white byte */ 1444 1445 while (*ptr && apr_isspace(*ptr)) 1446 ++ptr; 1447 1448 tok_start = ptr; 1449 1450 /* find token end, skipping over quoted strings. 1451 * (comments are already gone). 1452 */ 1453 1454 while (*ptr && (accept_white || !apr_isspace(*ptr)) 1455 && *ptr != ';' && *ptr != ',') { 1456 if (*ptr++ == '"') 1457 while (*ptr) 1458 if (*ptr++ == '"') 1459 break; 1460 } 1461 1462 tok_len = ptr - tok_start; 1463 token = apr_pstrndup(p, tok_start, tok_len); 1464 1465 /* Advance accept_line pointer to the next non-white byte */ 1466 1467 while (*ptr && apr_isspace(*ptr)) 1468 ++ptr; 1469 1470 *accept_line = ptr; 1471 return token; 1472} 1473 1474 1475/* find http tokens, see the definition of token from RFC2068 */ 1476AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok) 1477{ 1478 const unsigned char *start_token; 1479 const unsigned char *s; 1480 1481 if (!line) 1482 return 0; 1483 1484 s = (const unsigned char *)line; 1485 for (;;) { 1486 /* find start of token, skip all stop characters, note NUL 1487 * isn't a token stop, so we don't need to test for it 1488 */ 1489 while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { 1490 ++s; 1491 } 1492 if (!*s) { 1493 return 0; 1494 } 1495 start_token = s; 1496 /* find end of the token */ 1497 while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { 1498 ++s; 1499 } 1500 if (!strncasecmp((const char *)start_token, (const char *)tok, 1501 s - start_token)) { 1502 return 1; 1503 } 1504 if (!*s) { 1505 return 0; 1506 } 1507 } 1508} 1509 1510 1511AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, 1512 const char *tok) 1513{ 1514 int llen, tlen, lidx; 1515 1516 if (!line) 1517 return 0; 1518 1519 llen = strlen(line); 1520 tlen = strlen(tok); 1521 lidx = llen - tlen; 1522 1523 if (lidx < 0 || 1524 (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ','))) 1525 return 0; 1526 1527 return (strncasecmp(&line[lidx], tok, tlen) == 0); 1528} 1529 1530AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str) 1531{ 1532 char *cmd; 1533 unsigned char *d; 1534 const unsigned char *s; 1535 1536 cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */ 1537 d = (unsigned char *)cmd; 1538 s = (const unsigned char *)str; 1539 for (; *s; ++s) { 1540 1541#if defined(OS2) || defined(WIN32) 1542 /* 1543 * Newlines to Win32/OS2 CreateProcess() are ill advised. 1544 * Convert them to spaces since they are effectively white 1545 * space to most applications 1546 */ 1547 if (*s == '\r' || *s == '\n') { 1548 *d++ = ' '; 1549 continue; 1550 } 1551#endif 1552 1553 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) { 1554 *d++ = '\\'; 1555 } 1556 *d++ = *s; 1557 } 1558 *d = '\0'; 1559 1560 return cmd; 1561} 1562 1563static char x2c(const char *what) 1564{ 1565 register char digit; 1566 1567#if !APR_CHARSET_EBCDIC 1568 digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 1569 : (what[0] - '0')); 1570 digit *= 16; 1571 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 1572 : (what[1] - '0')); 1573#else /*APR_CHARSET_EBCDIC*/ 1574 char xstr[5]; 1575 xstr[0]='0'; 1576 xstr[1]='x'; 1577 xstr[2]=what[0]; 1578 xstr[3]=what[1]; 1579 xstr[4]='\0'; 1580 digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 1581 0xFF & strtol(xstr, NULL, 16)); 1582#endif /*APR_CHARSET_EBCDIC*/ 1583 return (digit); 1584} 1585 1586/* 1587 * Unescapes a URL. 1588 * Returns 0 on success, non-zero on error 1589 * Failure is due to 1590 * bad % escape returns HTTP_BAD_REQUEST 1591 * 1592 * decoding %00 -> \0 (the null character) 1593 * decoding %2f -> / (a special character) 1594 * returns HTTP_NOT_FOUND 1595 */ 1596AP_DECLARE(int) ap_unescape_url(char *url) 1597{ 1598 register int badesc, badpath; 1599 char *x, *y; 1600 1601 badesc = 0; 1602 badpath = 0; 1603 /* Initial scan for first '%'. Don't bother writing values before 1604 * seeing a '%' */ 1605 y = strchr(url, '%'); 1606 if (y == NULL) { 1607 return OK; 1608 } 1609 for (x = y; *y; ++x, ++y) { 1610 if (*y != '%') 1611 *x = *y; 1612 else { 1613 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) { 1614 badesc = 1; 1615 *x = '%'; 1616 } 1617 else { 1618 *x = x2c(y + 1); 1619 y += 2; 1620 if (IS_SLASH(*x) || *x == '\0') 1621 badpath = 1; 1622 } 1623 } 1624 } 1625 *x = '\0'; 1626 if (badesc) 1627 return HTTP_BAD_REQUEST; 1628 else if (badpath) 1629 return HTTP_NOT_FOUND; 1630 else 1631 return OK; 1632} 1633 1634AP_DECLARE(int) ap_unescape_url_keep2f_ex(char *url, int decode_2f) 1635{ 1636 register int badesc, badpath; 1637 char *x, *y; 1638 1639 badesc = 0; 1640 badpath = 0; 1641 /* Initial scan for first '%'. Don't bother writing values before 1642 * seeing a '%' */ 1643 y = strchr(url, '%'); 1644 if (y == NULL) { 1645 return OK; 1646 } 1647 for (x = y; *y; ++x, ++y) { 1648 if (*y != '%') { 1649 *x = *y; 1650 } 1651 else { 1652 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) { 1653 badesc = 1; 1654 *x = '%'; 1655 } 1656 else { 1657 char decoded; 1658 decoded = x2c(y + 1); 1659 if (decoded == '\0') { 1660 badpath = 1; 1661 } 1662 else if (IS_SLASH(decoded) && !decode_2f) { 1663 /* do not decode, just let it go by as-is */ 1664 *x = *y; 1665 } 1666 else { 1667 *x = decoded; 1668 y += 2; 1669 } 1670 } 1671 } 1672 } 1673 *x = '\0'; 1674 if (badesc) { 1675 return HTTP_BAD_REQUEST; 1676 } 1677 else if (badpath) { 1678 return HTTP_NOT_FOUND; 1679 } 1680 else { 1681 return OK; 1682 } 1683} 1684 1685AP_DECLARE(int) ap_unescape_url_keep2f(char *url) 1686{ 1687 return ap_unescape_url_keep2f_ex(url, 1); 1688} 1689 1690AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname, 1691 apr_port_t port, const request_rec *r) 1692{ 1693 if (ap_is_default_port(port, r)) { 1694 return apr_pstrdup(p, hostname); 1695 } 1696 else { 1697 return apr_psprintf(p, "%s:%u", hostname, port); 1698 } 1699} 1700 1701/* c2x takes an unsigned, and expects the caller has guaranteed that 1702 * 0 <= what < 256... which usually means that you have to cast to 1703 * unsigned char first, because (unsigned)(char)(x) first goes through 1704 * signed extension to an int before the unsigned cast. 1705 * 1706 * The reason for this assumption is to assist gcc code generation -- 1707 * the unsigned char -> unsigned extension is already done earlier in 1708 * both uses of this code, so there's no need to waste time doing it 1709 * again. 1710 */ 1711static const char c2x_table[] = "0123456789abcdef"; 1712 1713static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, 1714 unsigned char *where) 1715{ 1716#if APR_CHARSET_EBCDIC 1717 what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what); 1718#endif /*APR_CHARSET_EBCDIC*/ 1719 *where++ = prefix; 1720 *where++ = c2x_table[what >> 4]; 1721 *where++ = c2x_table[what & 0xf]; 1722 return where; 1723} 1724 1725/* 1726 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This 1727 * routine is (should be) OS independent. 1728 * 1729 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all 1730 * cases if a ':' occurs before the first '/' in the URL, the URL should be 1731 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means 1732 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For 1733 * efficiency reasons, we don't use escape_path_segment(), which is provided for 1734 * reference. Again, RFC 1808 is where this stuff is defined. 1735 * 1736 * If partial is set, os_escape_path() assumes that the path will be appended to 1737 * something with a '/' in it (and thus does not prefix "./"). 1738 */ 1739 1740AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment) 1741{ 1742 char *copy = apr_palloc(p, 3 * strlen(segment) + 1); 1743 const unsigned char *s = (const unsigned char *)segment; 1744 unsigned char *d = (unsigned char *)copy; 1745 unsigned c; 1746 1747 while ((c = *s)) { 1748 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) { 1749 d = c2x(c, '%', d); 1750 } 1751 else { 1752 *d++ = c; 1753 } 1754 ++s; 1755 } 1756 *d = '\0'; 1757 return copy; 1758} 1759 1760AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial) 1761{ 1762 char *copy = apr_palloc(p, 3 * strlen(path) + 3); 1763 const unsigned char *s = (const unsigned char *)path; 1764 unsigned char *d = (unsigned char *)copy; 1765 unsigned c; 1766 1767 if (!partial) { 1768 const char *colon = ap_strchr_c(path, ':'); 1769 const char *slash = ap_strchr_c(path, '/'); 1770 1771 if (colon && (!slash || colon < slash)) { 1772 *d++ = '.'; 1773 *d++ = '/'; 1774 } 1775 } 1776 while ((c = *s)) { 1777 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { 1778 d = c2x(c, '%', d); 1779 } 1780 else { 1781 *d++ = c; 1782 } 1783 ++s; 1784 } 1785 *d = '\0'; 1786 return copy; 1787} 1788 1789/* ap_escape_uri is now a macro for os_escape_path */ 1790 1791AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc) 1792{ 1793 int i, j; 1794 char *x; 1795 1796 /* first, count the number of extra characters */ 1797 for (i = 0, j = 0; s[i] != '\0'; i++) 1798 if (s[i] == '<' || s[i] == '>') 1799 j += 3; 1800 else if (s[i] == '&') 1801 j += 4; 1802 else if (s[i] == '"') 1803 j += 5; 1804 else if (toasc && !apr_isascii(s[i])) 1805 j += 5; 1806 1807 if (j == 0) 1808 return apr_pstrmemdup(p, s, i); 1809 1810 x = apr_palloc(p, i + j + 1); 1811 for (i = 0, j = 0; s[i] != '\0'; i++, j++) 1812 if (s[i] == '<') { 1813 memcpy(&x[j], "<", 4); 1814 j += 3; 1815 } 1816 else if (s[i] == '>') { 1817 memcpy(&x[j], ">", 4); 1818 j += 3; 1819 } 1820 else if (s[i] == '&') { 1821 memcpy(&x[j], "&", 5); 1822 j += 4; 1823 } 1824 else if (s[i] == '"') { 1825 memcpy(&x[j], """, 6); 1826 j += 5; 1827 } 1828 else if (toasc && !apr_isascii(s[i])) { 1829 char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]); 1830 memcpy(&x[j], esc, 6); 1831 j += 5; 1832 } 1833 else 1834 x[j] = s[i]; 1835 1836 x[j] = '\0'; 1837 return x; 1838} 1839AP_DECLARE(char *) ap_escape_html(apr_pool_t *p, const char *s) 1840{ 1841 return ap_escape_html2(p, s, 0); 1842} 1843AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str) 1844{ 1845 char *ret; 1846 unsigned char *d; 1847 const unsigned char *s; 1848 apr_size_t length, escapes = 0; 1849 1850 if (!str) { 1851 return NULL; 1852 } 1853 1854 /* Compute how many characters need to be escaped */ 1855 s = (const unsigned char *)str; 1856 for (; *s; ++s) { 1857 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1858 escapes++; 1859 } 1860 } 1861 1862 /* Compute the length of the input string, including NULL */ 1863 length = s - (const unsigned char *)str + 1; 1864 1865 /* Fast path: nothing to escape */ 1866 if (escapes == 0) { 1867 return apr_pmemdup(p, str, length); 1868 } 1869 1870 /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */ 1871 ret = apr_palloc(p, length + 3 * escapes); 1872 d = (unsigned char *)ret; 1873 s = (const unsigned char *)str; 1874 for (; *s; ++s) { 1875 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1876 *d++ = '\\'; 1877 switch(*s) { 1878 case '\b': 1879 *d++ = 'b'; 1880 break; 1881 case '\n': 1882 *d++ = 'n'; 1883 break; 1884 case '\r': 1885 *d++ = 'r'; 1886 break; 1887 case '\t': 1888 *d++ = 't'; 1889 break; 1890 case '\v': 1891 *d++ = 'v'; 1892 break; 1893 case '\\': 1894 case '"': 1895 *d++ = *s; 1896 break; 1897 default: 1898 c2x(*s, 'x', d); 1899 d += 3; 1900 } 1901 } 1902 else { 1903 *d++ = *s; 1904 } 1905 } 1906 *d = '\0'; 1907 1908 return ret; 1909} 1910 1911AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source, 1912 apr_size_t buflen) 1913{ 1914 unsigned char *d, *ep; 1915 const unsigned char *s; 1916 1917 if (!source || !buflen) { /* be safe */ 1918 return 0; 1919 } 1920 1921 d = (unsigned char *)dest; 1922 s = (const unsigned char *)source; 1923 ep = d + buflen - 1; 1924 1925 for (; d < ep && *s; ++s) { 1926 1927 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1928 *d++ = '\\'; 1929 if (d >= ep) { 1930 --d; 1931 break; 1932 } 1933 1934 switch(*s) { 1935 case '\b': 1936 *d++ = 'b'; 1937 break; 1938 case '\n': 1939 *d++ = 'n'; 1940 break; 1941 case '\r': 1942 *d++ = 'r'; 1943 break; 1944 case '\t': 1945 *d++ = 't'; 1946 break; 1947 case '\v': 1948 *d++ = 'v'; 1949 break; 1950 case '\\': 1951 *d++ = *s; 1952 break; 1953 case '"': /* no need for this in error log */ 1954 d[-1] = *s; 1955 break; 1956 default: 1957 if (d >= ep - 2) { 1958 ep = --d; /* break the for loop as well */ 1959 break; 1960 } 1961 c2x(*s, 'x', d); 1962 d += 3; 1963 } 1964 } 1965 else { 1966 *d++ = *s; 1967 } 1968 } 1969 *d = '\0'; 1970 1971 return (d - (unsigned char *)dest); 1972} 1973 1974AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) 1975{ 1976 apr_finfo_t finfo; 1977 1978 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) 1979 return 0; /* in error condition, just return no */ 1980 1981 return (finfo.filetype == APR_DIR); 1982} 1983 1984AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path) 1985{ 1986 apr_finfo_t finfo; 1987 1988 if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS) 1989 return 0; /* in error condition, just return no */ 1990 1991 return (finfo.filetype == APR_DIR); 1992} 1993 1994AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, 1995 const char *src2) 1996{ 1997 apr_size_t len1, len2; 1998 char *path; 1999 2000 len1 = strlen(src1); 2001 len2 = strlen(src2); 2002 /* allocate +3 for '/' delimiter, trailing NULL and overallocate 2003 * one extra byte to allow the caller to add a trailing '/' 2004 */ 2005 path = (char *)apr_palloc(a, len1 + len2 + 3); 2006 if (len1 == 0) { 2007 *path = '/'; 2008 memcpy(path + 1, src2, len2 + 1); 2009 } 2010 else { 2011 char *next; 2012 memcpy(path, src1, len1); 2013 next = path + len1; 2014 if (next[-1] != '/') { 2015 *next++ = '/'; 2016 } 2017 memcpy(next, src2, len2 + 1); 2018 } 2019 return path; 2020} 2021 2022/* 2023 * Check for an absoluteURI syntax (see section 3.2 in RFC2068). 2024 */ 2025AP_DECLARE(int) ap_is_url(const char *u) 2026{ 2027 register int x; 2028 2029 for (x = 0; u[x] != ':'; x++) { 2030 if ((!u[x]) || 2031 ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) && 2032 (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) { 2033 return 0; 2034 } 2035 } 2036 2037 return (x ? 1 : 0); /* If the first character is ':', it's broken, too */ 2038} 2039 2040AP_DECLARE(int) ap_ind(const char *s, char c) 2041{ 2042 const char *p = ap_strchr_c(s, c); 2043 2044 if (p == NULL) 2045 return -1; 2046 return p - s; 2047} 2048 2049AP_DECLARE(int) ap_rind(const char *s, char c) 2050{ 2051 const char *p = ap_strrchr_c(s, c); 2052 2053 if (p == NULL) 2054 return -1; 2055 return p - s; 2056} 2057 2058AP_DECLARE(void) ap_str_tolower(char *str) 2059{ 2060 while (*str) { 2061 *str = apr_tolower(*str); 2062 ++str; 2063 } 2064} 2065 2066/* 2067 * We must return a FQDN 2068 */ 2069char *ap_get_local_host(apr_pool_t *a) 2070{ 2071#ifndef MAXHOSTNAMELEN 2072#define MAXHOSTNAMELEN 256 2073#endif 2074 char str[MAXHOSTNAMELEN + 1]; 2075 char *server_hostname = NULL; 2076 apr_sockaddr_t *sockaddr; 2077 char *hostname; 2078 2079 if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) { 2080 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, 2081 "%s: apr_gethostname() failed to determine ServerName", 2082 ap_server_argv0); 2083 } else { 2084 str[sizeof(str) - 1] = '\0'; 2085 if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) { 2086 if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) && 2087 (ap_strchr_c(hostname, '.')) ) { 2088 server_hostname = apr_pstrdup(a, hostname); 2089 return server_hostname; 2090 } else if (ap_strchr_c(str, '.')) { 2091 server_hostname = apr_pstrdup(a, str); 2092 } else { 2093 apr_sockaddr_ip_get(&hostname, sockaddr); 2094 server_hostname = apr_pstrdup(a, hostname); 2095 } 2096 } else { 2097 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, 2098 "%s: apr_sockaddr_info_get() failed for %s", 2099 ap_server_argv0, str); 2100 } 2101 } 2102 2103 if (!server_hostname) 2104 server_hostname = apr_pstrdup(a, "127.0.0.1"); 2105 2106 ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, 2107 "%s: Could not reliably determine the server's fully qualified " 2108 "domain name, using %s for ServerName", 2109 ap_server_argv0, server_hostname); 2110 2111 return server_hostname; 2112} 2113 2114/* simple 'pool' alloc()ing glue to apr_base64.c 2115 */ 2116AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded) 2117{ 2118 char *decoded; 2119 int l; 2120 2121 decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded)); 2122 l = apr_base64_decode(decoded, bufcoded); 2123 decoded[l] = '\0'; /* make binary sequence into string */ 2124 2125 return decoded; 2126} 2127 2128AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string) 2129{ 2130 char *encoded; 2131 int l = strlen(string); 2132 2133 encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l)); 2134 l = apr_base64_encode(encoded, string, l); 2135 encoded[l] = '\0'; /* make binary sequence into string */ 2136 2137 return encoded; 2138} 2139 2140/* we want to downcase the type/subtype for comparison purposes 2141 * but nothing else because ;parameter=foo values are case sensitive. 2142 * XXX: in truth we want to downcase parameter names... but really, 2143 * apache has never handled parameters and such correctly. You 2144 * also need to compress spaces and such to be able to compare 2145 * properly. -djg 2146 */ 2147AP_DECLARE(void) ap_content_type_tolower(char *str) 2148{ 2149 char *semi; 2150 2151 semi = strchr(str, ';'); 2152 if (semi) { 2153 *semi = '\0'; 2154 } 2155 2156 ap_str_tolower(str); 2157 2158 if (semi) { 2159 *semi = ';'; 2160 } 2161} 2162 2163/* 2164 * Given a string, replace any bare " with \" . 2165 */ 2166AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring) 2167{ 2168 int newlen = 0; 2169 const char *inchr = instring; 2170 char *outchr, *outstring; 2171 2172 /* 2173 * Look through the input string, jogging the length of the output 2174 * string up by an extra byte each time we find an unescaped ". 2175 */ 2176 while (*inchr != '\0') { 2177 newlen++; 2178 if (*inchr == '"') { 2179 newlen++; 2180 } 2181 /* 2182 * If we find a slosh, and it's not the last byte in the string, 2183 * it's escaping something - advance past both bytes. 2184 */ 2185 if ((*inchr == '\\') && (inchr[1] != '\0')) { 2186 inchr++; 2187 newlen++; 2188 } 2189 inchr++; 2190 } 2191 outstring = apr_palloc(p, newlen + 1); 2192 inchr = instring; 2193 outchr = outstring; 2194 /* 2195 * Now copy the input string to the output string, inserting a slosh 2196 * in front of every " that doesn't already have one. 2197 */ 2198 while (*inchr != '\0') { 2199 if ((*inchr == '\\') && (inchr[1] != '\0')) { 2200 *outchr++ = *inchr++; 2201 *outchr++ = *inchr++; 2202 } 2203 if (*inchr == '"') { 2204 *outchr++ = '\\'; 2205 } 2206 if (*inchr != '\0') { 2207 *outchr++ = *inchr++; 2208 } 2209 } 2210 *outchr = '\0'; 2211 return outstring; 2212} 2213 2214/* 2215 * Given a string, append the PID deliminated by delim. 2216 * Usually used to create a pid-appended filepath name 2217 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not 2218 * a macro, to avoid unistd.h dependency 2219 */ 2220AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string, 2221 const char *delim) 2222{ 2223 return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string, 2224 delim, getpid()); 2225 2226} 2227 2228/** 2229 * Parse a given timeout parameter string into an apr_interval_time_t value. 2230 * The unit of the time interval is given as postfix string to the numeric 2231 * string. Currently the following units are understood: 2232 * 2233 * ms : milliseconds 2234 * s : seconds 2235 * mi[n] : minutes 2236 * h : hours 2237 * 2238 * If no unit is contained in the given timeout parameter the default_time_unit 2239 * will be used instead. 2240 * @param timeout_parameter The string containing the timeout parameter. 2241 * @param timeout The timeout value to be returned. 2242 * @param default_time_unit The default time unit to use if none is specified 2243 * in timeout_parameter. 2244 * @return Status value indicating whether the parsing was successful or not. 2245 */ 2246AP_DECLARE(apr_status_t) ap_timeout_parameter_parse( 2247 const char *timeout_parameter, 2248 apr_interval_time_t *timeout, 2249 const char *default_time_unit) 2250{ 2251 char *endp; 2252 const char *time_str; 2253 apr_int64_t tout; 2254 2255 tout = apr_strtoi64(timeout_parameter, &endp, 10); 2256 if (errno) { 2257 return errno; 2258 } 2259 if (!endp || !*endp) { 2260 time_str = default_time_unit; 2261 } 2262 else { 2263 time_str = endp; 2264 } 2265 2266 switch (*time_str) { 2267 /* Time is in seconds */ 2268 case 's': 2269 *timeout = (apr_interval_time_t) apr_time_from_sec(tout); 2270 break; 2271 case 'h': 2272 /* Time is in hours */ 2273 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600); 2274 break; 2275 case 'm': 2276 switch (*(++time_str)) { 2277 /* Time is in miliseconds */ 2278 case 's': 2279 *timeout = (apr_interval_time_t) tout * 1000; 2280 break; 2281 /* Time is in minutes */ 2282 case 'i': 2283 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60); 2284 break; 2285 default: 2286 return APR_EGENERAL; 2287 } 2288 break; 2289 default: 2290 return APR_EGENERAL; 2291 } 2292 return APR_SUCCESS; 2293} 2294 2295AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p, 2296 const char *cmd, 2297 const char * const * argv) 2298{ 2299 char buf[MAX_STRING_LEN]; 2300 apr_procattr_t *procattr; 2301 apr_proc_t *proc; 2302 apr_file_t *fp; 2303 apr_size_t nbytes = 1; 2304 char c; 2305 int k; 2306 2307 if (apr_procattr_create(&procattr, p) != APR_SUCCESS) 2308 return NULL; 2309 if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK, 2310 APR_FULL_BLOCK) != APR_SUCCESS) 2311 return NULL; 2312 if (apr_procattr_dir_set(procattr, 2313 ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS) 2314 return NULL; 2315 if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS) 2316 return NULL; 2317 proc = apr_pcalloc(p, sizeof(apr_proc_t)); 2318 if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS) 2319 return NULL; 2320 fp = proc->out; 2321 2322 if (fp == NULL) 2323 return NULL; 2324 /* XXX: we are reading 1 byte at a time here */ 2325 for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS 2326 && nbytes == 1 && (k < MAX_STRING_LEN-1) ; ) { 2327 if (c == '\n' || c == '\r') 2328 break; 2329 buf[k++] = c; 2330 } 2331 buf[k] = '\0'; 2332 apr_file_close(fp); 2333 2334 return apr_pstrndup(p, buf, k); 2335} 2336