/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * util.c: string utility things * * 3/21/93 Rob McCool * 1995-96 Many changes by the Apache Software Foundation *
*/
/* Debugging aid: * #define DEBUG to trace all cfg_open*()/cfg_closefile() calls * #define DEBUG_CFG_LINES to trace every line read from the config files
*/
/* A bunch of functions in util.c scan strings looking for certain characters. * To make that more efficient we encode a lookup table. The test_char_table * is generated automatically by gen_test_char.c.
*/ #include"test_char.h"
/* we know core's module_index is 0 */ #undef APLOG_MODULE_INDEX #define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
/* maximum nesting level for config directories */ #ifndef AP_MAX_FNMATCH_DIR_DEPTH #define AP_MAX_FNMATCH_DIR_DEPTH (128) #endif
/* * Examine a field value (such as a media-/content-type) string and return * it sans any parameters; e.g., strip off any ';charset=foo' and the like.
*/
AP_DECLARE(char *) ap_field_noparam(apr_pool_t *p, constchar *intype)
{ constchar *semi;
/* Roy owes Rob beer. */ /* Rob owes Roy dinner. */
/* These legacy comments would make a lot more sense if Roy hadn't * replaced the old later_than() routine with util_date.c. * * Well, okay, they still wouldn't make any sense.
*/
/* Match = 0, NoMatch = 1, Abort = -1 * Based loosely on sections of wildmat.c by Rich Salz * Hmmm... shouldn't this really go component by component?
*/
AP_DECLARE(int) ap_strcmp_match(constchar *str, constchar *expected)
{
apr_size_t x, y;
for (x = 0, y = 0; expected[y]; ++y, ++x) { if (expected[y] == '*') { while (expected[++y] == '*'); if (!expected[y]) return 0; while (str[x]) { int ret; if ((ret = ap_strcmp_match(&str[x++], &expected[y])) != 1) return ret;
} return -1;
} elseif (!str[x]) return -1; elseif ((expected[y] != '?') && (str[x] != expected[y])) return 1;
} return (str[x] != '\0');
}
for (x = 0, y = 0; expected[y]; ++y, ++x) { if (!str[x] && expected[y] != '*') return -1; if (expected[y] == '*') { while (expected[++y] == '*'); if (!expected[y]) return 0; while (str[x]) { int ret; if ((ret = ap_strcasecmp_match(&str[x++], &expected[y])) != 1) return ret;
} return -1;
} elseif (expected[y] != '?'
&& apr_tolower(str[x]) != apr_tolower(expected[y])) return 1;
} return (str[x] != '\0');
}
/* We actually compare the canonical root to this root, (but we don't * waste time checking the case), since every use of this function in * httpd-2.1 tests if the path is 'proper', meaning we've already passed * it through apr_filepath_merge, or we haven't.
*/
AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, constchar *dir)
{ constchar *newpath; constchar *ourdir = dir; if (apr_filepath_root(&newpath, &dir, 0, p) != APR_SUCCESS
|| strncmp(newpath, ourdir, strlen(newpath)) != 0) { return 0;
} return 1;
}
/* * Here's a pool-based interface to the POSIX-esque ap_regcomp(). * Note that we return ap_regex_t instead of being passed one. * The reason is that if you use an already-used ap_regex_t structure, * the memory that you've already allocated gets forgotten, and * regfree() doesn't clear it. So we don't allow it.
*/
/* * Similar to standard strstr() but we ignore case in this version. * Based on the strstr() implementation further below.
*/
AP_DECLARE(char *) ap_strcasestr(constchar *s1, constchar *s2)
{ char *p1, *p2; if (*s2 == '\0') { /* an empty s2 */ return((char *)s1);
} while(1) { for ( ; (*s1 != '\0') && (apr_tolower(*s1) != apr_tolower(*s2)); s1++); if (*s1 == '\0') { return(NULL);
} /* found first character of s2, see if the rest matches */
p1 = (char *)s1;
p2 = (char *)s2; for (++p1, ++p2; apr_tolower(*p1) == apr_tolower(*p2); ++p1, ++p2) { if (*p1 == '\0') { /* both strings ended together */ return((char *)s1);
}
} if (*p2 == '\0') { /* second string ended, a match */ break;
} /* didn't find a match here, try starting at next character in s1 */
s1++;
} return((char *)s1);
}
/* * Returns an offsetted pointer in bigstring immediately after * prefix. Returns bigstring if bigstring doesn't start with * prefix or if prefix is longer than bigstring while still matching. * NOTE: pointer returned is relative to bigstring, so we * can use standard pointer comparisons in the calling function * (eg: test if ap_stripprefix(a,b) == a)
*/
AP_DECLARE(constchar *) ap_stripprefix(constchar *bigstring, constchar *prefix)
{ constchar *p1;
if (*prefix == '\0') return bigstring;
p1 = bigstring; while (*p1 && *prefix) { if (*p1++ != *prefix++) return bigstring;
} if (*prefix == '\0') return p1;
/* hit the end of bigstring! */ return bigstring;
}
/* This function substitutes for $0-$9, filling in regular expression * submatches. Pass it the same nmatch and pmatch arguments that you * passed ap_regexec(). pmatch should not be greater than the maximum number * of subexpressions - i.e. one more than the re_nsub member of ap_regex_t. * * nmatch must be <=AP_MAX_REG_MATCH (10). * * input should be the string with the $-expressions, source should be the * string that was matched against. * * It returns the substituted string, or NULL if a vbuf is used. * On errors, returns the orig string. * * Parts of this code are based on Henry Spencer's regsub(), from his * AT&T V8 regexp package.
*/
#define IS_SLASH_OR_NUL(s) (s == '\0' || AP_IS_SLASH(s))
/* * Inspired by mod_jk's jk_servlet_normalize().
*/
AP_DECLARE(int) ap_normalize_path(char *path, unsignedint flags)
{ int ret = 1;
apr_size_t l = 1, w = 1, n; int decode_unreserved = (flags & AP_NORMALIZE_DECODE_UNRESERVED) != 0; int merge_slashes = (flags & AP_NORMALIZE_MERGE_SLASHES) != 0;
if (!AP_IS_SLASH(path[0])) { /* Besides "OPTIONS *", a request-target should start with '/' * per RFC 7230 section 5.3, so anything else is invalid.
*/ if (path[0] == '*' && path[1] == '\0') { return 1;
} /* However, AP_NORMALIZE_ALLOW_RELATIVE can be used to bypass * this restriction (e.g. for subrequest file lookups).
*/ if (!(flags & AP_NORMALIZE_ALLOW_RELATIVE) || path[0] == '\0') { return 0;
}
l = w = 0;
}
while (path[l] != '\0') { /* RFC-3986 section 2.3: * For consistency, percent-encoded octets in the ranges of * ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D), * period (%2E), underscore (%5F), or tilde (%7E) should [...] * be decoded to their corresponding unreserved characters by * URI normalizers.
*/ if (decode_unreserved && path[l] == '%') { if (apr_isxdigit(path[l + 1]) && apr_isxdigit(path[l + 2])) { constchar c = x2c(&path[l + 1]); if (TEST_CHAR(c, T_URI_UNRESERVED)) { /* Replace last char and fall through as the current
* read position */
l += 2;
path[l] = c;
}
} else { /* Invalid encoding */
ret = 0;
}
}
if (w == 0 || AP_IS_SLASH(path[w - 1])) { /* Collapse ///// sequences to / */ if (merge_slashes && AP_IS_SLASH(path[l])) { do {
l++;
} while (AP_IS_SLASH(path[l])); continue;
}
if (path[l] == '.') { /* Remove /./ segments */ if (IS_SLASH_OR_NUL(path[l + 1])) {
l++; if (path[l]) {
l++;
} continue;
}
/* Remove /xx/../ segments (or /xx/.%2e/ when * AP_NORMALIZE_DECODE_UNRESERVED is set since we * decoded only the first dot above).
*/
n = l + 1; if ((path[n] == '.' || (decode_unreserved
&& path[n] == '%'
&& path[++n] == '2'
&& (path[++n] == 'e'
|| path[n] == 'E')))
&& IS_SLASH_OR_NUL(path[n + 1])) { /* Wind w back to remove the previous segment */ if (w > 1) { do {
w--;
} while (w && !AP_IS_SLASH(path[w - 1]));
} else { /* Already at root, ignore and return a failure * if asked to.
*/ if (flags & AP_NORMALIZE_NOT_ABOVE_ROOT) {
ret = 0;
}
}
/* Move l forward to the next segment */
l = n + 1; if (path[l]) {
l++;
} continue;
}
}
}
path[w++] = path[l++];
}
path[w] = '\0';
return ret;
}
/* * Parse .. so we don't compromise security
*/
AP_DECLARE(void) ap_getparents(char *name)
{ if (!ap_normalize_path(name, AP_NORMALIZE_NOT_ABOVE_ROOT |
AP_NORMALIZE_ALLOW_RELATIVE)) {
name[0] = '\0';
}
}
AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
{
char *d, *s;
if (!*name) { return;
}
s = d = name;
#ifdef HAVE_UNC_PATHS /* Check for UNC names. Leave leading two slashes. */ if (is_fs_path && s[0] == '/' && s[1] == '/')
*d++ = *s++; #endif
while (*s) { if ((*d++ = *s) == '/') { do {
++s;
} while (*s == '/');
} else {
++s;
}
}
*d = '\0';
}
/* * copy at most n leading directories of s into d * d should be at least as large as s plus 1 extra byte * assumes n > 0 * the return value is the ever useful pointer to the trailing \0 of d * * MODIFIED FOR HAVE_DRIVE_LETTERS and NETWARE environments, * so that if n == 0, "/" is returned in d with n == 1 * and s == "e:/test.html", "e:/" is returned in d * *** See also ap_directory_walk in server/request.c * * examples: * /a/b, 0 ==> / (true for all platforms) * /a/b, 1 ==> / * /a/b, 2 ==> /a/ * /a/b, 3 ==> /a/b/ * /a/b, 4 ==> /a/b/ * * c:/a/b 0 ==> / * c:/a/b 1 ==> c:/ * c:/a/b 2 ==> c:/a/ * c:/a/b 3 ==> c:/a/b * c:/a/b 4 ==> c:/a/b
*/
AP_DECLARE(char *) ap_make_dirstr_prefix(char *d, constchar *s, int n)
{ if (n < 1) {
*d = '/';
*++d = '\0'; return (d);
}
/* * return the parent directory name including trailing / of the file s
*/
AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, constchar *s)
{ constchar *last_slash = ap_strrchr_c(s, '/'); char *d; int l;
if (last_slash == NULL) { return apr_pstrdup(p, "");
}
l = (last_slash - s) + 1;
d = apr_pstrmemdup(p, s, l);
return (d);
}
AP_DECLARE(int) ap_count_dirs(constchar *path)
{ int x, n;
for (x = 0, n = 0; path[x]; x++) if (path[x] == '/')
n++; return n;
}
/* we can't use apr_file_* directly because of linking issues on Windows */ static apr_status_t cfg_close(void *param)
{ return apr_file_close(param);
}
status = apr_file_info_get(&finfo, APR_FINFO_TYPE, file); if (status != APR_SUCCESS) return status;
if (finfo.filetype != APR_REG && #ifdefined(WIN32) || defined(OS2) || defined(NETWARE)
ap_cstr_casecmp(apr_filepath_name_get(name), "nul") != 0) { #else
strcmp(name, "/dev/null") != 0) { #endif/* WIN32 || OS2 */
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00554) "Access to file %s denied by server: not a regular file",
name);
apr_file_close(file); return APR_EBADF;
}
#ifdef WIN32 /* Some twisted character [no pun intended] at MS decided that a * zero width joiner as the lead wide character would be ideal for * describing Unicode text files. This was further convoluted to * another MSism that the same character mapped into utf-8, EF BB BF * would signify utf-8 text files. * * Since MS configuration files are all protecting utf-8 encoded * Unicode path, file and resource names, we already have the correct * WinNT encoding. But at least eat the stupid three bytes up front.
*/
{ unsignedchar buf[4];
apr_size_t len = 3;
status = apr_file_read(file, buf, &len); if ((status != APR_SUCCESS) || (len < 3)
|| memcmp(buf, "\xEF\xBB\xBF", 3) != 0) {
apr_off_t zero = 0;
apr_file_seek(file, APR_SET, &zero);
}
} #endif
if (rc == APR_ENOSPC) return apr_psprintf(p, "Error reading %s at line %d: Line too long",
cfp->name, cfp->line_number);
return apr_psprintf(p, "Error reading %s at line %d: %pm",
cfp->name, cfp->line_number, &rc);
}
/* Read one line from open ap_configfile_t, strip LF, increase line number */ /* If custom handler does not define a getstr() function, read char by char */ static apr_status_t ap_cfg_getline_core(char *buf, apr_size_t bufsize,
apr_size_t offset, ap_configfile_t *cfp)
{
apr_status_t rc; /* If a "get string" function is defined, use it */ if (cfp->getstr != NULL) { char *cp; char *cbuf = buf + offset;
apr_size_t cbufsize = bufsize - offset;
while (1) {
++cfp->line_number;
rc = cfp->getstr(cbuf, cbufsize, cfp->param); if (rc == APR_EOF) { if (cbuf != buf + offset) {
*cbuf = '\0'; break;
} else { return APR_EOF;
}
} if (rc != APR_SUCCESS) { return rc;
}
/* * check for line continuation, * i.e. match [^\\]\\[\r]\n only
*/
cp = cbuf;
cp += strlen(cp); if (cp > buf && cp[-1] == LF) {
cp--; if (cp > buf && cp[-1] == CR)
cp--; if (cp > buf && cp[-1] == '\\') {
cp--; /* * line continuation requested - * then remove backslash and continue
*/
cbufsize -= (cp-cbuf);
cbuf = cp; continue;
}
} elseif (cp - buf >= bufsize - 1) { return APR_ENOSPC;
} break;
}
} else { /* No "get string" function defined; read character by character */
apr_size_t i = offset;
if (bufsize < 2) { /* too small, assume caller is crazy */ return APR_EINVAL;
}
buf[offset] = '\0';
while (1) { char c;
rc = cfp->getch(&c, cfp->param); if (rc == APR_EOF) { if (i > offset) break; else return APR_EOF;
} if (rc != APR_SUCCESS) return rc; if (c == LF) {
++cfp->line_number; /* check for line continuation */ if (i > 0 && buf[i-1] == '\\') {
i--; continue;
} else { break;
}
}
buf[i] = c;
++i; if (i >= bufsize - 1) { return APR_ENOSPC;
}
}
buf[i] = '\0';
} return APR_SUCCESS;
}
staticint cfg_trim_line(char *buf)
{ char *start, *end; /* * Leading and trailing white space is eliminated completely
*/
start = buf; while (apr_isspace(*start))
++start; /* blast trailing whitespace */
end = &start[strlen(start)]; while (--end >= start && apr_isspace(*end))
*end = '\0'; /* Zap leading whitespace by shifting */ if (start != buf)
memmove(buf, start, end - start + 2); #ifdef DEBUG_CFG_LINES
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, APLOGNO(00555) "Read config: '%s'", buf); #endif return end - start + 1;
}
/* Read one line from open ap_configfile_t, strip LF, increase line number */ /* If custom handler does not define a getstr() function, read char by char */
AP_DECLARE(apr_status_t) ap_cfg_getline(char *buf, apr_size_t bufsize,
ap_configfile_t *cfp)
{
apr_status_t rc = ap_cfg_getline_core(buf, bufsize, 0, cfp); if (rc == APR_SUCCESS)
cfg_trim_line(buf); return rc;
}
for (;;) {
rc = ap_cfg_getline_core(vb->buf, vb->avail, vb->strlen, cfp); if (rc == APR_ENOSPC || rc == APR_SUCCESS)
vb->strlen += strlen(vb->buf + vb->strlen); if (rc != APR_ENOSPC) break; if (vb->avail >= max_len) return APR_ENOSPC;
new_len = vb->avail * 2; if (new_len > max_len)
new_len = max_len;
ap_varbuf_grow(vb, new_len);
--cfp->line_number;
} if (vb->strlen > max_len) return APR_ENOSPC; if (rc == APR_SUCCESS)
vb->strlen = cfg_trim_line(vb->buf); return rc;
}
/* Size an HTTP header field list item, as separated by a comma. * The return value is a pointer to the beginning of the non-empty list item * within the original string (or NULL if there is none) and the address * of field is shifted to the next non-comma, non-whitespace character. * len is the length of the item excluding any beginning whitespace.
*/
AP_DECLARE(constchar *) ap_size_list_item(constchar **field, int *len)
{ constunsignedchar *ptr = (constunsignedchar *)*field; constunsignedchar *token; int in_qpair, in_qstr, in_com;
/* Find first non-comma, non-whitespace byte */
while (*ptr == ',' || apr_isspace(*ptr))
++ptr;
token = ptr;
/* Find the end of this item, skipping over dead bits */
/* Retrieve an HTTP header field list item, as separated by a comma, * while stripping insignificant whitespace and lowercasing anything not in * a quoted string or comment. The return value is a new string containing * the converted list item (or NULL if none) and the address pointed to by * field is shifted to the next non-comma, non-whitespace.
*/
AP_DECLARE(char *) ap_get_list_item(apr_pool_t *p, constchar **field)
{ constchar *tok_start; constunsignedchar *ptr; unsignedchar *pos; char *token; int addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0, tok_len = 0;
/* Find the beginning and maximum length of the list item so that * we can allocate a buffer for the new string and reset the field.
*/ if ((tok_start = ap_size_list_item(field, &tok_len)) == NULL) { return NULL;
}
token = apr_palloc(p, tok_len + 1);
/* Scan the token again, but this time copy only the good bytes. * We skip extra whitespace and any whitespace around a '=', '/', * or ';' and lowercase normal characters not within a comment, * quoted-string or quoted-pair.
*/ for (ptr = (constunsignedchar *)tok_start, pos = (unsignedchar *)token;
*ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
++ptr) {
/* Find an item in canonical form (lowercase, no extra spaces) within * an HTTP field value list. Returns 1 if found, 0 if not found. * This would be much more efficient if we stored header fields as * an array of list items as they are received instead of a plain string.
*/ staticint find_list_item(apr_pool_t *p, constchar *line, constchar *tok, ap_etag_e type)
{ constunsignedchar *pos; constunsignedchar *ptr = (constunsignedchar *)line; int good = 0, addspace = 0, in_qpair = 0, in_qstr = 0, in_com = 0;
/* Find first non-comma, non-whitespace byte */ while (*ptr == ',' || apr_isspace(*ptr)) {
++ptr;
}
/* Account for strong or weak Etags, depending on our search */ if (type == AP_ETAG_STRONG && *ptr != '\"') { break;
} if (type == AP_ETAG_WEAK) { if (*ptr == 'W' && (*(ptr+1)) == '/' && (*(ptr+2)) == '\"') {
ptr += 2;
} elseif (*ptr != '\"') { break;
}
}
if (*ptr)
good = 1; /* until proven otherwise for this item */ else break; /* no items left and nothing good found */
/* We skip extra whitespace and any whitespace around a '=', '/', * or ';' and lowercase normal characters not within a comment, * quoted-string or quoted-pair.
*/ for (pos = (constunsignedchar *)tok;
*ptr && (in_qpair || in_qstr || in_com || *ptr != ',');
++ptr) {
if (in_qpair) {
in_qpair = 0; if (good)
good = (*pos++ == *ptr);
} else { switch (*ptr) { case'\\': in_qpair = 1; if (addspace == 1)
good = good && (*pos++ == ' ');
good = good && (*pos++ == *ptr);
addspace = 0; break; case'"' : if (!in_com)
in_qstr = !in_qstr; if (addspace == 1)
good = good && (*pos++ == ' ');
good = good && (*pos++ == *ptr);
addspace = 0; break; case'(' : if (!in_qstr)
++in_com; if (addspace == 1)
good = good && (*pos++ == ' ');
good = good && (*pos++ == *ptr);
addspace = 0; break; case')' : if (in_com)
--in_com;
good = good && (*pos++ == *ptr);
addspace = 0; break; case' ' : case'\t': if (addspace || !good) break; if (in_com || in_qstr)
good = (*pos++ == *ptr); else
addspace = 1; break; case'=' : case'/' : case';' : if (!(in_com || in_qstr))
addspace = -1;
good = good && (*pos++ == *ptr); break; default : if (!good) break; if (addspace == 1)
good = (*pos++ == ' '); if (in_com || in_qstr)
good = good && (*pos++ == *ptr); else
good = good
&& (apr_tolower(*pos++) == apr_tolower(*ptr));
addspace = 0; break;
}
}
} if (good && *pos)
good = 0; /* not good if only a prefix was matched */
} while (*ptr && !good);
return good;
}
/* Find an item in canonical form (lowercase, no extra spaces) within * an HTTP field value list. Returns 1 if found, 0 if not found. * This would be much more efficient if we stored header fields as * an array of list items as they are received instead of a plain string.
*/
AP_DECLARE(int) ap_find_list_item(apr_pool_t *p, constchar *line, constchar *tok)
{ return find_list_item(p, line, tok, AP_ETAG_NONE);
}
/* Find a strong Etag in canonical form (lowercase, no extra spaces) within * an HTTP field value list. Returns 1 if found, 0 if not found.
*/
AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, constchar *line, constchar *tok)
{ return find_list_item(p, line, tok, AP_ETAG_STRONG);
}
/* Find a weak ETag in canonical form (lowercase, no extra spaces) within * an HTTP field value list. Returns 1 if found, 0 if not found.
*/
AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, constchar *line, constchar *tok)
{ return find_list_item(p, line, tok, AP_ETAG_WEAK);
}
/* Grab a list of tokens of the format 1#token (from RFC7230) */
AP_DECLARE(constchar *) ap_parse_token_list_strict(apr_pool_t *p, constchar *str_in,
apr_array_header_t **tokens, int skip_invalid)
{ int in_leading_space = 1; int in_trailing_space = 0; int string_end = 0; constchar *tok_begin; constchar *cur;
if (!str_in) { return NULL;
}
tok_begin = cur = str_in;
while (!string_end) { constunsignedchar c = (unsignedchar)*cur;
if (!TEST_CHAR(c, T_HTTP_TOKEN_STOP)) { /* Non-separator character; we are finished with leading * whitespace. We must never have encountered any trailing
* whitespace before the delimiter (comma) */
in_leading_space = 0; if (in_trailing_space) { return"Encountered illegal whitespace in token";
}
} elseif (c == ' ' || c == '\t') { /* "Linear whitespace" only includes ASCII CRLF, space, and tab; * we can't get a CRLF since headers are split on them already,
* so only look for a space or a tab */ if (in_leading_space) { /* We're still in leading whitespace */
++tok_begin;
} else { /* We must be in trailing whitespace */
++in_trailing_space;
}
} elseif (c == ',' || c == '\0') { if (!in_leading_space) { /* If we're out of the leading space, we know we've read some
* characters of a token */ if (*tokens == NULL) {
*tokens = apr_array_make(p, 4, sizeof(char *));
}
APR_ARRAY_PUSH(*tokens, char *) =
apr_pstrmemdup((*tokens)->pool, tok_begin,
(cur - tok_begin) - in_trailing_space);
} /* We're allowed to have null elements, just don't add them to the
* array */
tok_begin = cur + 1;
in_leading_space = 1;
in_trailing_space = 0;
string_end = (c == '\0');
} else { /* Encountered illegal separator char */ if (skip_invalid) { /* Skip to the next separator */ constchar *temp;
temp = ap_strchr_c(cur, ','); if(!temp) {
temp = ap_strchr_c(cur, '\0');
}
/* Act like we haven't seen a token so we reset */
cur = temp - 1;
in_leading_space = 1;
in_trailing_space = 0;
} else { return apr_psprintf(p, "Encountered illegal separator " "'\\x%.2x'", (unsignedint)c);
}
}
++cur;
}
return NULL;
}
/* Scan a string for HTTP VCHAR/obs-text characters including HT and SP * (as used in header values, for example, in RFC 7230 section 3.2) * returning the pointer to the first non-HT ASCII ctrl character.
*/
AP_DECLARE(constchar *) ap_scan_http_field_content(constchar *ptr)
{ for ( ; !TEST_CHAR(*ptr, T_HTTP_CTRLS); ++ptr) ;
return ptr;
}
/* Scan a string for HTTP token characters, returning the pointer to * the first non-token character.
*/
AP_DECLARE(constchar *) ap_scan_http_token(constchar *ptr)
{ for ( ; !TEST_CHAR(*ptr, T_HTTP_TOKEN_STOP); ++ptr) ;
return ptr;
}
/* Scan a string for visible ASCII (0x21-0x7E) or obstext (0x80+) * and return a pointer to the first ctrl/space character encountered.
*/
AP_DECLARE(constchar *) ap_scan_vchar_obstext(constchar *ptr)
{ for ( ; TEST_CHAR(*ptr, T_VCHAR_OBSTEXT); ++ptr) ;
return ptr;
}
/* Retrieve a token, spacing over it and returning a pointer to * the first non-white byte afterwards. Note that these tokens * are delimited by semis and commas; and can also be delimited * by whitespace at the caller's option.
*/
cmd = apr_palloc(p, 2 * strlen(str) + 1); /* Be safe */
d = (unsignedchar *)cmd;
s = (constunsignedchar *)str; for (; *s; ++s) {
#ifdefined(OS2) || defined(WIN32) /* * Newlines to Win32/OS2 CreateProcess() are ill advised. * Convert them to spaces since they are effectively white * space to most applications
*/ if (*s == '\r' || *s == '\n') {
*d++ = ' '; continue;
} #endif
/* * Unescapes a URL, leaving reserved characters intact. * Returns 0 on success, non-zero on error * Failure is due to * bad % escape returns HTTP_BAD_REQUEST * * decoding %00 or a forbidden character returns HTTP_NOT_FOUND
*/
#ifdef NEW_APIS /* IFDEF these out until they've been thought through. * Just a germ of an API extension for now
*/
AP_DECLARE(int) ap_unescape_url_proxy(char *url)
{ /* leave RFC1738 reserved characters intact, * so proxied URLs * don't get mangled. Where does that leave encoded '&' ?
*/ return unescape_url(url, NULL, "/;?", 0);
}
AP_DECLARE(int) ap_unescape_url_reserved(char *url, constchar *reserved)
{ return unescape_url(url, NULL, reserved);
} #endif
/* c2x takes an unsigned, and expects the caller has guaranteed that * 0 <= what < 256... which usually means that you have to cast to * unsigned char first, because (unsigned)(char)(x) first goes through * signed extension to an int before the unsigned cast. * * The reason for this assumption is to assist gcc code generation -- * the unsigned char -> unsigned extension is already done earlier in * both uses of this code, so there's no need to waste time doing it * again.
*/ staticconstchar c2x_table[] = "0123456789abcdef";
/* * escape_path_segment() escapes a path segment, as defined in RFC 1808. This * routine is (should be) OS independent. * * os_escape_path() converts an OS path to a URL, in an OS dependent way. In all * cases if a ':' occurs before the first '/' in the URL, the URL should be * prefixed with "./" (or the ':' escaped). In the case of Unix, this means * leaving '/' alone, but otherwise doing what escape_path_segment() does. For * efficiency reasons, we don't use escape_path_segment(), which is provided for * reference. Again, RFC 1808 is where this stuff is defined. * * If partial is set, os_escape_path() assumes that the path will be appended to * something with a '/' in it (and thus does not prefix "./").
*/
/* Compute how many characters need to be escaped */
s = (constunsignedchar *)str; for (; *s; ++s) { if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
escapes++;
}
}
/* Compute the length of the input string, including NULL */
length = s - (constunsignedchar *)str + 1;
/* Fast path: nothing to escape */ if (escapes == 0) { return apr_pmemdup(p, str, length);
}
/* Each escaped character needs up to 3 extra bytes (0 --> \x00) */
ret = apr_palloc(p, length + 3 * escapes);
d = (unsignedchar *)ret;
s = (constunsignedchar *)str; for (; *s; ++s) { if (TEST_CHAR(*s, T_ESCAPE_LOGITEM)) {
*d++ = '\\'; switch(*s) { case'\b':
*d++ = 'b'; break; case'\n':
*d++ = 'n'; break; case'\r':
*d++ = 'r'; break; case'\t':
*d++ = 't'; break; case'\v':
*d++ = 'v'; break; case'\\': case'"':
*d++ = *s; break; default:
c2x(*s, 'x', d);
d += 3;
}
} else {
*d++ = *s;
}
}
*d = '\0';
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.