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_PROCESS_H 42#include <process.h> /* for getpid() on Win32 */ 43#endif 44#if APR_HAVE_NETDB_H 45#include <netdb.h> /* for gethostbyname() */ 46#endif 47 48#include "ap_config.h" 49#include "apr_base64.h" 50#include "httpd.h" 51#include "http_main.h" 52#include "http_log.h" 53#include "http_protocol.h" 54#include "http_config.h" 55#include "http_core.h" 56#include "util_ebcdic.h" 57#include "util_varbuf.h" 58 59#ifdef HAVE_PWD_H 60#include <pwd.h> 61#endif 62#ifdef HAVE_GRP_H 63#include <grp.h> 64#endif 65#ifdef HAVE_SYS_LOADAVG_H 66#include <sys/loadavg.h> 67#endif 68 69#include "ap_mpm.h" 70 71/* A bunch of functions in util.c scan strings looking for certain characters. 72 * To make that more efficient we encode a lookup table. The test_char_table 73 * is generated automatically by gen_test_char.c. 74 */ 75#include "test_char.h" 76 77/* we assume the folks using this ensure 0 <= c < 256... which means 78 * you need a cast to (unsigned char) first, you can't just plug a 79 * char in here and get it to work, because if char is signed then it 80 * will first be sign extended. 81 */ 82#define TEST_CHAR(c, f) (test_char_table[(unsigned)(c)] & (f)) 83 84/* Win32/NetWare/OS2 need to check for both forward and back slashes 85 * in ap_getparents() and ap_escape_url. 86 */ 87#ifdef CASE_BLIND_FILESYSTEM 88#define IS_SLASH(s) ((s == '/') || (s == '\\')) 89#define SLASHES "/\\" 90#else 91#define IS_SLASH(s) (s == '/') 92#define SLASHES "/" 93#endif 94 95/* we know core's module_index is 0 */ 96#undef APLOG_MODULE_INDEX 97#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX 98 99 100/* 101 * Examine a field value (such as a media-/content-type) string and return 102 * it sans any parameters; e.g., strip off any ';charset=foo' and the like. 103 */ 104AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, const char *intype) 105{ 106 const char *semi; 107 108 if (intype == NULL) return NULL; 109 110 semi = ap_strchr_c(intype, ';'); 111 if (semi == NULL) { 112 return apr_pstrdup(p, intype); 113 } 114 else { 115 while ((semi > intype) && apr_isspace(semi[-1])) { 116 semi--; 117 } 118 return apr_pstrmemdup(p, intype, semi - intype); 119 } 120} 121 122AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt, 123 int gmt) 124{ 125 apr_size_t retcode; 126 char ts[MAX_STRING_LEN]; 127 char tf[MAX_STRING_LEN]; 128 apr_time_exp_t xt; 129 130 if (gmt) { 131 const char *f; 132 char *strp; 133 134 apr_time_exp_gmt(&xt, t); 135 /* Convert %Z to "GMT" and %z to "+0000"; 136 * on hosts that do not have a time zone string in struct tm, 137 * strftime must assume its argument is local time. 138 */ 139 for(strp = tf, f = fmt; strp < tf + sizeof(tf) - 6 && (*strp = *f) 140 ; f++, strp++) { 141 if (*f != '%') continue; 142 switch (f[1]) { 143 case '%': 144 *++strp = *++f; 145 break; 146 case 'Z': 147 *strp++ = 'G'; 148 *strp++ = 'M'; 149 *strp = 'T'; 150 f++; 151 break; 152 case 'z': /* common extension */ 153 *strp++ = '+'; 154 *strp++ = '0'; 155 *strp++ = '0'; 156 *strp++ = '0'; 157 *strp = '0'; 158 f++; 159 break; 160 } 161 } 162 *strp = '\0'; 163 fmt = tf; 164 } 165 else { 166 apr_time_exp_lt(&xt, t); 167 } 168 169 /* check return code? */ 170 apr_strftime(ts, &retcode, MAX_STRING_LEN, fmt, &xt); 171 ts[MAX_STRING_LEN - 1] = '\0'; 172 return apr_pstrdup(p, ts); 173} 174 175/* Roy owes Rob beer. */ 176/* Rob owes Roy dinner. */ 177 178/* These legacy comments would make a lot more sense if Roy hadn't 179 * replaced the old later_than() routine with util_date.c. 180 * 181 * Well, okay, they still wouldn't make any sense. 182 */ 183 184/* Match = 0, NoMatch = 1, Abort = -1 185 * Based loosely on sections of wildmat.c by Rich Salz 186 * Hmmm... shouldn't this really go component by component? 187 */ 188AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected) 189{ 190 int x, y; 191 192 for (x = 0, y = 0; expected[y]; ++y, ++x) { 193 if ((!str[x]) && (expected[y] != '*')) 194 return -1; 195 if (expected[y] == '*') { 196 while (expected[++y] == '*'); 197 if (!expected[y]) 198 return 0; 199 while (str[x]) { 200 int ret; 201 if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1) 202 return ret; 203 } 204 return -1; 205 } 206 else if ((expected[y] != '?') && (str[x] != expected[y])) 207 return 1; 208 } 209 return (str[x] != '\0'); 210} 211 212AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected) 213{ 214 int x, y; 215 216 for (x = 0, y = 0; expected[y]; ++y, ++x) { 217 if (!str[x] && expected[y] != '*') 218 return -1; 219 if (expected[y] == '*') { 220 while (expected[++y] == '*'); 221 if (!expected[y]) 222 return 0; 223 while (str[x]) { 224 int ret; 225 if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1) 226 return ret; 227 } 228 return -1; 229 } 230 else if (expected[y] != '?' 231 && apr_tolower(str[x]) != apr_tolower(expected[y])) 232 return 1; 233 } 234 return (str[x] != '\0'); 235} 236 237/* We actually compare the canonical root to this root, (but we don't 238 * waste time checking the case), since every use of this function in 239 * httpd-2.1 tests if the path is 'proper', meaning we've already passed 240 * it through apr_filepath_merge, or we haven't. 241 */ 242AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir) 243{ 244 const char *newpath; 245 const char *ourdir = dir; 246 if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS 247 || strncmp(newpath, ourdir, strlen(newpath)) != 0) { 248 return 0; 249 } 250 return 1; 251} 252 253AP_DECLARE(int) ap_is_matchexp(const char *str) 254{ 255 register int x; 256 257 for (x = 0; str[x]; x++) 258 if ((str[x] == '*') || (str[x] == '?')) 259 return 1; 260 return 0; 261} 262 263/* 264 * Here's a pool-based interface to the POSIX-esque ap_regcomp(). 265 * Note that we return ap_regex_t instead of being passed one. 266 * The reason is that if you use an already-used ap_regex_t structure, 267 * the memory that you've already allocated gets forgotten, and 268 * regfree() doesn't clear it. So we don't allow it. 269 */ 270 271static apr_status_t regex_cleanup(void *preg) 272{ 273 ap_regfree((ap_regex_t *) preg); 274 return APR_SUCCESS; 275} 276 277AP_DECLARE(ap_regex_t *) ap_pregcomp(apr_pool_t *p, const char *pattern, 278 int cflags) 279{ 280 ap_regex_t *preg = apr_palloc(p, sizeof *preg); 281 int err = ap_regcomp(preg, pattern, cflags); 282 if (err) { 283 if (err == AP_REG_ESPACE) 284 ap_abort_on_oom(); 285 return NULL; 286 } 287 288 apr_pool_cleanup_register(p, (void *) preg, regex_cleanup, 289 apr_pool_cleanup_null); 290 291 return preg; 292} 293 294AP_DECLARE(void) ap_pregfree(apr_pool_t *p, ap_regex_t *reg) 295{ 296 ap_regfree(reg); 297 apr_pool_cleanup_kill(p, (void *) reg, regex_cleanup); 298} 299 300/* 301 * Similar to standard strstr() but we ignore case in this version. 302 * Based on the strstr() implementation further below. 303 */ 304AP_DECLARE(char *) ap_strcasestr(const char *s1, const char *s2) 305{ 306 char *p1, *p2; 307 if (*s2 == '\0') { 308 /* an empty s2 */ 309 return((char *)s1); 310 } 311 while(1) { 312 for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++); 313 if (*s1 == '\0') { 314 return(NULL); 315 } 316 /* found first character of s2, see if the rest matches */ 317 p1 = (char *)s1; 318 p2 = (char *)s2; 319 for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) { 320 if (*p1 == '\0') { 321 /* both strings ended together */ 322 return((char *)s1); 323 } 324 } 325 if (*p2 == '\0') { 326 /* second string ended, a match */ 327 break; 328 } 329 /* didn't find a match here, try starting at next character in s1 */ 330 s1++; 331 } 332 return((char *)s1); 333} 334 335/* 336 * Returns an offsetted pointer in bigstring immediately after 337 * prefix. Returns bigstring if bigstring doesn't start with 338 * prefix or if prefix is longer than bigstring while still matching. 339 * NOTE: pointer returned is relative to bigstring, so we 340 * can use standard pointer comparisons in the calling function 341 * (eg: test if ap_stripprefix(a,b) == a) 342 */ 343AP_DECLARE(const char *) ap_stripprefix(const char *bigstring, 344 const char *prefix) 345{ 346 const char *p1; 347 348 if (*prefix == '\0') 349 return bigstring; 350 351 p1 = bigstring; 352 while (*p1 && *prefix) { 353 if (*p1++ != *prefix++) 354 return bigstring; 355 } 356 if (*prefix == '\0') 357 return p1; 358 359 /* hit the end of bigstring! */ 360 return bigstring; 361} 362 363/* This function substitutes for $0-$9, filling in regular expression 364 * submatches. Pass it the same nmatch and pmatch arguments that you 365 * passed ap_regexec(). pmatch should not be greater than the maximum number 366 * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t. 367 * 368 * nmatch must be <=AP_MAX_REG_MATCH (10). 369 * 370 * input should be the string with the $-expressions, source should be the 371 * string that was matched against. 372 * 373 * It returns the substituted string, or NULL if a vbuf is used. 374 * On errors, returns the orig string. 375 * 376 * Parts of this code are based on Henry Spencer's regsub(), from his 377 * AT&T V8 regexp package. 378 */ 379 380static apr_status_t regsub_core(apr_pool_t *p, char **result, 381 struct ap_varbuf *vb, const char *input, 382 const char *source, apr_size_t nmatch, 383 ap_regmatch_t pmatch[], apr_size_t maxlen) 384{ 385 const char *src = input; 386 char *dst; 387 char c; 388 apr_size_t no; 389 apr_size_t len = 0; 390 391 AP_DEBUG_ASSERT((result && p && !vb) || (vb && !p && !result)); 392 if (!source || nmatch>AP_MAX_REG_MATCH) 393 return APR_EINVAL; 394 if (!nmatch) { 395 len = strlen(src); 396 if (maxlen > 0 && len >= maxlen) 397 return APR_ENOMEM; 398 if (!vb) { 399 *result = apr_pstrmemdup(p, src, len); 400 return APR_SUCCESS; 401 } 402 else { 403 ap_varbuf_strmemcat(vb, src, len); 404 return APR_SUCCESS; 405 } 406 } 407 408 /* First pass, find the size */ 409 while ((c = *src++) != '\0') { 410 if (c == '$' && apr_isdigit(*src)) 411 no = *src++ - '0'; 412 else 413 no = AP_MAX_REG_MATCH; 414 415 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */ 416 if (c == '\\' && *src) 417 src++; 418 len++; 419 } 420 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { 421 if (APR_SIZE_MAX - len <= pmatch[no].rm_eo - pmatch[no].rm_so) 422 return APR_ENOMEM; 423 len += pmatch[no].rm_eo - pmatch[no].rm_so; 424 } 425 426 } 427 428 if (len >= maxlen && maxlen > 0) 429 return APR_ENOMEM; 430 431 if (!vb) { 432 *result = dst = apr_palloc(p, len + 1); 433 } 434 else { 435 if (vb->strlen == AP_VARBUF_UNKNOWN) 436 vb->strlen = strlen(vb->buf); 437 ap_varbuf_grow(vb, vb->strlen + len); 438 dst = vb->buf + vb->strlen; 439 vb->strlen += len; 440 } 441 442 /* Now actually fill in the string */ 443 444 src = input; 445 446 while ((c = *src++) != '\0') { 447 if (c == '$' && apr_isdigit(*src)) 448 no = *src++ - '0'; 449 else 450 no = AP_MAX_REG_MATCH; 451 452 if (no >= AP_MAX_REG_MATCH) { /* Ordinary character. */ 453 if (c == '\\' && *src) 454 c = *src++; 455 *dst++ = c; 456 } 457 else if (no < nmatch && pmatch[no].rm_so < pmatch[no].rm_eo) { 458 len = pmatch[no].rm_eo - pmatch[no].rm_so; 459 memcpy(dst, source + pmatch[no].rm_so, len); 460 dst += len; 461 } 462 463 } 464 *dst = '\0'; 465 466 return APR_SUCCESS; 467} 468 469#ifndef AP_PREGSUB_MAXLEN 470#define AP_PREGSUB_MAXLEN (HUGE_STRING_LEN * 8) 471#endif 472AP_DECLARE(char *) ap_pregsub(apr_pool_t *p, const char *input, 473 const char *source, apr_size_t nmatch, 474 ap_regmatch_t pmatch[]) 475{ 476 char *result; 477 apr_status_t rc = regsub_core(p, &result, NULL, input, source, nmatch, 478 pmatch, AP_PREGSUB_MAXLEN); 479 if (rc != APR_SUCCESS) 480 result = NULL; 481 return result; 482} 483 484AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result, 485 const char *input, const char *source, 486 apr_size_t nmatch, ap_regmatch_t pmatch[], 487 apr_size_t maxlen) 488{ 489 apr_status_t rc = regsub_core(p, result, NULL, input, source, nmatch, 490 pmatch, maxlen); 491 if (rc != APR_SUCCESS) 492 *result = NULL; 493 return rc; 494} 495 496/* 497 * Parse .. so we don't compromise security 498 */ 499AP_DECLARE(void) ap_getparents(char *name) 500{ 501 char *next; 502 int l, w, first_dot; 503 504 /* Four paseses, as per RFC 1808 */ 505 /* a) remove ./ path segments */ 506 for (next = name; *next && (*next != '.'); next++) { 507 } 508 509 l = w = first_dot = next - name; 510 while (name[l] != '\0') { 511 if (name[l] == '.' && IS_SLASH(name[l + 1]) 512 && (l == 0 || IS_SLASH(name[l - 1]))) 513 l += 2; 514 else 515 name[w++] = name[l++]; 516 } 517 518 /* b) remove trailing . path, segment */ 519 if (w == 1 && name[0] == '.') 520 w--; 521 else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2])) 522 w--; 523 name[w] = '\0'; 524 525 /* c) remove all xx/../ segments. (including leading ../ and /../) */ 526 l = first_dot; 527 528 while (name[l] != '\0') { 529 if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2]) 530 && (l == 0 || IS_SLASH(name[l - 1]))) { 531 register int m = l + 3, n; 532 533 l = l - 2; 534 if (l >= 0) { 535 while (l >= 0 && !IS_SLASH(name[l])) 536 l--; 537 l++; 538 } 539 else 540 l = 0; 541 n = l; 542 while ((name[n] = name[m])) 543 (++n, ++m); 544 } 545 else 546 ++l; 547 } 548 549 /* d) remove trailing xx/.. segment. */ 550 if (l == 2 && name[0] == '.' && name[1] == '.') 551 name[0] = '\0'; 552 else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' 553 && IS_SLASH(name[l - 3])) { 554 l = l - 4; 555 if (l >= 0) { 556 while (l >= 0 && !IS_SLASH(name[l])) 557 l--; 558 l++; 559 } 560 else 561 l = 0; 562 name[l] = '\0'; 563 } 564} 565 566AP_DECLARE(void) ap_no2slash(char *name) 567{ 568 char *d, *s; 569 570 s = d = name; 571 572#ifdef HAVE_UNC_PATHS 573 /* Check for UNC names. Leave leading two slashes. */ 574 if (s[0] == '/' && s[1] == '/') 575 *d++ = *s++; 576#endif 577 578 while (*s) { 579 if ((*d++ = *s) == '/') { 580 do { 581 ++s; 582 } while (*s == '/'); 583 } 584 else { 585 ++s; 586 } 587 } 588 *d = '\0'; 589} 590 591 592/* 593 * copy at most n leading directories of s into d 594 * d should be at least as large as s plus 1 extra byte 595 * assumes n > 0 596 * the return value is the ever useful pointer to the trailing \0 of d 597 * 598 * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments, 599 * so that if n == 0, "/" is returned in d with n == 1 600 * and s == "e:/test.html", "e:/" is returned in d 601 * *** See also directory_walk in modules/http/http_request.c 602 603 * examples: 604 * /a/b, 0 ==> / (true for all platforms) 605 * /a/b, 1 ==> / 606 * /a/b, 2 ==> /a/ 607 * /a/b, 3 ==> /a/b/ 608 * /a/b, 4 ==> /a/b/ 609 * 610 * c:/a/b 0 ==> / 611 * c:/a/b 1 ==> c:/ 612 * c:/a/b 2 ==> c:/a/ 613 * c:/a/b 3 ==> c:/a/b 614 * c:/a/b 4 ==> c:/a/b 615 */ 616AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, const char *s, int n) 617{ 618 if (n < 1) { 619 *d = '/'; 620 *++d = '\0'; 621 return (d); 622 } 623 624 for (;;) { 625 if (*s == '\0' || (*s == '/' && (--n) == 0)) { 626 *d = '/'; 627 break; 628 } 629 *d++ = *s++; 630 } 631 *++d = 0; 632 return (d); 633} 634 635 636/* 637 * return the parent directory name including trailing / of the file s 638 */ 639AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s) 640{ 641 const char *last_slash = ap_strrchr_c(s, '/'); 642 char *d; 643 int l; 644 645 if (last_slash == NULL) { 646 return apr_pstrdup(p, ""); 647 } 648 l = (last_slash - s) + 1; 649 d = apr_pstrmemdup(p, s, l); 650 651 return (d); 652} 653 654 655AP_DECLARE(int) ap_count_dirs(const char *path) 656{ 657 register int x, n; 658 659 for (x = 0, n = 0; path[x]; x++) 660 if (path[x] == '/') 661 n++; 662 return n; 663} 664 665AP_DECLARE(char *) ap_getword_nc(apr_pool_t *atrans, char **line, char stop) 666{ 667 return ap_getword(atrans, (const char **) line, stop); 668} 669 670AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop) 671{ 672 const char *pos = *line; 673 int len; 674 char *res; 675 676 while ((*pos != stop) && *pos) { 677 ++pos; 678 } 679 680 len = pos - *line; 681 res = apr_pstrmemdup(atrans, *line, len); 682 683 if (stop) { 684 while (*pos == stop) { 685 ++pos; 686 } 687 } 688 *line = pos; 689 690 return res; 691} 692 693AP_DECLARE(char *) ap_getword_white_nc(apr_pool_t *atrans, char **line) 694{ 695 return ap_getword_white(atrans, (const char **) line); 696} 697 698AP_DECLARE(char *) ap_getword_white(apr_pool_t *atrans, const char **line) 699{ 700 const char *pos = *line; 701 int len; 702 char *res; 703 704 while (!apr_isspace(*pos) && *pos) { 705 ++pos; 706 } 707 708 len = pos - *line; 709 res = apr_pstrmemdup(atrans, *line, len); 710 711 while (apr_isspace(*pos)) { 712 ++pos; 713 } 714 715 *line = pos; 716 717 return res; 718} 719 720AP_DECLARE(char *) ap_getword_nulls_nc(apr_pool_t *atrans, char **line, 721 char stop) 722{ 723 return ap_getword_nulls(atrans, (const char **) line, stop); 724} 725 726AP_DECLARE(char *) ap_getword_nulls(apr_pool_t *atrans, const char **line, 727 char stop) 728{ 729 const char *pos = ap_strchr_c(*line, stop); 730 char *res; 731 732 if (!pos) { 733 apr_size_t len = strlen(*line); 734 res = apr_pstrmemdup(atrans, *line, len); 735 *line += len; 736 return res; 737 } 738 739 res = apr_pstrmemdup(atrans, *line, pos - *line); 740 741 ++pos; 742 743 *line = pos; 744 745 return res; 746} 747 748/* Get a word, (new) config-file style --- quoted strings and backslashes 749 * all honored 750 */ 751 752static char *substring_conf(apr_pool_t *p, const char *start, int len, 753 char quote) 754{ 755 char *result = apr_palloc(p, len + 1); 756 char *resp = result; 757 int i; 758 759 for (i = 0; i < len; ++i) { 760 if (start[i] == '\\' && (start[i + 1] == '\\' 761 || (quote && start[i + 1] == quote))) 762 *resp++ = start[++i]; 763 else 764 *resp++ = start[i]; 765 } 766 767 *resp++ = '\0'; 768#if RESOLVE_ENV_PER_TOKEN 769 return (char *)ap_resolve_env(p,result); 770#else 771 return result; 772#endif 773} 774 775AP_DECLARE(char *) ap_getword_conf_nc(apr_pool_t *p, char **line) 776{ 777 return ap_getword_conf(p, (const char **) line); 778} 779 780AP_DECLARE(char *) ap_getword_conf(apr_pool_t *p, const char **line) 781{ 782 const char *str = *line, *strend; 783 char *res; 784 char quote; 785 786 while (apr_isspace(*str)) 787 ++str; 788 789 if (!*str) { 790 *line = str; 791 return ""; 792 } 793 794 if ((quote = *str) == '"' || quote == '\'') { 795 strend = str + 1; 796 while (*strend && *strend != quote) { 797 if (*strend == '\\' && strend[1] && 798 (strend[1] == quote || strend[1] == '\\')) { 799 strend += 2; 800 } 801 else { 802 ++strend; 803 } 804 } 805 res = substring_conf(p, str + 1, strend - str - 1, quote); 806 807 if (*strend == quote) 808 ++strend; 809 } 810 else { 811 strend = str; 812 while (*strend && !apr_isspace(*strend)) 813 ++strend; 814 815 res = substring_conf(p, str, strend - str, 0); 816 } 817 818 while (apr_isspace(*strend)) 819 ++strend; 820 *line = strend; 821 return res; 822} 823 824AP_DECLARE(int) ap_cfg_closefile(ap_configfile_t *cfp) 825{ 826#ifdef DEBUG 827 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00551) 828 "Done with config file %s", cfp->name); 829#endif 830 return (cfp->close == NULL) ? 0 : cfp->close(cfp->param); 831} 832 833/* we can't use apr_file_* directly because of linking issues on Windows */ 834static apr_status_t cfg_close(void *param) 835{ 836 return apr_file_close(param); 837} 838 839static apr_status_t cfg_getch(char *ch, void *param) 840{ 841 return apr_file_getc(ch, param); 842} 843 844static apr_status_t cfg_getstr(void *buf, apr_size_t bufsiz, void *param) 845{ 846 return apr_file_gets(buf, bufsiz, param); 847} 848 849/* Open a ap_configfile_t as FILE, return open ap_configfile_t struct pointer */ 850AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg, 851 apr_pool_t *p, const char *name) 852{ 853 ap_configfile_t *new_cfg; 854 apr_file_t *file = NULL; 855 apr_finfo_t finfo; 856 apr_status_t status; 857#ifdef DEBUG 858 char buf[120]; 859#endif 860 861 if (name == NULL) { 862 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00552) 863 "Internal error: pcfg_openfile() called with NULL filename"); 864 return APR_EBADF; 865 } 866 867 status = apr_file_open(&file, name, APR_READ | APR_BUFFERED, 868 APR_OS_DEFAULT, p); 869#ifdef DEBUG 870 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL, APLOGNO(00553) 871 "Opening config file %s (%s)", 872 name, (status != APR_SUCCESS) ? 873 apr_strerror(status, buf, sizeof(buf)) : "successful"); 874#endif 875 if (status != APR_SUCCESS) 876 return status; 877 878 status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); 879 if (status != APR_SUCCESS) 880 return status; 881 882 if (finfo.filetype != APR_REG && 883#if defined(WIN32) || defined(OS2) || defined(NETWARE) 884 strcasecmp(apr_filepath_name_get(name), "nul") != 0) { 885#else 886 strcmp(name, "/dev/null") != 0) { 887#endif /* WIN32 || OS2 */ 888 ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554) 889 "Access to file %s denied by server: not a regular file", 890 name); 891 apr_file_close(file); 892 return APR_EBADF; 893 } 894 895#ifdef WIN32 896 /* Some twisted character [no pun intended] at MS decided that a 897 * zero width joiner as the lead wide character would be ideal for 898 * describing Unicode text files. This was further convoluted to 899 * another MSism that the same character mapped into utf-8, EF BB BF 900 * would signify utf-8 text files. 901 * 902 * Since MS configuration files are all protecting utf-8 encoded 903 * Unicode path, file and resource names, we already have the correct 904 * WinNT encoding. But at least eat the stupid three bytes up front. 905 */ 906 { 907 unsigned char buf[4]; 908 apr_size_t len = 3; 909 status = apr_file_read(file, buf, &len); 910 if ((status != APR_SUCCESS) || (len < 3) 911 || memcmp(buf, "\xEF\xBB\xBF", 3) != 0) { 912 apr_off_t zero = 0; 913 apr_file_seek(file, APR_SET, &zero); 914 } 915 } 916#endif 917 918 new_cfg = apr_palloc(p, sizeof(*new_cfg)); 919 new_cfg->param = file; 920 new_cfg->name = apr_pstrdup(p, name); 921 new_cfg->getch = cfg_getch; 922 new_cfg->getstr = cfg_getstr; 923 new_cfg->close = cfg_close; 924 new_cfg->line_number = 0; 925 *ret_cfg = new_cfg; 926 return APR_SUCCESS; 927} 928 929 930/* Allocate a ap_configfile_t handle with user defined functions and params */ 931AP_DECLARE(ap_configfile_t *) ap_pcfg_open_custom( 932 apr_pool_t *p, const char *descr, void *param, 933 apr_status_t (*getc_func) (char *ch, void *param), 934 apr_status_t (*gets_func) (void *buf, apr_size_t bufsize, void *param), 935 apr_status_t (*close_func) (void *param)) 936{ 937 ap_configfile_t *new_cfg = apr_palloc(p, sizeof(*new_cfg)); 938 new_cfg->param = param; 939 new_cfg->name = descr; 940 new_cfg->getch = getc_func; 941 new_cfg->getstr = gets_func; 942 new_cfg->close = close_func; 943 new_cfg->line_number = 0; 944 return new_cfg; 945} 946 947/* Read one character from a configfile_t */ 948AP_DECLARE(apr_status_t) ap_cfg_getc(char *ch, ap_configfile_t *cfp) 949{ 950 apr_status_t rc = cfp->getch(ch, cfp->param); 951 if (rc == APR_SUCCESS && *ch == LF) 952 ++cfp->line_number; 953 return rc; 954} 955 956AP_DECLARE(const char *) ap_pcfg_strerror(apr_pool_t *p, ap_configfile_t *cfp, 957 apr_status_t rc) 958{ 959 if (rc == APR_SUCCESS) 960 return NULL; 961 962 if (rc == APR_ENOSPC) 963 return apr_psprintf(p, "Error reading %s at line %d: Line too long", 964 cfp->name, cfp->line_number); 965 966 return apr_psprintf(p, "Error reading %s at line %d: %pm", 967 cfp->name, cfp->line_number, &rc); 968} 969 970/* Read one line from open ap_configfile_t, strip LF, increase line number */ 971/* If custom handler does not define a getstr() function, read char by char */ 972static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize, 973 ap_configfile_t *cfp) 974{ 975 apr_status_t rc; 976 /* If a "get string" function is defined, use it */ 977 if (cfp->getstr != NULL) { 978 char *cp; 979 char *cbuf = buf; 980 apr_size_t cbufsize = bufsize; 981 982 while (1) { 983 ++cfp->line_number; 984 rc = cfp->getstr(cbuf, cbufsize, cfp->param); 985 if (rc == APR_EOF) { 986 if (cbuf != buf) { 987 *cbuf = '\0'; 988 break; 989 } 990 else { 991 return APR_EOF; 992 } 993 } 994 if (rc != APR_SUCCESS) { 995 return rc; 996 } 997 998 /* 999 * check for line continuation, 1000 * i.e. match [^\\]\\[\r]\n only 1001 */ 1002 cp = cbuf; 1003 cp += strlen(cp); 1004 if (cp > cbuf && cp[-1] == LF) { 1005 cp--; 1006 if (cp > cbuf && cp[-1] == CR) 1007 cp--; 1008 if (cp > cbuf && cp[-1] == '\\') { 1009 cp--; 1010 /* 1011 * line continuation requested - 1012 * then remove backslash and continue 1013 */ 1014 cbufsize -= (cp-cbuf); 1015 cbuf = cp; 1016 continue; 1017 } 1018 } 1019 else if (cp - buf >= bufsize - 1) { 1020 return APR_ENOSPC; 1021 } 1022 break; 1023 } 1024 } else { 1025 /* No "get string" function defined; read character by character */ 1026 apr_size_t i = 0; 1027 1028 if (bufsize < 2) { 1029 /* too small, assume caller is crazy */ 1030 return APR_EINVAL; 1031 } 1032 buf[0] = '\0'; 1033 1034 while (1) { 1035 char c; 1036 rc = cfp->getch(&c, cfp->param); 1037 if (rc == APR_EOF) { 1038 if (i > 0) 1039 break; 1040 else 1041 return APR_EOF; 1042 } 1043 if (rc != APR_SUCCESS) 1044 return rc; 1045 if (c == LF) { 1046 ++cfp->line_number; 1047 /* check for line continuation */ 1048 if (i > 0 && buf[i-1] == '\\') { 1049 i--; 1050 continue; 1051 } 1052 else { 1053 break; 1054 } 1055 } 1056 else if (i >= bufsize - 2) { 1057 return APR_ENOSPC; 1058 } 1059 buf[i] = c; 1060 ++i; 1061 } 1062 buf[i] = '\0'; 1063 } 1064 return APR_SUCCESS; 1065} 1066 1067static int cfg_trim_line(char *buf) 1068{ 1069 char *start, *end; 1070 /* 1071 * Leading and trailing white space is eliminated completely 1072 */ 1073 start = buf; 1074 while (apr_isspace(*start)) 1075 ++start; 1076 /* blast trailing whitespace */ 1077 end = &start[strlen(start)]; 1078 while (--end >= start && apr_isspace(*end)) 1079 *end = '\0'; 1080 /* Zap leading whitespace by shifting */ 1081 if (start != buf) 1082 memmove(buf, start, end - start + 2); 1083#ifdef DEBUG_CFG_LINES 1084 ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); 1085#endif 1086 return end - start + 1; 1087} 1088 1089/* Read one line from open ap_configfile_t, strip LF, increase line number */ 1090/* If custom handler does not define a getstr() function, read char by char */ 1091AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize, 1092 ap_configfile_t *cfp) 1093{ 1094 apr_status_t rc = ap_cfg_getline_core(buf, bufsize, cfp); 1095 if (rc == APR_SUCCESS) 1096 cfg_trim_line(buf); 1097 return rc; 1098} 1099 1100AP_DECLARE(apr_status_t) ap_varbuf_cfg_getline(struct ap_varbuf *vb, 1101 ap_configfile_t *cfp, 1102 apr_size_t max_len) 1103{ 1104 apr_status_t rc; 1105 apr_size_t new_len; 1106 vb->strlen = 0; 1107 *vb->buf = '\0'; 1108 1109 if (vb->strlen == AP_VARBUF_UNKNOWN) 1110 vb->strlen = strlen(vb->buf); 1111 if (vb->avail - vb->strlen < 3) { 1112 new_len = vb->avail * 2; 1113 if (new_len > max_len) 1114 new_len = max_len; 1115 else if (new_len < 3) 1116 new_len = 3; 1117 ap_varbuf_grow(vb, new_len); 1118 } 1119 1120 for (;;) { 1121 rc = ap_cfg_getline_core(vb->buf + vb->strlen, vb->avail - vb->strlen, cfp); 1122 if (rc == APR_ENOSPC || rc == APR_SUCCESS) 1123 vb->strlen += strlen(vb->buf + vb->strlen); 1124 if (rc != APR_ENOSPC) 1125 break; 1126 if (vb->avail >= max_len) 1127 return APR_ENOSPC; 1128 new_len = vb->avail * 2; 1129 if (new_len > max_len) 1130 new_len = max_len; 1131 ap_varbuf_grow(vb, new_len); 1132 --cfp->line_number; 1133 } 1134 if (vb->strlen > max_len) 1135 return APR_ENOSPC; 1136 if (rc == APR_SUCCESS) 1137 vb->strlen = cfg_trim_line(vb->buf); 1138 return rc; 1139} 1140 1141/* Size an HTTP header field list item, as separated by a comma. 1142 * The return value is a pointer to the beginning of the non-empty list item 1143 * within the original string (or NULL if there is none) and the address 1144 * of field is shifted to the next non-comma, non-whitespace character. 1145 * len is the length of the item excluding any beginning whitespace. 1146 */ 1147AP_DECLARE(const char *) ap_size_list_item(const char **field, int *len) 1148{ 1149 const unsigned char *ptr = (const unsigned char *)*field; 1150 const unsigned char *token; 1151 int in_qpair, in_qstr, in_com; 1152 1153 /* Find first non-comma, non-whitespace byte */ 1154 1155 while (*ptr == ',' || apr_isspace(*ptr)) 1156 ++ptr; 1157 1158 token = ptr; 1159 1160 /* Find the end of this item, skipping over dead bits */ 1161 1162 for (in_qpair = in_qstr = in_com = 0; 1163 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1164 ++ptr) { 1165 1166 if (in_qpair) { 1167 in_qpair = 0; 1168 } 1169 else { 1170 switch (*ptr) { 1171 case '\\': in_qpair = 1; /* quoted-pair */ 1172 break; 1173 case '"' : if (!in_com) /* quoted string delim */ 1174 in_qstr = !in_qstr; 1175 break; 1176 case '(' : if (!in_qstr) /* comment (may nest) */ 1177 ++in_com; 1178 break; 1179 case ')' : if (in_com) /* end comment */ 1180 --in_com; 1181 break; 1182 default : break; 1183 } 1184 } 1185 } 1186 1187 if ((*len = (ptr - token)) == 0) { 1188 *field = (const char *)ptr; 1189 return NULL; 1190 } 1191 1192 /* Advance field pointer to the next non-comma, non-white byte */ 1193 1194 while (*ptr == ',' || apr_isspace(*ptr)) 1195 ++ptr; 1196 1197 *field = (const char *)ptr; 1198 return (const char *)token; 1199} 1200 1201/* Retrieve an HTTP header field list item, as separated by a comma, 1202 * while stripping insignificant whitespace and lowercasing anything not in 1203 * a quoted string or comment. The return value is a new string containing 1204 * the converted list item (or NULL if none) and the address pointed to by 1205 * field is shifted to the next non-comma, non-whitespace. 1206 */ 1207AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, const char **field) 1208{ 1209 const char *tok_start; 1210 const unsigned char *ptr; 1211 unsigned char *pos; 1212 char *token; 1213 int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0; 1214 1215 /* Find the beginning and maximum length of the list item so that 1216 * we can allocate a buffer for the new string and reset the field. 1217 */ 1218 if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { 1219 return NULL; 1220 } 1221 token = apr_palloc(p, tok_len + 1); 1222 1223 /* Scan the token again, but this time copy only the good bytes. 1224 * We skip extra whitespace and any whitespace around a '=', '/', 1225 * or ';' and lowercase normal characters not within a comment, 1226 * quoted-string or quoted-pair. 1227 */ 1228 for (ptr = (const unsigned char *)tok_start, pos = (unsigned char *)token; 1229 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1230 ++ptr) { 1231 1232 if (in_qpair) { 1233 in_qpair = 0; 1234 *pos++ = *ptr; 1235 } 1236 else { 1237 switch (*ptr) { 1238 case '\\': in_qpair = 1; 1239 if (addspace == 1) 1240 *pos++ = ' '; 1241 *pos++ = *ptr; 1242 addspace = 0; 1243 break; 1244 case '"' : if (!in_com) 1245 in_qstr = !in_qstr; 1246 if (addspace == 1) 1247 *pos++ = ' '; 1248 *pos++ = *ptr; 1249 addspace = 0; 1250 break; 1251 case '(' : if (!in_qstr) 1252 ++in_com; 1253 if (addspace == 1) 1254 *pos++ = ' '; 1255 *pos++ = *ptr; 1256 addspace = 0; 1257 break; 1258 case ')' : if (in_com) 1259 --in_com; 1260 *pos++ = *ptr; 1261 addspace = 0; 1262 break; 1263 case ' ' : 1264 case '\t': if (addspace) 1265 break; 1266 if (in_com || in_qstr) 1267 *pos++ = *ptr; 1268 else 1269 addspace = 1; 1270 break; 1271 case '=' : 1272 case '/' : 1273 case ';' : if (!(in_com || in_qstr)) 1274 addspace = -1; 1275 *pos++ = *ptr; 1276 break; 1277 default : if (addspace == 1) 1278 *pos++ = ' '; 1279 *pos++ = (in_com || in_qstr) ? *ptr 1280 : apr_tolower(*ptr); 1281 addspace = 0; 1282 break; 1283 } 1284 } 1285 } 1286 *pos = '\0'; 1287 1288 return token; 1289} 1290 1291typedef enum ap_etag_e { 1292 AP_ETAG_NONE, 1293 AP_ETAG_WEAK, 1294 AP_ETAG_STRONG 1295} ap_etag_e; 1296 1297/* Find an item in canonical form (lowercase, no extra spaces) within 1298 * an HTTP field value list. Returns 1 if found, 0 if not found. 1299 * This would be much more efficient if we stored header fields as 1300 * an array of list items as they are received instead of a plain string. 1301 */ 1302static int find_list_item(apr_pool_t *p, const char *line, 1303 const char *tok, ap_etag_e type) 1304{ 1305 const unsigned char *pos; 1306 const unsigned char *ptr = (const unsigned char *)line; 1307 int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0; 1308 1309 if (!line || !tok) { 1310 return 0; 1311 } 1312 if (type == AP_ETAG_STRONG && *tok != '\"') { 1313 return 0; 1314 } 1315 if (type == AP_ETAG_WEAK) { 1316 if (*tok == 'W' && (*(tok+1)) == '/' && (*(tok+2)) == '\"') { 1317 tok += 2; 1318 } 1319 else if (*tok != '\"') { 1320 return 0; 1321 } 1322 } 1323 1324 do { /* loop for each item in line's list */ 1325 1326 /* Find first non-comma, non-whitespace byte */ 1327 while (*ptr == ',' || apr_isspace(*ptr)) { 1328 ++ptr; 1329 } 1330 1331 /* Account for strong or weak Etags, depending on our search */ 1332 if (type == AP_ETAG_STRONG && *ptr != '\"') { 1333 break; 1334 } 1335 if (type == AP_ETAG_WEAK) { 1336 if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') { 1337 ptr += 2; 1338 } 1339 else if (*ptr != '\"') { 1340 break; 1341 } 1342 } 1343 1344 if (*ptr) 1345 good = 1; /* until proven otherwise for this item */ 1346 else 1347 break; /* no items left and nothing good found */ 1348 1349 /* We skip extra whitespace and any whitespace around a '=', '/', 1350 * or ';' and lowercase normal characters not within a comment, 1351 * quoted-string or quoted-pair. 1352 */ 1353 for (pos = (const unsigned char *)tok; 1354 *ptr && (in_qpair || in_qstr || in_com || *ptr != ','); 1355 ++ptr) { 1356 1357 if (in_qpair) { 1358 in_qpair = 0; 1359 if (good) 1360 good = (*pos++ == *ptr); 1361 } 1362 else { 1363 switch (*ptr) { 1364 case '\\': in_qpair = 1; 1365 if (addspace == 1) 1366 good = good && (*pos++ == ' '); 1367 good = good && (*pos++ == *ptr); 1368 addspace = 0; 1369 break; 1370 case '"' : if (!in_com) 1371 in_qstr = !in_qstr; 1372 if (addspace == 1) 1373 good = good && (*pos++ == ' '); 1374 good = good && (*pos++ == *ptr); 1375 addspace = 0; 1376 break; 1377 case '(' : if (!in_qstr) 1378 ++in_com; 1379 if (addspace == 1) 1380 good = good && (*pos++ == ' '); 1381 good = good && (*pos++ == *ptr); 1382 addspace = 0; 1383 break; 1384 case ')' : if (in_com) 1385 --in_com; 1386 good = good && (*pos++ == *ptr); 1387 addspace = 0; 1388 break; 1389 case ' ' : 1390 case '\t': if (addspace || !good) 1391 break; 1392 if (in_com || in_qstr) 1393 good = (*pos++ == *ptr); 1394 else 1395 addspace = 1; 1396 break; 1397 case '=' : 1398 case '/' : 1399 case ';' : if (!(in_com || in_qstr)) 1400 addspace = -1; 1401 good = good && (*pos++ == *ptr); 1402 break; 1403 default : if (!good) 1404 break; 1405 if (addspace == 1) 1406 good = (*pos++ == ' '); 1407 if (in_com || in_qstr) 1408 good = good && (*pos++ == *ptr); 1409 else 1410 good = good 1411 && (apr_tolower(*pos++) == apr_tolower(*ptr)); 1412 addspace = 0; 1413 break; 1414 } 1415 } 1416 } 1417 if (good && *pos) 1418 good = 0; /* not good if only a prefix was matched */ 1419 1420 } while (*ptr && !good); 1421 1422 return good; 1423} 1424 1425/* Find an item in canonical form (lowercase, no extra spaces) within 1426 * an HTTP field value list. Returns 1 if found, 0 if not found. 1427 * This would be much more efficient if we stored header fields as 1428 * an array of list items as they are received instead of a plain string. 1429 */ 1430AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, const char *line, 1431 const char *tok) 1432{ 1433 return find_list_item(p, line, tok, AP_ETAG_NONE); 1434} 1435 1436/* Find a strong Etag in canonical form (lowercase, no extra spaces) within 1437 * an HTTP field value list. Returns 1 if found, 0 if not found. 1438 */ 1439AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line, 1440 const char *tok) 1441{ 1442 return find_list_item(p, line, tok, AP_ETAG_STRONG); 1443} 1444 1445/* Find a weak ETag in canonical form (lowercase, no extra spaces) within 1446 * an HTTP field value list. Returns 1 if found, 0 if not found. 1447 */ 1448AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, 1449 const char *tok) 1450{ 1451 return find_list_item(p, line, tok, AP_ETAG_WEAK); 1452} 1453 1454/* Retrieve a token, spacing over it and returning a pointer to 1455 * the first non-white byte afterwards. Note that these tokens 1456 * are delimited by semis and commas; and can also be delimited 1457 * by whitespace at the caller's option. 1458 */ 1459 1460AP_DECLARE(char *) ap_get_token(apr_pool_t *p, const char **accept_line, 1461 int accept_white) 1462{ 1463 const char *ptr = *accept_line; 1464 const char *tok_start; 1465 char *token; 1466 1467 /* Find first non-white byte */ 1468 1469 while (apr_isspace(*ptr)) 1470 ++ptr; 1471 1472 tok_start = ptr; 1473 1474 /* find token end, skipping over quoted strings. 1475 * (comments are already gone). 1476 */ 1477 1478 while (*ptr && (accept_white || !apr_isspace(*ptr)) 1479 && *ptr != ';' && *ptr != ',') { 1480 if (*ptr++ == '"') 1481 while (*ptr) 1482 if (*ptr++ == '"') 1483 break; 1484 } 1485 1486 token = apr_pstrmemdup(p, tok_start, ptr - tok_start); 1487 1488 /* Advance accept_line pointer to the next non-white byte */ 1489 1490 while (apr_isspace(*ptr)) 1491 ++ptr; 1492 1493 *accept_line = ptr; 1494 return token; 1495} 1496 1497 1498/* find http tokens, see the definition of token from RFC2068 */ 1499AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok) 1500{ 1501 const unsigned char *start_token; 1502 const unsigned char *s; 1503 1504 if (!line) 1505 return 0; 1506 1507 s = (const unsigned char *)line; 1508 for (;;) { 1509 /* find start of token, skip all stop characters, note NUL 1510 * isn't a token stop, so we don't need to test for it 1511 */ 1512 while (TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { 1513 ++s; 1514 } 1515 if (!*s) { 1516 return 0; 1517 } 1518 start_token = s; 1519 /* find end of the token */ 1520 while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) { 1521 ++s; 1522 } 1523 if (!strncasecmp((const char *)start_token, (const char *)tok, 1524 s - start_token)) { 1525 return 1; 1526 } 1527 if (!*s) { 1528 return 0; 1529 } 1530 } 1531} 1532 1533 1534AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line, 1535 const char *tok) 1536{ 1537 int llen, tlen, lidx; 1538 1539 if (!line) 1540 return 0; 1541 1542 llen = strlen(line); 1543 tlen = strlen(tok); 1544 lidx = llen - tlen; 1545 1546 if (lidx < 0 || 1547 (lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ','))) 1548 return 0; 1549 1550 return (strncasecmp(&line[lidx], tok, tlen) == 0); 1551} 1552 1553AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str) 1554{ 1555 char *cmd; 1556 unsigned char *d; 1557 const unsigned char *s; 1558 1559 cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */ 1560 d = (unsigned char *)cmd; 1561 s = (const unsigned char *)str; 1562 for (; *s; ++s) { 1563 1564#if defined(OS2) || defined(WIN32) 1565 /* 1566 * Newlines to Win32/OS2 CreateProcess() are ill advised. 1567 * Convert them to spaces since they are effectively white 1568 * space to most applications 1569 */ 1570 if (*s == '\r' || *s == '\n') { 1571 *d++ = ' '; 1572 continue; 1573 } 1574#endif 1575 1576 if (TEST_CHAR(*s, T_ESCAPE_SHELL_CMD)) { 1577 *d++ = '\\'; 1578 } 1579 *d++ = *s; 1580 } 1581 *d = '\0'; 1582 1583 return cmd; 1584} 1585 1586static char x2c(const char *what) 1587{ 1588 register char digit; 1589 1590#if !APR_CHARSET_EBCDIC 1591 digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A') + 10 1592 : (what[0] - '0')); 1593 digit *= 16; 1594 digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A') + 10 1595 : (what[1] - '0')); 1596#else /*APR_CHARSET_EBCDIC*/ 1597 char xstr[5]; 1598 xstr[0]='0'; 1599 xstr[1]='x'; 1600 xstr[2]=what[0]; 1601 xstr[3]=what[1]; 1602 xstr[4]='\0'; 1603 digit = apr_xlate_conv_byte(ap_hdrs_from_ascii, 1604 0xFF & strtol(xstr, NULL, 16)); 1605#endif /*APR_CHARSET_EBCDIC*/ 1606 return (digit); 1607} 1608 1609/* 1610 * Unescapes a URL, leaving reserved characters intact. 1611 * Returns 0 on success, non-zero on error 1612 * Failure is due to 1613 * bad % escape returns HTTP_BAD_REQUEST 1614 * 1615 * decoding %00 or a forbidden character returns HTTP_NOT_FOUND 1616 */ 1617 1618static int unescape_url(char *url, const char *forbid, const char *reserved) 1619{ 1620 register int badesc, badpath; 1621 char *x, *y; 1622 1623 badesc = 0; 1624 badpath = 0; 1625 /* Initial scan for first '%'. Don't bother writing values before 1626 * seeing a '%' */ 1627 y = strchr(url, '%'); 1628 if (y == NULL) { 1629 return OK; 1630 } 1631 for (x = y; *y; ++x, ++y) { 1632 if (*y != '%') { 1633 *x = *y; 1634 } 1635 else { 1636 if (!apr_isxdigit(*(y + 1)) || !apr_isxdigit(*(y + 2))) { 1637 badesc = 1; 1638 *x = '%'; 1639 } 1640 else { 1641 char decoded; 1642 decoded = x2c(y + 1); 1643 if ((decoded == '\0') 1644 || (forbid && ap_strchr_c(forbid, decoded))) { 1645 badpath = 1; 1646 *x = decoded; 1647 y += 2; 1648 } 1649 else if (reserved && ap_strchr_c(reserved, decoded)) { 1650 *x++ = *y++; 1651 *x++ = *y++; 1652 *x = *y; 1653 } 1654 else { 1655 *x = decoded; 1656 y += 2; 1657 } 1658 } 1659 } 1660 } 1661 *x = '\0'; 1662 if (badesc) { 1663 return HTTP_BAD_REQUEST; 1664 } 1665 else if (badpath) { 1666 return HTTP_NOT_FOUND; 1667 } 1668 else { 1669 return OK; 1670 } 1671} 1672AP_DECLARE(int) ap_unescape_url(char *url) 1673{ 1674 /* Traditional */ 1675 return unescape_url(url, SLASHES, NULL); 1676} 1677AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes) 1678{ 1679 /* AllowEncodedSlashes (corrected) */ 1680 if (decode_slashes) { 1681 /* no chars reserved */ 1682 return unescape_url(url, NULL, NULL); 1683 } else { 1684 /* reserve (do not decode) encoded slashes */ 1685 return unescape_url(url, NULL, SLASHES); 1686 } 1687} 1688#ifdef NEW_APIS 1689/* IFDEF these out until they've been thought through. 1690 * Just a germ of an API extension for now 1691 */ 1692AP_DECLARE(int) ap_unescape_url_proxy(char *url) 1693{ 1694 /* leave RFC1738 reserved characters intact, * so proxied URLs 1695 * don't get mangled. Where does that leave encoded '&' ? 1696 */ 1697 return unescape_url(url, NULL, "/;?"); 1698} 1699AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved) 1700{ 1701 return unescape_url(url, NULL, reserved); 1702} 1703#endif 1704 1705AP_DECLARE(int) ap_unescape_urlencoded(char *query) 1706{ 1707 char *slider; 1708 1709 /* replace plus with a space */ 1710 if (query) { 1711 for (slider = query; *slider; slider++) { 1712 if (*slider == '+') { 1713 *slider = ' '; 1714 } 1715 } 1716 } 1717 1718 /* unescape everything else */ 1719 return unescape_url(query, NULL, NULL); 1720} 1721 1722AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname, 1723 apr_port_t port, const request_rec *r) 1724{ 1725 if (ap_is_default_port(port, r)) { 1726 return apr_pstrdup(p, hostname); 1727 } 1728 else { 1729 return apr_psprintf(p, "%s:%u", hostname, port); 1730 } 1731} 1732 1733AP_DECLARE(int) ap_unescape_all(char *url) 1734{ 1735 return unescape_url(url, NULL, NULL); 1736} 1737 1738/* c2x takes an unsigned, and expects the caller has guaranteed that 1739 * 0 <= what < 256... which usually means that you have to cast to 1740 * unsigned char first, because (unsigned)(char)(x) first goes through 1741 * signed extension to an int before the unsigned cast. 1742 * 1743 * The reason for this assumption is to assist gcc code generation -- 1744 * the unsigned char -> unsigned extension is already done earlier in 1745 * both uses of this code, so there's no need to waste time doing it 1746 * again. 1747 */ 1748static const char c2x_table[] = "0123456789abcdef"; 1749 1750static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, 1751 unsigned char *where) 1752{ 1753#if APR_CHARSET_EBCDIC 1754 what = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)what); 1755#endif /*APR_CHARSET_EBCDIC*/ 1756 *where++ = prefix; 1757 *where++ = c2x_table[what >> 4]; 1758 *where++ = c2x_table[what & 0xf]; 1759 return where; 1760} 1761 1762/* 1763 * escape_path_segment() escapes a path segment, as defined in RFC 1808. This 1764 * routine is (should be) OS independent. 1765 * 1766 * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all 1767 * cases if a ':' occurs before the first '/' in the URL, the URL should be 1768 * prefixed with "./" (or the ':' escaped). In the case of Unix, this means 1769 * leaving '/' alone, but otherwise doing what escape_path_segment() does. For 1770 * efficiency reasons, we don't use escape_path_segment(), which is provided for 1771 * reference. Again, RFC 1808 is where this stuff is defined. 1772 * 1773 * If partial is set, os_escape_path() assumes that the path will be appended to 1774 * something with a '/' in it (and thus does not prefix "./"). 1775 */ 1776 1777AP_DECLARE(char *) ap_escape_path_segment_buffer(char *copy, const char *segment) 1778{ 1779 const unsigned char *s = (const unsigned char *)segment; 1780 unsigned char *d = (unsigned char *)copy; 1781 unsigned c; 1782 1783 while ((c = *s)) { 1784 if (TEST_CHAR(c, T_ESCAPE_PATH_SEGMENT)) { 1785 d = c2x(c, '%', d); 1786 } 1787 else { 1788 *d++ = c; 1789 } 1790 ++s; 1791 } 1792 *d = '\0'; 1793 return copy; 1794} 1795 1796AP_DECLARE(char *) ap_escape_path_segment(apr_pool_t *p, const char *segment) 1797{ 1798 return ap_escape_path_segment_buffer(apr_palloc(p, 3 * strlen(segment) + 1), segment); 1799} 1800 1801AP_DECLARE(char *) ap_os_escape_path(apr_pool_t *p, const char *path, int partial) 1802{ 1803 char *copy = apr_palloc(p, 3 * strlen(path) + 3); 1804 const unsigned char *s = (const unsigned char *)path; 1805 unsigned char *d = (unsigned char *)copy; 1806 unsigned c; 1807 1808 if (!partial) { 1809 const char *colon = ap_strchr_c(path, ':'); 1810 const char *slash = ap_strchr_c(path, '/'); 1811 1812 if (colon && (!slash || colon < slash)) { 1813 *d++ = '.'; 1814 *d++ = '/'; 1815 } 1816 } 1817 while ((c = *s)) { 1818 if (TEST_CHAR(c, T_OS_ESCAPE_PATH)) { 1819 d = c2x(c, '%', d); 1820 } 1821 else { 1822 *d++ = c; 1823 } 1824 ++s; 1825 } 1826 *d = '\0'; 1827 return copy; 1828} 1829 1830AP_DECLARE(char *) ap_escape_urlencoded_buffer(char *copy, const char *buffer) 1831{ 1832 const unsigned char *s = (const unsigned char *)buffer; 1833 unsigned char *d = (unsigned char *)copy; 1834 unsigned c; 1835 1836 while ((c = *s)) { 1837 if (TEST_CHAR(c, T_ESCAPE_URLENCODED)) { 1838 d = c2x(c, '%', d); 1839 } 1840 else if (c == ' ') { 1841 *d++ = '+'; 1842 } 1843 else { 1844 *d++ = c; 1845 } 1846 ++s; 1847 } 1848 *d = '\0'; 1849 return copy; 1850} 1851 1852AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer) 1853{ 1854 return ap_escape_urlencoded_buffer(apr_palloc(p, 3 * strlen(buffer) + 1), buffer); 1855} 1856 1857/* ap_escape_uri is now a macro for os_escape_path */ 1858 1859AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc) 1860{ 1861 int i, j; 1862 char *x; 1863 1864 /* first, count the number of extra characters */ 1865 for (i = 0, j = 0; s[i] != '\0'; i++) 1866 if (s[i] == '<' || s[i] == '>') 1867 j += 3; 1868 else if (s[i] == '&') 1869 j += 4; 1870 else if (s[i] == '"') 1871 j += 5; 1872 else if (toasc && !apr_isascii(s[i])) 1873 j += 5; 1874 1875 if (j == 0) 1876 return apr_pstrmemdup(p, s, i); 1877 1878 x = apr_palloc(p, i + j + 1); 1879 for (i = 0, j = 0; s[i] != '\0'; i++, j++) 1880 if (s[i] == '<') { 1881 memcpy(&x[j], "<", 4); 1882 j += 3; 1883 } 1884 else if (s[i] == '>') { 1885 memcpy(&x[j], ">", 4); 1886 j += 3; 1887 } 1888 else if (s[i] == '&') { 1889 memcpy(&x[j], "&", 5); 1890 j += 4; 1891 } 1892 else if (s[i] == '"') { 1893 memcpy(&x[j], """, 6); 1894 j += 5; 1895 } 1896 else if (toasc && !apr_isascii(s[i])) { 1897 char *esc = apr_psprintf(p, "&#%3.3d;", (unsigned char)s[i]); 1898 memcpy(&x[j], esc, 6); 1899 j += 5; 1900 } 1901 else 1902 x[j] = s[i]; 1903 1904 x[j] = '\0'; 1905 return x; 1906} 1907AP_DECLARE(char *) ap_escape_logitem(apr_pool_t *p, const char *str) 1908{ 1909 char *ret; 1910 unsigned char *d; 1911 const unsigned char *s; 1912 apr_size_t length, escapes = 0; 1913 1914 if (!str) { 1915 return NULL; 1916 } 1917 1918 /* Compute how many characters need to be escaped */ 1919 s = (const unsigned char *)str; 1920 for (; *s; ++s) { 1921 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1922 escapes++; 1923 } 1924 } 1925 1926 /* Compute the length of the input string, including NULL */ 1927 length = s - (const unsigned char *)str + 1; 1928 1929 /* Fast path: nothing to escape */ 1930 if (escapes == 0) { 1931 return apr_pmemdup(p, str, length); 1932 } 1933 1934 /* Each escaped character needs up to 3 extra bytes (0 --> \x00) */ 1935 ret = apr_palloc(p, length + 3 * escapes); 1936 d = (unsigned char *)ret; 1937 s = (const unsigned char *)str; 1938 for (; *s; ++s) { 1939 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1940 *d++ = '\\'; 1941 switch(*s) { 1942 case '\b': 1943 *d++ = 'b'; 1944 break; 1945 case '\n': 1946 *d++ = 'n'; 1947 break; 1948 case '\r': 1949 *d++ = 'r'; 1950 break; 1951 case '\t': 1952 *d++ = 't'; 1953 break; 1954 case '\v': 1955 *d++ = 'v'; 1956 break; 1957 case '\\': 1958 case '"': 1959 *d++ = *s; 1960 break; 1961 default: 1962 c2x(*s, 'x', d); 1963 d += 3; 1964 } 1965 } 1966 else { 1967 *d++ = *s; 1968 } 1969 } 1970 *d = '\0'; 1971 1972 return ret; 1973} 1974 1975AP_DECLARE(apr_size_t) ap_escape_errorlog_item(char *dest, const char *source, 1976 apr_size_t buflen) 1977{ 1978 unsigned char *d, *ep; 1979 const unsigned char *s; 1980 1981 if (!source || !buflen) { /* be safe */ 1982 return 0; 1983 } 1984 1985 d = (unsigned char *)dest; 1986 s = (const unsigned char *)source; 1987 ep = d + buflen - 1; 1988 1989 for (; d < ep && *s; ++s) { 1990 1991 if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) { 1992 *d++ = '\\'; 1993 if (d >= ep) { 1994 --d; 1995 break; 1996 } 1997 1998 switch(*s) { 1999 case '\b': 2000 *d++ = 'b'; 2001 break; 2002 case '\n': 2003 *d++ = 'n'; 2004 break; 2005 case '\r': 2006 *d++ = 'r'; 2007 break; 2008 case '\t': 2009 *d++ = 't'; 2010 break; 2011 case '\v': 2012 *d++ = 'v'; 2013 break; 2014 case '\\': 2015 *d++ = *s; 2016 break; 2017 case '"': /* no need for this in error log */ 2018 d[-1] = *s; 2019 break; 2020 default: 2021 if (d >= ep - 2) { 2022 ep = --d; /* break the for loop as well */ 2023 break; 2024 } 2025 c2x(*s, 'x', d); 2026 d += 3; 2027 } 2028 } 2029 else { 2030 *d++ = *s; 2031 } 2032 } 2033 *d = '\0'; 2034 2035 return (d - (unsigned char *)dest); 2036} 2037 2038AP_DECLARE(void) ap_bin2hex(const void *src, apr_size_t srclen, char *dest) 2039{ 2040 const unsigned char *in = src; 2041 apr_size_t i; 2042 2043 for (i = 0; i < srclen; i++) { 2044 *dest++ = c2x_table[in[i] >> 4]; 2045 *dest++ = c2x_table[in[i] & 0xf]; 2046 } 2047 *dest = '\0'; 2048} 2049 2050AP_DECLARE(int) ap_is_directory(apr_pool_t *p, const char *path) 2051{ 2052 apr_finfo_t finfo; 2053 2054 if (apr_stat(&finfo, path, APR_FINFO_TYPE, p) != APR_SUCCESS) 2055 return 0; /* in error condition, just return no */ 2056 2057 return (finfo.filetype == APR_DIR); 2058} 2059 2060AP_DECLARE(int) ap_is_rdirectory(apr_pool_t *p, const char *path) 2061{ 2062 apr_finfo_t finfo; 2063 2064 if (apr_stat(&finfo, path, APR_FINFO_LINK | APR_FINFO_TYPE, p) != APR_SUCCESS) 2065 return 0; /* in error condition, just return no */ 2066 2067 return (finfo.filetype == APR_DIR); 2068} 2069 2070AP_DECLARE(char *) ap_make_full_path(apr_pool_t *a, const char *src1, 2071 const char *src2) 2072{ 2073 apr_size_t len1, len2; 2074 char *path; 2075 2076 len1 = strlen(src1); 2077 len2 = strlen(src2); 2078 /* allocate +3 for '/' delimiter, trailing NULL and overallocate 2079 * one extra byte to allow the caller to add a trailing '/' 2080 */ 2081 path = (char *)apr_palloc(a, len1 + len2 + 3); 2082 if (len1 == 0) { 2083 *path = '/'; 2084 memcpy(path + 1, src2, len2 + 1); 2085 } 2086 else { 2087 char *next; 2088 memcpy(path, src1, len1); 2089 next = path + len1; 2090 if (next[-1] != '/') { 2091 *next++ = '/'; 2092 } 2093 memcpy(next, src2, len2 + 1); 2094 } 2095 return path; 2096} 2097 2098/* 2099 * Check for an absoluteURI syntax (see section 3.2 in RFC2068). 2100 */ 2101AP_DECLARE(int) ap_is_url(const char *u) 2102{ 2103 register int x; 2104 2105 for (x = 0; u[x] != ':'; x++) { 2106 if ((!u[x]) || 2107 ((!apr_isalpha(u[x])) && (!apr_isdigit(u[x])) && 2108 (u[x] != '+') && (u[x] != '-') && (u[x] != '.'))) { 2109 return 0; 2110 } 2111 } 2112 2113 return (x ? 1 : 0); /* If the first character is ':', it's broken, too */ 2114} 2115 2116AP_DECLARE(int) ap_ind(const char *s, char c) 2117{ 2118 const char *p = ap_strchr_c(s, c); 2119 2120 if (p == NULL) 2121 return -1; 2122 return p - s; 2123} 2124 2125AP_DECLARE(int) ap_rind(const char *s, char c) 2126{ 2127 const char *p = ap_strrchr_c(s, c); 2128 2129 if (p == NULL) 2130 return -1; 2131 return p - s; 2132} 2133 2134AP_DECLARE(void) ap_str_tolower(char *str) 2135{ 2136 while (*str) { 2137 *str = apr_tolower(*str); 2138 ++str; 2139 } 2140} 2141 2142AP_DECLARE(void) ap_str_toupper(char *str) 2143{ 2144 while (*str) { 2145 *str = apr_toupper(*str); 2146 ++str; 2147 } 2148} 2149 2150/* 2151 * We must return a FQDN 2152 */ 2153char *ap_get_local_host(apr_pool_t *a) 2154{ 2155#ifndef MAXHOSTNAMELEN 2156#define MAXHOSTNAMELEN 256 2157#endif 2158 char str[MAXHOSTNAMELEN + 1]; 2159 char *server_hostname = NULL; 2160 apr_sockaddr_t *sockaddr; 2161 char *hostname; 2162 2163 if (apr_gethostname(str, sizeof(str) - 1, a) != APR_SUCCESS) { 2164 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00556) 2165 "%s: apr_gethostname() failed to determine ServerName", 2166 ap_server_argv0); 2167 } else { 2168 str[sizeof(str) - 1] = '\0'; 2169 if (apr_sockaddr_info_get(&sockaddr, str, APR_UNSPEC, 0, 0, a) == APR_SUCCESS) { 2170 if ( (apr_getnameinfo(&hostname, sockaddr, 0) == APR_SUCCESS) && 2171 (ap_strchr_c(hostname, '.')) ) { 2172 server_hostname = apr_pstrdup(a, hostname); 2173 return server_hostname; 2174 } else if (ap_strchr_c(str, '.')) { 2175 server_hostname = apr_pstrdup(a, str); 2176 } else { 2177 apr_sockaddr_ip_get(&hostname, sockaddr); 2178 server_hostname = apr_pstrdup(a, hostname); 2179 } 2180 } else { 2181 ap_log_perror(APLOG_MARK, APLOG_STARTUP | APLOG_WARNING, 0, a, APLOGNO(00557) 2182 "%s: apr_sockaddr_info_get() failed for %s", 2183 ap_server_argv0, str); 2184 } 2185 } 2186 2187 if (!server_hostname) 2188 server_hostname = apr_pstrdup(a, "127.0.0.1"); 2189 2190 ap_log_perror(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0, a, APLOGNO(00558) 2191 "%s: Could not reliably determine the server's fully qualified " 2192 "domain name, using %s. Set the 'ServerName' directive globally " 2193 "to suppress this message", 2194 ap_server_argv0, server_hostname); 2195 2196 return server_hostname; 2197} 2198 2199/* simple 'pool' alloc()ing glue to apr_base64.c 2200 */ 2201AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded) 2202{ 2203 char *decoded; 2204 int l; 2205 2206 decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded)); 2207 l = apr_base64_decode(decoded, bufcoded); 2208 decoded[l] = '\0'; /* make binary sequence into string */ 2209 2210 return decoded; 2211} 2212 2213AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string) 2214{ 2215 char *encoded; 2216 int l = strlen(string); 2217 2218 encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l)); 2219 l = apr_base64_encode(encoded, string, l); 2220 encoded[l] = '\0'; /* make binary sequence into string */ 2221 2222 return encoded; 2223} 2224 2225/* we want to downcase the type/subtype for comparison purposes 2226 * but nothing else because ;parameter=foo values are case sensitive. 2227 * XXX: in truth we want to downcase parameter names... but really, 2228 * apache has never handled parameters and such correctly. You 2229 * also need to compress spaces and such to be able to compare 2230 * properly. -djg 2231 */ 2232AP_DECLARE(void) ap_content_type_tolower(char *str) 2233{ 2234 char *semi; 2235 2236 semi = strchr(str, ';'); 2237 if (semi) { 2238 *semi = '\0'; 2239 } 2240 2241 ap_str_tolower(str); 2242 2243 if (semi) { 2244 *semi = ';'; 2245 } 2246} 2247 2248/* 2249 * Given a string, replace any bare " with \" . 2250 */ 2251AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring) 2252{ 2253 int newlen = 0; 2254 const char *inchr = instring; 2255 char *outchr, *outstring; 2256 2257 /* 2258 * Look through the input string, jogging the length of the output 2259 * string up by an extra byte each time we find an unescaped ". 2260 */ 2261 while (*inchr != '\0') { 2262 newlen++; 2263 if (*inchr == '"') { 2264 newlen++; 2265 } 2266 /* 2267 * If we find a slosh, and it's not the last byte in the string, 2268 * it's escaping something - advance past both bytes. 2269 */ 2270 if ((*inchr == '\\') && (inchr[1] != '\0')) { 2271 inchr++; 2272 newlen++; 2273 } 2274 inchr++; 2275 } 2276 outstring = apr_palloc(p, newlen + 1); 2277 inchr = instring; 2278 outchr = outstring; 2279 /* 2280 * Now copy the input string to the output string, inserting a slosh 2281 * in front of every " that doesn't already have one. 2282 */ 2283 while (*inchr != '\0') { 2284 if ((*inchr == '\\') && (inchr[1] != '\0')) { 2285 *outchr++ = *inchr++; 2286 *outchr++ = *inchr++; 2287 } 2288 if (*inchr == '"') { 2289 *outchr++ = '\\'; 2290 } 2291 if (*inchr != '\0') { 2292 *outchr++ = *inchr++; 2293 } 2294 } 2295 *outchr = '\0'; 2296 return outstring; 2297} 2298 2299/* 2300 * Given a string, append the PID deliminated by delim. 2301 * Usually used to create a pid-appended filepath name 2302 * (eg: /a/b/foo -> /a/b/foo.6726). A function, and not 2303 * a macro, to avoid unistd.h dependency 2304 */ 2305AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string, 2306 const char *delim) 2307{ 2308 return apr_psprintf(p, "%s%s%" APR_PID_T_FMT, string, 2309 delim, getpid()); 2310 2311} 2312 2313/** 2314 * Parse a given timeout parameter string into an apr_interval_time_t value. 2315 * The unit of the time interval is given as postfix string to the numeric 2316 * string. Currently the following units are understood: 2317 * 2318 * ms : milliseconds 2319 * s : seconds 2320 * mi[n] : minutes 2321 * h : hours 2322 * 2323 * If no unit is contained in the given timeout parameter the default_time_unit 2324 * will be used instead. 2325 * @param timeout_parameter The string containing the timeout parameter. 2326 * @param timeout The timeout value to be returned. 2327 * @param default_time_unit The default time unit to use if none is specified 2328 * in timeout_parameter. 2329 * @return Status value indicating whether the parsing was successful or not. 2330 */ 2331AP_DECLARE(apr_status_t) ap_timeout_parameter_parse( 2332 const char *timeout_parameter, 2333 apr_interval_time_t *timeout, 2334 const char *default_time_unit) 2335{ 2336 char *endp; 2337 const char *time_str; 2338 apr_int64_t tout; 2339 2340 tout = apr_strtoi64(timeout_parameter, &endp, 10); 2341 if (errno) { 2342 return errno; 2343 } 2344 if (!endp || !*endp) { 2345 time_str = default_time_unit; 2346 } 2347 else { 2348 time_str = endp; 2349 } 2350 2351 switch (*time_str) { 2352 /* Time is in seconds */ 2353 case 's': 2354 *timeout = (apr_interval_time_t) apr_time_from_sec(tout); 2355 break; 2356 case 'h': 2357 /* Time is in hours */ 2358 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600); 2359 break; 2360 case 'm': 2361 switch (*(++time_str)) { 2362 /* Time is in milliseconds */ 2363 case 's': 2364 *timeout = (apr_interval_time_t) tout * 1000; 2365 break; 2366 /* Time is in minutes */ 2367 case 'i': 2368 *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60); 2369 break; 2370 default: 2371 return APR_EGENERAL; 2372 } 2373 break; 2374 default: 2375 return APR_EGENERAL; 2376 } 2377 return APR_SUCCESS; 2378} 2379 2380/** 2381 * Determine if a request has a request body or not. 2382 * 2383 * @param r the request_rec of the request 2384 * @return truth value 2385 */ 2386AP_DECLARE(int) ap_request_has_body(request_rec *r) 2387{ 2388 apr_off_t cl; 2389 char *estr; 2390 const char *cls; 2391 int has_body; 2392 2393 has_body = (!r->header_only 2394 && (r->kept_body 2395 || apr_table_get(r->headers_in, "Transfer-Encoding") 2396 || ( (cls = apr_table_get(r->headers_in, "Content-Length")) 2397 && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS) 2398 && (!*estr) 2399 && (cl > 0) ) 2400 ) 2401 ); 2402 return has_body; 2403} 2404 2405AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_) 2406{ 2407 void **ptr = (void **)data_; 2408 *ptr = NULL; 2409 return APR_SUCCESS; 2410} 2411 2412AP_DECLARE(apr_status_t) ap_str2_alnum(const char *src, char *dest) { 2413 2414 for ( ; *src; src++, dest++) 2415 { 2416 if (!apr_isprint(*src)) 2417 *dest = 'x'; 2418 else if (!apr_isalnum(*src)) 2419 *dest = '_'; 2420 else 2421 *dest = (char)*src; 2422 } 2423 *dest = '\0'; 2424 return APR_SUCCESS; 2425 2426} 2427 2428AP_DECLARE(apr_status_t) ap_pstr2_alnum(apr_pool_t *p, const char *src, 2429 const char **dest) 2430{ 2431 char *new = apr_palloc(p, strlen(src)+1); 2432 if (!new) 2433 return APR_ENOMEM; 2434 *dest = new; 2435 return ap_str2_alnum(src, new); 2436} 2437 2438/** 2439 * Read the body and parse any form found, which must be of the 2440 * type application/x-www-form-urlencoded. 2441 * 2442 * Name/value pairs are returned in an array, with the names as 2443 * strings with a maximum length of HUGE_STRING_LEN, and the 2444 * values as bucket brigades. This allows values to be arbitrarily 2445 * large. 2446 * 2447 * All url-encoding is removed from both the names and the values 2448 * on the fly. The names are interpreted as strings, while the 2449 * values are interpreted as blocks of binary data, that may 2450 * contain the 0 character. 2451 * 2452 * In order to ensure that resource limits are not exceeded, a 2453 * maximum size must be provided. If the sum of the lengths of 2454 * the names and the values exceed this size, this function 2455 * will return HTTP_REQUEST_ENTITY_TOO_LARGE. 2456 * 2457 * An optional number of parameters can be provided, if the number 2458 * of parameters provided exceeds this amount, this function will 2459 * return HTTP_REQUEST_ENTITY_TOO_LARGE. If this value is negative, 2460 * no limit is imposed, and the number of parameters is in turn 2461 * constrained by the size parameter above. 2462 * 2463 * This function honours any kept_body configuration, and the 2464 * original raw request body will be saved to the kept_body brigade 2465 * if so configured, just as ap_discard_request_body does. 2466 * 2467 * NOTE: File upload is not yet supported, but can be without change 2468 * to the function call. 2469 */ 2470 2471/* form parsing stuff */ 2472typedef enum { 2473 FORM_NORMAL, 2474 FORM_AMP, 2475 FORM_NAME, 2476 FORM_VALUE, 2477 FORM_PERCENTA, 2478 FORM_PERCENTB, 2479 FORM_ABORT 2480} ap_form_type_t; 2481 2482AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f, 2483 apr_array_header_t **ptr, 2484 apr_size_t num, apr_size_t usize) 2485{ 2486 apr_bucket_brigade *bb = NULL; 2487 int seen_eos = 0; 2488 char buffer[HUGE_STRING_LEN + 1]; 2489 const char *ct; 2490 apr_size_t offset = 0; 2491 apr_ssize_t size; 2492 ap_form_type_t state = FORM_NAME, percent = FORM_NORMAL; 2493 ap_form_pair_t *pair = NULL; 2494 apr_array_header_t *pairs = apr_array_make(r->pool, 4, sizeof(ap_form_pair_t)); 2495 2496 char hi = 0; 2497 char low = 0; 2498 2499 *ptr = pairs; 2500 2501 /* sanity check - we only support forms for now */ 2502 ct = apr_table_get(r->headers_in, "Content-Type"); 2503 if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) { 2504 return ap_discard_request_body(r); 2505 } 2506 2507 if (usize > APR_SIZE_MAX >> 1) 2508 size = APR_SIZE_MAX >> 1; 2509 else 2510 size = usize; 2511 2512 if (!f) { 2513 f = r->input_filters; 2514 } 2515 2516 bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); 2517 do { 2518 apr_bucket *bucket = NULL, *last = NULL; 2519 2520 int rv = ap_get_brigade(f, bb, AP_MODE_READBYTES, 2521 APR_BLOCK_READ, HUGE_STRING_LEN); 2522 if (rv != APR_SUCCESS) { 2523 apr_brigade_destroy(bb); 2524 return (rv == AP_FILTER_ERROR) ? rv : HTTP_BAD_REQUEST; 2525 } 2526 2527 for (bucket = APR_BRIGADE_FIRST(bb); 2528 bucket != APR_BRIGADE_SENTINEL(bb); 2529 last = bucket, bucket = APR_BUCKET_NEXT(bucket)) { 2530 const char *data; 2531 apr_size_t len, slide; 2532 2533 if (last) { 2534 apr_bucket_delete(last); 2535 } 2536 if (APR_BUCKET_IS_EOS(bucket)) { 2537 seen_eos = 1; 2538 break; 2539 } 2540 if (bucket->length == 0) { 2541 continue; 2542 } 2543 2544 rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ); 2545 if (rv != APR_SUCCESS) { 2546 apr_brigade_destroy(bb); 2547 return HTTP_BAD_REQUEST; 2548 } 2549 2550 slide = len; 2551 while (state != FORM_ABORT && slide-- > 0 && size >= 0 && num != 0) { 2552 char c = *data++; 2553 if ('+' == c) { 2554 c = ' '; 2555 } 2556 else if ('&' == c) { 2557 state = FORM_AMP; 2558 } 2559 if ('%' == c) { 2560 percent = FORM_PERCENTA; 2561 continue; 2562 } 2563 if (FORM_PERCENTA == percent) { 2564 if (c >= 'a') { 2565 hi = c - 'a' + 10; 2566 } 2567 else if (c >= 'A') { 2568 hi = c - 'A' + 10; 2569 } 2570 else if (c >= '0') { 2571 hi = c - '0'; 2572 } 2573 hi = hi << 4; 2574 percent = FORM_PERCENTB; 2575 continue; 2576 } 2577 if (FORM_PERCENTB == percent) { 2578 if (c >= 'a') { 2579 low = c - 'a' + 10; 2580 } 2581 else if (c >= 'A') { 2582 low = c - 'A' + 10; 2583 } 2584 else if (c >= '0') { 2585 low = c - '0'; 2586 } 2587 c = low | hi; 2588 percent = FORM_NORMAL; 2589 } 2590 switch (state) { 2591 case FORM_AMP: 2592 if (pair) { 2593 const char *tmp = apr_pmemdup(r->pool, buffer, offset); 2594 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc); 2595 APR_BRIGADE_INSERT_TAIL(pair->value, b); 2596 } 2597 state = FORM_NAME; 2598 pair = NULL; 2599 offset = 0; 2600 num--; 2601 break; 2602 case FORM_NAME: 2603 if (offset < HUGE_STRING_LEN) { 2604 if ('=' == c) { 2605 buffer[offset] = 0; 2606 offset = 0; 2607 pair = (ap_form_pair_t *) apr_array_push(pairs); 2608 pair->name = apr_pstrdup(r->pool, buffer); 2609 pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc); 2610 state = FORM_VALUE; 2611 } 2612 else { 2613 buffer[offset++] = c; 2614 size--; 2615 } 2616 } 2617 else { 2618 state = FORM_ABORT; 2619 } 2620 break; 2621 case FORM_VALUE: 2622 if (offset >= HUGE_STRING_LEN) { 2623 const char *tmp = apr_pmemdup(r->pool, buffer, offset); 2624 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc); 2625 APR_BRIGADE_INSERT_TAIL(pair->value, b); 2626 offset = 0; 2627 } 2628 buffer[offset++] = c; 2629 size--; 2630 break; 2631 default: 2632 break; 2633 } 2634 } 2635 2636 } 2637 2638 apr_brigade_cleanup(bb); 2639 } while (!seen_eos); 2640 2641 if (FORM_ABORT == state || size < 0 || num == 0) { 2642 return HTTP_REQUEST_ENTITY_TOO_LARGE; 2643 } 2644 else if (FORM_VALUE == state && pair && offset > 0) { 2645 const char *tmp = apr_pmemdup(r->pool, buffer, offset); 2646 apr_bucket *b = apr_bucket_pool_create(tmp, offset, r->pool, r->connection->bucket_alloc); 2647 APR_BRIGADE_INSERT_TAIL(pair->value, b); 2648 } 2649 2650 return OK; 2651 2652} 2653 2654#define VARBUF_SMALL_SIZE 2048 2655#define VARBUF_MAX_SIZE (APR_SIZE_MAX - 1 - \ 2656 APR_ALIGN_DEFAULT(sizeof(struct ap_varbuf_info))) 2657 2658struct ap_varbuf_info { 2659 struct apr_memnode_t *node; 2660 apr_allocator_t *allocator; 2661}; 2662 2663static apr_status_t varbuf_cleanup(void *info_) 2664{ 2665 struct ap_varbuf_info *info = info_; 2666 info->node->next = NULL; 2667 apr_allocator_free(info->allocator, info->node); 2668 return APR_SUCCESS; 2669} 2670 2671const char nul = '\0'; 2672static char * const varbuf_empty = (char *)&nul; 2673 2674AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb, 2675 apr_size_t init_size) 2676{ 2677 vb->buf = varbuf_empty; 2678 vb->avail = 0; 2679 vb->strlen = AP_VARBUF_UNKNOWN; 2680 vb->pool = p; 2681 vb->info = NULL; 2682 2683 ap_varbuf_grow(vb, init_size); 2684} 2685 2686AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len) 2687{ 2688 apr_memnode_t *new_node = NULL; 2689 apr_allocator_t *allocator; 2690 struct ap_varbuf_info *new_info; 2691 char *new; 2692 2693 AP_DEBUG_ASSERT(vb->strlen == AP_VARBUF_UNKNOWN || vb->avail >= vb->strlen); 2694 2695 if (new_len <= vb->avail) 2696 return; 2697 2698 if (new_len < 2 * vb->avail && vb->avail < VARBUF_MAX_SIZE/2) { 2699 /* at least double the size, to avoid repeated reallocations */ 2700 new_len = 2 * vb->avail; 2701 } 2702 else if (new_len > VARBUF_MAX_SIZE) { 2703 apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool); 2704 ap_assert(abort_fn != NULL); 2705 abort_fn(APR_ENOMEM); 2706 return; 2707 } 2708 2709 new_len++; /* add space for trailing \0 */ 2710 if (new_len <= VARBUF_SMALL_SIZE) { 2711 new_len = APR_ALIGN_DEFAULT(new_len); 2712 new = apr_palloc(vb->pool, new_len); 2713 if (vb->avail && vb->strlen != 0) { 2714 AP_DEBUG_ASSERT(vb->buf != NULL); 2715 AP_DEBUG_ASSERT(vb->buf != varbuf_empty); 2716 if (new == vb->buf + vb->avail + 1) { 2717 /* We are lucky: the new memory lies directly after our old 2718 * buffer, we can now use both. 2719 */ 2720 vb->avail += new_len; 2721 return; 2722 } 2723 else { 2724 /* copy up to vb->strlen + 1 bytes */ 2725 memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ? 2726 vb->avail + 1 : vb->strlen + 1); 2727 } 2728 } 2729 else { 2730 *new = '\0'; 2731 } 2732 vb->avail = new_len - 1; 2733 vb->buf = new; 2734 return; 2735 } 2736 2737 /* The required block is rather larger. Use allocator directly so that 2738 * the memory can be freed independently from the pool. */ 2739 allocator = apr_pool_allocator_get(vb->pool); 2740 if (new_len <= VARBUF_MAX_SIZE) 2741 new_node = apr_allocator_alloc(allocator, 2742 new_len + APR_ALIGN_DEFAULT(sizeof(*new_info))); 2743 if (!new_node) { 2744 apr_abortfunc_t abort_fn = apr_pool_abort_get(vb->pool); 2745 ap_assert(abort_fn != NULL); 2746 abort_fn(APR_ENOMEM); 2747 return; 2748 } 2749 new_info = (struct ap_varbuf_info *)new_node->first_avail; 2750 new_node->first_avail += APR_ALIGN_DEFAULT(sizeof(*new_info)); 2751 new_info->node = new_node; 2752 new_info->allocator = allocator; 2753 new = new_node->first_avail; 2754 AP_DEBUG_ASSERT(new_node->endp - new_node->first_avail >= new_len); 2755 new_len = new_node->endp - new_node->first_avail; 2756 2757 if (vb->avail && vb->strlen != 0) 2758 memcpy(new, vb->buf, vb->strlen == AP_VARBUF_UNKNOWN ? 2759 vb->avail + 1 : vb->strlen + 1); 2760 else 2761 *new = '\0'; 2762 if (vb->info) 2763 apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup); 2764 apr_pool_cleanup_register(vb->pool, new_info, varbuf_cleanup, 2765 apr_pool_cleanup_null); 2766 vb->info = new_info; 2767 vb->buf = new; 2768 vb->avail = new_len - 1; 2769} 2770 2771AP_DECLARE(void) ap_varbuf_strmemcat(struct ap_varbuf *vb, const char *str, 2772 int len) 2773{ 2774 if (len == 0) 2775 return; 2776 if (!vb->avail) { 2777 ap_varbuf_grow(vb, len); 2778 memcpy(vb->buf, str, len); 2779 vb->buf[len] = '\0'; 2780 vb->strlen = len; 2781 return; 2782 } 2783 if (vb->strlen == AP_VARBUF_UNKNOWN) 2784 vb->strlen = strlen(vb->buf); 2785 ap_varbuf_grow(vb, vb->strlen + len); 2786 memcpy(vb->buf + vb->strlen, str, len); 2787 vb->strlen += len; 2788 vb->buf[vb->strlen] = '\0'; 2789} 2790 2791AP_DECLARE(void) ap_varbuf_free(struct ap_varbuf *vb) 2792{ 2793 if (vb->info) { 2794 apr_pool_cleanup_run(vb->pool, vb->info, varbuf_cleanup); 2795 vb->info = NULL; 2796 } 2797 vb->buf = NULL; 2798} 2799 2800AP_DECLARE(char *) ap_varbuf_pdup(apr_pool_t *p, struct ap_varbuf *buf, 2801 const char *prepend, apr_size_t prepend_len, 2802 const char *append, apr_size_t append_len, 2803 apr_size_t *new_len) 2804{ 2805 apr_size_t i = 0; 2806 struct iovec vec[3]; 2807 2808 if (prepend) { 2809 vec[i].iov_base = (void *)prepend; 2810 vec[i].iov_len = prepend_len; 2811 i++; 2812 } 2813 if (buf->avail && buf->strlen) { 2814 if (buf->strlen == AP_VARBUF_UNKNOWN) 2815 buf->strlen = strlen(buf->buf); 2816 vec[i].iov_base = (void *)buf->buf; 2817 vec[i].iov_len = buf->strlen; 2818 i++; 2819 } 2820 if (append) { 2821 vec[i].iov_base = (void *)append; 2822 vec[i].iov_len = append_len; 2823 i++; 2824 } 2825 if (i) 2826 return apr_pstrcatv(p, vec, i, new_len); 2827 2828 if (new_len) 2829 *new_len = 0; 2830 return ""; 2831} 2832 2833AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb, 2834 const char *input, 2835 const char *source, 2836 apr_size_t nmatch, 2837 ap_regmatch_t pmatch[], 2838 apr_size_t maxlen) 2839{ 2840 return regsub_core(NULL, NULL, vb, input, source, nmatch, pmatch, maxlen); 2841} 2842 2843static const char * const oom_message = "[crit] Memory allocation failed, " 2844 "aborting process." APR_EOL_STR; 2845 2846AP_DECLARE(void) ap_abort_on_oom() 2847{ 2848 int written, count = strlen(oom_message); 2849 const char *buf = oom_message; 2850 do { 2851 written = write(STDERR_FILENO, buf, count); 2852 if (written == count) 2853 break; 2854 if (written > 0) { 2855 buf += written; 2856 count -= written; 2857 } 2858 } while (written >= 0 || errno == EINTR); 2859 abort(); 2860} 2861 2862AP_DECLARE(void *) ap_malloc(size_t size) 2863{ 2864 void *p = malloc(size); 2865 if (p == NULL && size != 0) 2866 ap_abort_on_oom(); 2867 return p; 2868} 2869 2870AP_DECLARE(void *) ap_calloc(size_t nelem, size_t size) 2871{ 2872 void *p = calloc(nelem, size); 2873 if (p == NULL && nelem != 0 && size != 0) 2874 ap_abort_on_oom(); 2875 return p; 2876} 2877 2878AP_DECLARE(void *) ap_realloc(void *ptr, size_t size) 2879{ 2880 void *p = realloc(ptr, size); 2881 if (p == NULL && size != 0) 2882 ap_abort_on_oom(); 2883 return p; 2884} 2885 2886AP_DECLARE(void) ap_get_sload(ap_sload_t *ld) 2887{ 2888 int i, j, server_limit, thread_limit; 2889 int ready = 0; 2890 int busy = 0; 2891 int total; 2892 ap_generation_t mpm_generation; 2893 2894 /* preload errored fields, we overwrite */ 2895 ld->idle = -1; 2896 ld->busy = -1; 2897 ld->bytes_served = 0; 2898 ld->access_count = 0; 2899 2900 ap_mpm_query(AP_MPMQ_GENERATION, &mpm_generation); 2901 ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); 2902 ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); 2903 2904 for (i = 0; i < server_limit; i++) { 2905 process_score *ps; 2906 ps = ap_get_scoreboard_process(i); 2907 2908 for (j = 0; j < thread_limit; j++) { 2909 int res; 2910 worker_score *ws = NULL; 2911 ws = &ap_scoreboard_image->servers[i][j]; 2912 res = ws->status; 2913 2914 if (!ps->quiescing && ps->pid) { 2915 if (res == SERVER_READY && ps->generation == mpm_generation) { 2916 ready++; 2917 } 2918 else if (res != SERVER_DEAD && 2919 res != SERVER_STARTING && res != SERVER_IDLE_KILL && 2920 ps->generation == mpm_generation) { 2921 busy++; 2922 } 2923 } 2924 2925 if (ap_extended_status && !ps->quiescing && ps->pid) { 2926 if (ws->access_count != 0 2927 || (res != SERVER_READY && res != SERVER_DEAD)) { 2928 ld->access_count += ws->access_count; 2929 ld->bytes_served += ws->bytes_served; 2930 } 2931 } 2932 } 2933 } 2934 total = busy + ready; 2935 if (total) { 2936 ld->idle = ready * 100 / total; 2937 ld->busy = busy * 100 / total; 2938 } 2939} 2940 2941AP_DECLARE(void) ap_get_loadavg(ap_loadavg_t *ld) 2942{ 2943 /* preload errored fields, we overwrite */ 2944 ld->loadavg = -1.0; 2945 ld->loadavg5 = -1.0; 2946 ld->loadavg15 = -1.0; 2947 2948#if HAVE_GETLOADAVG 2949 { 2950 double la[3]; 2951 int num; 2952 2953 num = getloadavg(la, 3); 2954 if (num > 0) { 2955 ld->loadavg = (float)la[0]; 2956 } 2957 if (num > 1) { 2958 ld->loadavg5 = (float)la[1]; 2959 } 2960 if (num > 2) { 2961 ld->loadavg15 = (float)la[2]; 2962 } 2963 } 2964#endif 2965} 2966 2967AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p, 2968 const char *cmd, 2969 const char * const * argv) 2970{ 2971 char buf[MAX_STRING_LEN]; 2972 apr_procattr_t *procattr; 2973 apr_proc_t *proc; 2974 apr_file_t *fp; 2975 apr_size_t nbytes = 1; 2976 char c; 2977 int k; 2978 2979 if (apr_procattr_create(&procattr, p) != APR_SUCCESS) 2980 return NULL; 2981 if (apr_procattr_io_set(procattr, APR_FULL_BLOCK, APR_FULL_BLOCK, 2982 APR_FULL_BLOCK) != APR_SUCCESS) 2983 return NULL; 2984 if (apr_procattr_dir_set(procattr, 2985 ap_make_dirstr_parent(p, cmd)) != APR_SUCCESS) 2986 return NULL; 2987 if (apr_procattr_cmdtype_set(procattr, APR_PROGRAM) != APR_SUCCESS) 2988 return NULL; 2989 proc = apr_pcalloc(p, sizeof(apr_proc_t)); 2990 if (apr_proc_create(proc, cmd, argv, NULL, procattr, p) != APR_SUCCESS) 2991 return NULL; 2992 fp = proc->out; 2993 2994 if (fp == NULL) 2995 return NULL; 2996 /* XXX: we are reading 1 byte at a time here */ 2997 for (k = 0; apr_file_read(fp, &c, &nbytes) == APR_SUCCESS 2998 && nbytes == 1 && (k < MAX_STRING_LEN-1) ; ) { 2999 if (c == '\n' || c == '\r') 3000 break; 3001 buf[k++] = c; 3002 } 3003 buf[k] = '\0'; 3004 apr_file_close(fp); 3005 3006 return apr_pstrndup(p, buf, k); 3007} 3008