diff options
Diffstat (limited to 'gnulib-local/lib/libxml/uri.c')
-rw-r--r-- | gnulib-local/lib/libxml/uri.c | 2203 |
1 files changed, 1143 insertions, 1060 deletions
diff --git a/gnulib-local/lib/libxml/uri.c b/gnulib-local/lib/libxml/uri.c index 1e29223..ff47abb 100644 --- a/gnulib-local/lib/libxml/uri.c +++ b/gnulib-local/lib/libxml/uri.c @@ -1,7 +1,7 @@ /** - * uri.c: set of generic URI related routines + * uri.c: set of generic URI related routines * - * Reference: RFCs 2396, 2732 and 2373 + * Reference: RFCs 3986, 2732 and 2373 * * See Copyright for the status of this software. * @@ -18,14 +18,41 @@ #include <libxml/globals.h> #include <libxml/xmlerror.h> -/************************************************************************ - * * - * Macros to differentiate various character type * - * directly extracted from RFC 2396 * - * * - ************************************************************************/ +/** + * MAX_URI_LENGTH: + * + * The definition of the URI regexp in the above RFC has no size limit + * In practice they are usually relativey short except for the + * data URI scheme as defined in RFC 2397. Even for data URI the usual + * maximum size before hitting random practical limits is around 64 KB + * and 4KB is usually a maximum admitted limit for proper operations. + * The value below is more a security limit than anything else and + * really should never be hit by 'normal' operations + * Set to 1 MByte in 2012, this is only enforced on output + */ +#define MAX_URI_LENGTH 1024 * 1024 + +static void +xmlURIErrMemory(const char *extra) +{ + if (extra) + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_URI, + XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, + extra, NULL, NULL, 0, 0, + "Memory allocation failed : %s\n", extra); + else + __xmlRaiseError(NULL, NULL, NULL, + NULL, NULL, XML_FROM_URI, + XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, + NULL, NULL, NULL, 0, 0, + "Memory allocation failed\n"); +} + +static void xmlCleanURI(xmlURIPtr uri); /* + * Old rule from 2396 used in legacy handling code * alpha = lowalpha | upalpha */ #define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x)) @@ -61,31 +88,30 @@ #define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) /* - * hex = digit | "A" | "B" | "C" | "D" | "E" | "F" | - * "a" | "b" | "c" | "d" | "e" | "f" - */ - -#define IS_HEX(x) ((IS_DIGIT(x)) || (((x) >= 'a') && ((x) <= 'f')) || \ - (((x) >= 'A') && ((x) <= 'F'))) - -/* * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" */ -#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ - ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ +#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') || \ + ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') || \ ((x) == '(') || ((x) == ')')) +/* + * unwise = "{" | "}" | "|" | "\" | "^" | "`" + */ +#define IS_UNWISE(p) \ + (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ + ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ + ((*(p) == ']')) || ((*(p) == '`'))) /* * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | - * "[" | "]" + * "[" | "]" */ -#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ - ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ - ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ - ((x) == ']')) +#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ + ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ + ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ + ((x) == ']')) /* * unreserved = alphanum | mark @@ -94,98 +120,879 @@ #define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) /* - * escaped = "%" hex hex + * Skip to next pointer char, handle escaped sequences */ -#define IS_ESCAPED(p) ((*(p) == '%') && (IS_HEX((p)[1])) && \ - (IS_HEX((p)[2]))) +#define NEXT(p) ((*p == '%')? p += 3 : p++) /* - * uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" | - * "&" | "=" | "+" | "$" | "," + * Productions from the spec. + * + * authority = server | reg_name + * reg_name = 1*( unreserved | escaped | "$" | "," | + * ";" | ":" | "@" | "&" | "=" | "+" ) + * + * path = [ abs_path | opaque_part ] */ -#define IS_URIC_NO_SLASH(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) ||\ - ((*(p) == ';')) || ((*(p) == '?')) || ((*(p) == ':')) ||\ - ((*(p) == '@')) || ((*(p) == '&')) || ((*(p) == '=')) ||\ - ((*(p) == '+')) || ((*(p) == '$')) || ((*(p) == ','))) + +#define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n)) + +/************************************************************************ + * * + * RFC 3986 parser * + * * + ************************************************************************/ + +#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9')) +#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) || \ + ((*(p) >= 'A') && (*(p) <= 'Z'))) +#define ISA_HEXDIG(p) \ + (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) || \ + ((*(p) >= 'A') && (*(p) <= 'F'))) /* - * pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+" | "$" | "," + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + * / "*" / "+" / "," / ";" / "=" */ -#define IS_PCHAR(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) || \ - ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||\ - ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||\ - ((*(p) == ','))) +#define ISA_SUB_DELIM(p) \ + (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) || \ + ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) || \ + ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) || \ + ((*(p) == '=')) || ((*(p) == '\''))) /* - * rel_segment = 1*( unreserved | escaped | - * ";" | "@" | "&" | "=" | "+" | "$" | "," ) + * gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" */ +#define ISA_GEN_DELIM(p) \ + (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) || \ + ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) || \ + ((*(p) == '@'))) -#define IS_SEGMENT(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) || \ - ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || \ - ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || \ - ((*(p) == ','))) +/* + * reserved = gen-delims / sub-delims + */ +#define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p))) /* - * scheme = alpha *( alpha | digit | "+" | "-" | "." ) + * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */ +#define ISA_UNRESERVED(p) \ + ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ + ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) -#define IS_SCHEME(x) ((IS_ALPHA(x)) || (IS_DIGIT(x)) || \ - ((x) == '+') || ((x) == '-') || ((x) == '.')) +/* + * pct-encoded = "%" HEXDIG HEXDIG + */ +#define ISA_PCT_ENCODED(p) \ + ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2))) /* - * reg_name = 1*( unreserved | escaped | "$" | "," | - * ";" | ":" | "@" | "&" | "=" | "+" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" */ +#define ISA_PCHAR(p) \ + (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ + ((*(p) == ':')) || ((*(p) == '@'))) -#define IS_REG_NAME(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) || \ - ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) || \ - ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) || \ - ((*(p) == '=')) || ((*(p) == '+'))) +/** + * xmlParse3986Scheme: + * @uri: pointer to an URI structure + * @str: pointer to the string to analyze + * + * Parse an URI scheme + * + * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + * + * Returns 0 or the error code + */ +static int +xmlParse3986Scheme(xmlURIPtr uri, const char **str) { + const char *cur; -/* - * userinfo = *( unreserved | escaped | ";" | ":" | "&" | "=" | - * "+" | "$" | "," ) + if (str == NULL) + return(-1); + + cur = *str; + if (!ISA_ALPHA(cur)) + return(2); + cur++; + while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || + (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++; + if (uri != NULL) { + if (uri->scheme != NULL) xmlFree(uri->scheme); + uri->scheme = STRNDUP(*str, cur - *str); + } + *str = cur; + return(0); +} + +/** + * xmlParse3986Fragment: + * @uri: pointer to an URI structure + * @str: pointer to the string to analyze + * + * Parse the query part of an URI + * + * fragment = *( pchar / "/" / "?" ) + * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']' + * in the fragment identifier but this is used very broadly for + * xpointer scheme selection, so we are allowing it here to not break + * for example all the DocBook processing chains. + * + * Returns 0 or the error code */ -#define IS_USERINFO(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) || \ - ((*(p) == ';')) || ((*(p) == ':')) || ((*(p) == '&')) || \ - ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) || \ - ((*(p) == ','))) +static int +xmlParse3986Fragment(xmlURIPtr uri, const char **str) +{ + const char *cur; -/* - * uric = reserved | unreserved | escaped + if (str == NULL) + return (-1); + + cur = *str; + + while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || + (*cur == '[') || (*cur == ']') || + ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) + NEXT(cur); + if (uri != NULL) { + if (uri->fragment != NULL) + xmlFree(uri->fragment); + if (uri->cleanup & 2) + uri->fragment = STRNDUP(*str, cur - *str); + else + uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL); + } + *str = cur; + return (0); +} + +/** + * xmlParse3986Query: + * @uri: pointer to an URI structure + * @str: pointer to the string to analyze + * + * Parse the query part of an URI + * + * query = *uric + * + * Returns 0 or the error code */ +static int +xmlParse3986Query(xmlURIPtr uri, const char **str) +{ + const char *cur; -#define IS_URIC(p) ((IS_UNRESERVED(*(p))) || (IS_ESCAPED(p)) || \ - (IS_RESERVED(*(p)))) + if (str == NULL) + return (-1); -/* -* unwise = "{" | "}" | "|" | "\" | "^" | "`" -*/ + cur = *str; -#define IS_UNWISE(p) \ - (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ - ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ - ((*(p) == ']')) || ((*(p) == '`'))) + while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || + ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) + NEXT(cur); + if (uri != NULL) { + if (uri->query != NULL) + xmlFree(uri->query); + if (uri->cleanup & 2) + uri->query = STRNDUP(*str, cur - *str); + else + uri->query = xmlURIUnescapeString(*str, cur - *str, NULL); -/* - * Skip to next pointer char, handle escaped sequences + /* Save the raw bytes of the query as well. + * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114 + */ + if (uri->query_raw != NULL) + xmlFree (uri->query_raw); + uri->query_raw = STRNDUP (*str, cur - *str); + } + *str = cur; + return (0); +} + +/** + * xmlParse3986Port: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse a port part and fills in the appropriate fields + * of the @uri structure + * + * port = *DIGIT + * + * Returns 0 or the error code */ +static int +xmlParse3986Port(xmlURIPtr uri, const char **str) +{ + const char *cur = *str; -#define NEXT(p) ((*p == '%')? p += 3 : p++) + if (ISA_DIGIT(cur)) { + if (uri != NULL) + uri->port = 0; + while (ISA_DIGIT(cur)) { + if (uri != NULL) + uri->port = uri->port * 10 + (*cur - '0'); + cur++; + } + *str = cur; + return(0); + } + return(1); +} -/* - * Productions from the spec. +/** + * xmlParse3986Userinfo: + * @uri: pointer to an URI structure + * @str: the string to analyze * - * authority = server | reg_name - * reg_name = 1*( unreserved | escaped | "$" | "," | - * ";" | ":" | "@" | "&" | "=" | "+" ) + * Parse an user informations part and fills in the appropriate fields + * of the @uri structure * - * path = [ abs_path | opaque_part ] + * userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + * + * Returns 0 or the error code */ +static int +xmlParse3986Userinfo(xmlURIPtr uri, const char **str) +{ + const char *cur; -#define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n)) + cur = *str; + while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || + ISA_SUB_DELIM(cur) || (*cur == ':')) + NEXT(cur); + if (*cur == '@') { + if (uri != NULL) { + if (uri->user != NULL) xmlFree(uri->user); + if (uri->cleanup & 2) + uri->user = STRNDUP(*str, cur - *str); + else + uri->user = xmlURIUnescapeString(*str, cur - *str, NULL); + } + *str = cur; + return(0); + } + return(1); +} + +/** + * xmlParse3986DecOctet: + * @str: the string to analyze + * + * dec-octet = DIGIT ; 0-9 + * / %x31-39 DIGIT ; 10-99 + * / "1" 2DIGIT ; 100-199 + * / "2" %x30-34 DIGIT ; 200-249 + * / "25" %x30-35 ; 250-255 + * + * Skip a dec-octet. + * + * Returns 0 if found and skipped, 1 otherwise + */ +static int +xmlParse3986DecOctet(const char **str) { + const char *cur = *str; + + if (!(ISA_DIGIT(cur))) + return(1); + if (!ISA_DIGIT(cur+1)) + cur++; + else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur+2))) + cur += 2; + else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2))) + cur += 3; + else if ((*cur == '2') && (*(cur + 1) >= '0') && + (*(cur + 1) <= '4') && (ISA_DIGIT(cur + 2))) + cur += 3; + else if ((*cur == '2') && (*(cur + 1) == '5') && + (*(cur + 2) >= '0') && (*(cur + 1) <= '5')) + cur += 3; + else + return(1); + *str = cur; + return(0); +} +/** + * xmlParse3986Host: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an host part and fills in the appropriate fields + * of the @uri structure + * + * host = IP-literal / IPv4address / reg-name + * IP-literal = "[" ( IPv6address / IPvFuture ) "]" + * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet + * reg-name = *( unreserved / pct-encoded / sub-delims ) + * + * Returns 0 or the error code + */ +static int +xmlParse3986Host(xmlURIPtr uri, const char **str) +{ + const char *cur = *str; + const char *host; + + host = cur; + /* + * IPv6 and future adressing scheme are enclosed between brackets + */ + if (*cur == '[') { + cur++; + while ((*cur != ']') && (*cur != 0)) + cur++; + if (*cur != ']') + return(1); + cur++; + goto found; + } + /* + * try to parse an IPv4 + */ + if (ISA_DIGIT(cur)) { + if (xmlParse3986DecOctet(&cur) != 0) + goto not_ipv4; + if (*cur != '.') + goto not_ipv4; + cur++; + if (xmlParse3986DecOctet(&cur) != 0) + goto not_ipv4; + if (*cur != '.') + goto not_ipv4; + if (xmlParse3986DecOctet(&cur) != 0) + goto not_ipv4; + if (*cur != '.') + goto not_ipv4; + if (xmlParse3986DecOctet(&cur) != 0) + goto not_ipv4; + goto found; +not_ipv4: + cur = *str; + } + /* + * then this should be a hostname which can be empty + */ + while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) + NEXT(cur); +found: + if (uri != NULL) { + if (uri->authority != NULL) xmlFree(uri->authority); + uri->authority = NULL; + if (uri->server != NULL) xmlFree(uri->server); + if (cur != host) { + if (uri->cleanup & 2) + uri->server = STRNDUP(host, cur - host); + else + uri->server = xmlURIUnescapeString(host, cur - host, NULL); + } else + uri->server = NULL; + } + *str = cur; + return(0); +} + +/** + * xmlParse3986Authority: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an authority part and fills in the appropriate fields + * of the @uri structure + * + * authority = [ userinfo "@" ] host [ ":" port ] + * + * Returns 0 or the error code + */ +static int +xmlParse3986Authority(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + /* + * try to parse an userinfo and check for the trailing @ + */ + ret = xmlParse3986Userinfo(uri, &cur); + if ((ret != 0) || (*cur != '@')) + cur = *str; + else + cur++; + ret = xmlParse3986Host(uri, &cur); + if (ret != 0) return(ret); + if (*cur == ':') { + cur++; + ret = xmlParse3986Port(uri, &cur); + if (ret != 0) return(ret); + } + *str = cur; + return(0); +} + +/** + * xmlParse3986Segment: + * @str: the string to analyze + * @forbid: an optional forbidden character + * @empty: allow an empty segment + * + * Parse a segment and fills in the appropriate fields + * of the @uri structure + * + * segment = *pchar + * segment-nz = 1*pchar + * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + * ; non-zero-length segment without any colon ":" + * + * Returns 0 or the error code + */ +static int +xmlParse3986Segment(const char **str, char forbid, int empty) +{ + const char *cur; + + cur = *str; + if (!ISA_PCHAR(cur)) { + if (empty) + return(0); + return(1); + } + while (ISA_PCHAR(cur) && (*cur != forbid)) + NEXT(cur); + *str = cur; + return (0); +} + +/** + * xmlParse3986PathAbEmpty: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an path absolute or empty and fills in the appropriate fields + * of the @uri structure + * + * path-abempty = *( "/" segment ) + * + * Returns 0 or the error code + */ +static int +xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + + while (*cur == '/') { + cur++; + ret = xmlParse3986Segment(&cur, 0, 1); + if (ret != 0) return(ret); + } + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + if (*str != cur) { + if (uri->cleanup & 2) + uri->path = STRNDUP(*str, cur - *str); + else + uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + } else { + uri->path = NULL; + } + } + *str = cur; + return (0); +} + +/** + * xmlParse3986PathAbsolute: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an path absolute and fills in the appropriate fields + * of the @uri structure + * + * path-absolute = "/" [ segment-nz *( "/" segment ) ] + * + * Returns 0 or the error code + */ +static int +xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + + if (*cur != '/') + return(1); + cur++; + ret = xmlParse3986Segment(&cur, 0, 0); + if (ret == 0) { + while (*cur == '/') { + cur++; + ret = xmlParse3986Segment(&cur, 0, 1); + if (ret != 0) return(ret); + } + } + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + if (cur != *str) { + if (uri->cleanup & 2) + uri->path = STRNDUP(*str, cur - *str); + else + uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + } else { + uri->path = NULL; + } + } + *str = cur; + return (0); +} + +/** + * xmlParse3986PathRootless: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an path without root and fills in the appropriate fields + * of the @uri structure + * + * path-rootless = segment-nz *( "/" segment ) + * + * Returns 0 or the error code + */ +static int +xmlParse3986PathRootless(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + + ret = xmlParse3986Segment(&cur, 0, 0); + if (ret != 0) return(ret); + while (*cur == '/') { + cur++; + ret = xmlParse3986Segment(&cur, 0, 1); + if (ret != 0) return(ret); + } + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + if (cur != *str) { + if (uri->cleanup & 2) + uri->path = STRNDUP(*str, cur - *str); + else + uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + } else { + uri->path = NULL; + } + } + *str = cur; + return (0); +} + +/** + * xmlParse3986PathNoScheme: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an path which is not a scheme and fills in the appropriate fields + * of the @uri structure + * + * path-noscheme = segment-nz-nc *( "/" segment ) + * + * Returns 0 or the error code + */ +static int +xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + + ret = xmlParse3986Segment(&cur, ':', 0); + if (ret != 0) return(ret); + while (*cur == '/') { + cur++; + ret = xmlParse3986Segment(&cur, 0, 1); + if (ret != 0) return(ret); + } + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + if (cur != *str) { + if (uri->cleanup & 2) + uri->path = STRNDUP(*str, cur - *str); + else + uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + } else { + uri->path = NULL; + } + } + *str = cur; + return (0); +} + +/** + * xmlParse3986HierPart: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an hierarchical part and fills in the appropriate fields + * of the @uri structure + * + * hier-part = "//" authority path-abempty + * / path-absolute + * / path-rootless + * / path-empty + * + * Returns 0 or the error code + */ +static int +xmlParse3986HierPart(xmlURIPtr uri, const char **str) +{ + const char *cur; + int ret; + + cur = *str; + + if ((*cur == '/') && (*(cur + 1) == '/')) { + cur += 2; + ret = xmlParse3986Authority(uri, &cur); + if (ret != 0) return(ret); + if (uri->server == NULL) + uri->port = -1; + ret = xmlParse3986PathAbEmpty(uri, &cur); + if (ret != 0) return(ret); + *str = cur; + return(0); + } else if (*cur == '/') { + ret = xmlParse3986PathAbsolute(uri, &cur); + if (ret != 0) return(ret); + } else if (ISA_PCHAR(cur)) { + ret = xmlParse3986PathRootless(uri, &cur); + if (ret != 0) return(ret); + } else { + /* path-empty is effectively empty */ + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + uri->path = NULL; + } + } + *str = cur; + return (0); +} + +/** + * xmlParse3986RelativeRef: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an URI string and fills in the appropriate fields + * of the @uri structure + * + * relative-ref = relative-part [ "?" query ] [ "#" fragment ] + * relative-part = "//" authority path-abempty + * / path-absolute + * / path-noscheme + * / path-empty + * + * Returns 0 or the error code + */ +static int +xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) { + int ret; + + if ((*str == '/') && (*(str + 1) == '/')) { + str += 2; + ret = xmlParse3986Authority(uri, &str); + if (ret != 0) return(ret); + ret = xmlParse3986PathAbEmpty(uri, &str); + if (ret != 0) return(ret); + } else if (*str == '/') { + ret = xmlParse3986PathAbsolute(uri, &str); + if (ret != 0) return(ret); + } else if (ISA_PCHAR(str)) { + ret = xmlParse3986PathNoScheme(uri, &str); + if (ret != 0) return(ret); + } else { + /* path-empty is effectively empty */ + if (uri != NULL) { + if (uri->path != NULL) xmlFree(uri->path); + uri->path = NULL; + } + } + + if (*str == '?') { + str++; + ret = xmlParse3986Query(uri, &str); + if (ret != 0) return(ret); + } + if (*str == '#') { + str++; + ret = xmlParse3986Fragment(uri, &str); + if (ret != 0) return(ret); + } + if (*str != 0) { + xmlCleanURI(uri); + return(1); + } + return(0); +} + + +/** + * xmlParse3986URI: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an URI string and fills in the appropriate fields + * of the @uri structure + * + * scheme ":" hier-part [ "?" query ] [ "#" fragment ] + * + * Returns 0 or the error code + */ +static int +xmlParse3986URI(xmlURIPtr uri, const char *str) { + int ret; + + ret = xmlParse3986Scheme(uri, &str); + if (ret != 0) return(ret); + if (*str != ':') { + return(1); + } + str++; + ret = xmlParse3986HierPart(uri, &str); + if (ret != 0) return(ret); + if (*str == '?') { + str++; + ret = xmlParse3986Query(uri, &str); + if (ret != 0) return(ret); + } + if (*str == '#') { + str++; + ret = xmlParse3986Fragment(uri, &str); + if (ret != 0) return(ret); + } + if (*str != 0) { + xmlCleanURI(uri); + return(1); + } + return(0); +} + +/** + * xmlParse3986URIReference: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an URI reference string and fills in the appropriate fields + * of the @uri structure + * + * URI-reference = URI / relative-ref + * + * Returns 0 or the error code + */ +static int +xmlParse3986URIReference(xmlURIPtr uri, const char *str) { + int ret; + + if (str == NULL) + return(-1); + xmlCleanURI(uri); + + /* + * Try first to parse absolute refs, then fallback to relative if + * it fails. + */ + ret = xmlParse3986URI(uri, str); + if (ret != 0) { + xmlCleanURI(uri); + ret = xmlParse3986RelativeRef(uri, str); + if (ret != 0) { + xmlCleanURI(uri); + return(ret); + } + } + return(0); +} + +/** + * xmlParseURI: + * @str: the URI string to analyze + * + * Parse an URI based on RFC 3986 + * + * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] + * + * Returns a newly built xmlURIPtr or NULL in case of error + */ +xmlURIPtr +xmlParseURI(const char *str) { + xmlURIPtr uri; + int ret; + + if (str == NULL) + return(NULL); + uri = xmlCreateURI(); + if (uri != NULL) { + ret = xmlParse3986URIReference(uri, str); + if (ret) { + xmlFreeURI(uri); + return(NULL); + } + } + return(uri); +} + +/** + * xmlParseURIReference: + * @uri: pointer to an URI structure + * @str: the string to analyze + * + * Parse an URI reference string based on RFC 3986 and fills in the + * appropriate fields of the @uri structure + * + * URI-reference = URI / relative-ref + * + * Returns 0 or the error code + */ +int +xmlParseURIReference(xmlURIPtr uri, const char *str) { + return(xmlParse3986URIReference(uri, str)); +} + +/** + * xmlParseURIRaw: + * @str: the URI string to analyze + * @raw: if 1 unescaping of URI pieces are disabled + * + * Parse an URI but allows to keep intact the original fragments. + * + * URI-reference = URI / relative-ref + * + * Returns a newly built xmlURIPtr or NULL in case of error + */ +xmlURIPtr +xmlParseURIRaw(const char *str, int raw) { + xmlURIPtr uri; + int ret; + + if (str == NULL) + return(NULL); + uri = xmlCreateURI(); + if (uri != NULL) { + if (raw) { + uri->cleanup |= 2; + } + ret = xmlParseURIReference(uri, str); + if (ret) { + xmlFreeURI(uri); + return(NULL); + } + } + return(uri); +} /************************************************************************ * * @@ -206,8 +1013,7 @@ xmlCreateURI(void) { ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI)); if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlCreateURI: out of memory\n"); + xmlURIErrMemory("creating URI structure\n"); return(NULL); } memset(ret, 0, sizeof(xmlURI)); @@ -215,6 +1021,31 @@ xmlCreateURI(void) { } /** + * xmlSaveUriRealloc: + * + * Function to handle properly a reallocation when saving an URI + * Also imposes some limit on the length of an URI string output + */ +static xmlChar * +xmlSaveUriRealloc(xmlChar *ret, int *max) { + xmlChar *temp; + int tmp; + + if (*max > MAX_URI_LENGTH) { + xmlURIErrMemory("reaching arbitrary MAX_URI_LENGTH limit\n"); + return(NULL); + } + tmp = *max * 2; + temp = (xmlChar *) xmlRealloc(ret, (tmp + 1)); + if (temp == NULL) { + xmlURIErrMemory("saving URI\n"); + return(NULL); + } + *max = tmp; + return(temp); +} + +/** * xmlSaveUri: * @uri: pointer to an xmlURI * @@ -225,6 +1056,7 @@ xmlCreateURI(void) { xmlChar * xmlSaveUri(xmlURIPtr uri) { xmlChar *ret = NULL; + xmlChar *temp; const char *p; int len; int max; @@ -235,8 +1067,7 @@ xmlSaveUri(xmlURIPtr uri) { max = 80; ret = (xmlChar *) xmlMallocAtomic((max + 1) * sizeof(xmlChar)); if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); + xmlURIErrMemory("saving URI\n"); return(NULL); } len = 0; @@ -245,24 +1076,16 @@ xmlSaveUri(xmlURIPtr uri) { p = uri->scheme; while (*p != 0) { if (len >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = *p++; } if (len >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = ':'; } @@ -270,13 +1093,9 @@ xmlSaveUri(xmlURIPtr uri) { p = uri->opaque; while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p))) ret[len++] = *p++; @@ -289,15 +1108,11 @@ xmlSaveUri(xmlURIPtr uri) { } } } else { - if (uri->server != NULL) { + if ((uri->server != NULL) || (uri->port == -1)) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = '/'; ret[len++] = '/'; @@ -305,14 +1120,9 @@ xmlSaveUri(xmlURIPtr uri) { p = uri->user; while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } if ((IS_UNRESERVED(*(p))) || ((*(p) == ';')) || ((*(p) == ':')) || @@ -329,68 +1139,45 @@ xmlSaveUri(xmlURIPtr uri) { } } if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = '@'; } - p = uri->server; - while (*p != 0) { - if (len >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); + if (uri->server != NULL) { + p = uri->server; + while (*p != 0) { + if (len >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } + ret[len++] = *p++; } - ret[len++] = *p++; - } - if (uri->port > 0) { - if (len + 10 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); + if (uri->port > 0) { + if (len + 10 >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } + len += snprintf((char *) &ret[len], max - len, ":%d", uri->port); } - len += snprintf((char *) &ret[len], max - len, ":%d", uri->port); } } else if (uri->authority != NULL) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = '/'; ret[len++] = '/'; p = uri->authority; while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } if ((IS_UNRESERVED(*(p))) || ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) || @@ -407,30 +1194,37 @@ xmlSaveUri(xmlURIPtr uri) { } } else if (uri->scheme != NULL) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } - ret[len++] = '/'; - ret[len++] = '/'; } if (uri->path != NULL) { p = uri->path; + /* + * the colon in file:///d: should not be escaped or + * Windows accesses fail later. + */ + if ((uri->scheme != NULL) && + (p[0] == '/') && + (((p[1] >= 'a') && (p[1] <= 'z')) || + ((p[1] >= 'A') && (p[1] <= 'Z'))) && + (p[2] == ':') && + (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) { + if (len + 3 >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; + } + ret[len++] = *p++; + ret[len++] = *p++; + ret[len++] = *p++; + } while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) || ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) || @@ -446,31 +1240,37 @@ xmlSaveUri(xmlURIPtr uri) { } } } - if (uri->query != NULL) { - if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); + if (uri->query_raw != NULL) { + if (len + 1 >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; + } + ret[len++] = '?'; + p = uri->query_raw; + while (*p != 0) { + if (len + 1 >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } + ret[len++] = *p++; + } + } else if (uri->query != NULL) { + if (len + 3 >= max) { + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = '?'; p = uri->query; while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } - if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) + if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) ret[len++] = *p++; else { int val = *(unsigned char *)p++; @@ -484,29 +1284,19 @@ xmlSaveUri(xmlURIPtr uri) { } if (uri->fragment != NULL) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } ret[len++] = '#'; p = uri->fragment; while (*p != 0) { if (len + 3 >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, - (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } - if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) + if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p)))) ret[len++] = *p++; else { int val = *(unsigned char *)p++; @@ -518,16 +1308,16 @@ xmlSaveUri(xmlURIPtr uri) { } } if (len >= max) { - max *= 2; - ret = (xmlChar *) xmlRealloc(ret, (max + 1) * sizeof(xmlChar)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlSaveUri: out of memory\n"); - return(NULL); - } + temp = xmlSaveUriRealloc(ret, &max); + if (temp == NULL) goto mem_error; + ret = temp; } - ret[len++] = 0; + ret[len] = 0; return(ret); + +mem_error: + xmlFree(ret); + return(NULL); } /** @@ -574,6 +1364,8 @@ xmlCleanURI(xmlURIPtr uri) { uri->authority = NULL; if (uri->query != NULL) xmlFree(uri->query); uri->query = NULL; + if (uri->query_raw != NULL) xmlFree(uri->query_raw); + uri->query_raw = NULL; } /** @@ -594,6 +1386,7 @@ xmlFreeURI(xmlURIPtr uri) { if (uri->opaque != NULL) xmlFree(uri->opaque); if (uri->authority != NULL) xmlFree(uri->authority); if (uri->query != NULL) xmlFree(uri->query); + if (uri->query_raw != NULL) xmlFree(uri->query_raw); xmlFree(uri); } @@ -738,10 +1531,11 @@ xmlNormalizeURIPath(char *path) { break; } /* Valgrind complained, strcpy(cur, segp + 3); */ - /* string will overlap, do not use strcpy */ - tmp = cur; - segp += 3; - while ((*tmp++ = *segp++) != 0); + /* string will overlap, do not use strcpy */ + tmp = cur; + segp += 3; + while ((*tmp++ = *segp++) != 0) + ; /* If there are no previous segments, then keep going from here. */ segp = cur; @@ -805,10 +1599,13 @@ static int is_hex(char c) { * @len: the length in bytes to unescape (or <= 0 to indicate full string) * @target: optional destination buffer * - * Unescaping routine, does not do validity checks ! - * Output is direct unsigned char translation of %XX values (no encoding) + * Unescaping routine, but does not check that the string is an URI. The + * output is a direct unsigned char translation of %XX values (no encoding) + * Note that the length of the result can only be smaller or same size as + * the input string. * - * Returns an copy of the string, but unescaped + * Returns a copy of the string, but unescaped, will return NULL only in case + * of error */ char * xmlURIUnescapeString(const char *str, int len, char *target) { @@ -823,8 +1620,7 @@ xmlURIUnescapeString(const char *str, int len, char *target) { if (target == NULL) { ret = (char *) xmlMallocAtomic(len + 1); if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlURIUnescapeString: out of memory\n"); + xmlURIErrMemory("unescaping URI value\n"); return(NULL); } } else @@ -834,14 +1630,14 @@ xmlURIUnescapeString(const char *str, int len, char *target) { while(len > 0) { if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) { in++; - if ((*in >= '0') && (*in <= '9')) + if ((*in >= '0') && (*in <= '9')) *out = (*in - '0'); else if ((*in >= 'a') && (*in <= 'f')) *out = (*in - 'a') + 10; else if ((*in >= 'A') && (*in <= 'F')) *out = (*in - 'A') + 10; in++; - if ((*in >= '0') && (*in <= '9')) + if ((*in >= '0') && (*in <= '9')) *out = *out * 16 + (*in - '0'); else if ((*in >= 'a') && (*in <= 'f')) *out = *out * 16 + (*in - 'a') + 10; @@ -872,9 +1668,9 @@ xmlURIUnescapeString(const char *str, int len, char *target) { xmlChar * xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) { xmlChar *ret, ch; + xmlChar *temp; const xmlChar *in; - - unsigned int len, out; + int len, out; if (str == NULL) return(NULL); @@ -886,21 +1682,20 @@ xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) { len += 20; ret = (xmlChar *) xmlMallocAtomic(len); if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlURIEscapeStr: out of memory\n"); + xmlURIErrMemory("escaping URI value\n"); return(NULL); } in = (const xmlChar *) str; out = 0; while(*in != 0) { if (len - out <= 3) { - len += 20; - ret = (xmlChar *) xmlRealloc(ret, len); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlURIEscapeStr: out of memory\n"); + temp = xmlSaveUriRealloc(ret, &len); + if (temp == NULL) { + xmlURIErrMemory("escaping URI value\n"); + xmlFree(ret); return(NULL); } + ret = temp; } ch = *in; @@ -951,9 +1746,9 @@ xmlURIEscape(const xmlChar * str) int ret2; #define NULLCHK(p) if(!p) { \ - xmlGenericError(xmlGenericErrorContext, \ - "xmlURIEscape: out of memory\n"); \ - return NULL; } + xmlURIErrMemory("escaping URI value\n"); \ + xmlFreeURI(uri); \ + return NULL; } \ if (str == NULL) return (NULL); @@ -996,7 +1791,7 @@ xmlURIEscape(const xmlChar * str) if (uri->user) { segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,"); NULLCHK(segment) - ret = xmlStrcat(ret,BAD_CAST "//"); + ret = xmlStrcat(ret,BAD_CAST "//"); ret = xmlStrcat(ret, segment); ret = xmlStrcat(ret, BAD_CAST "@"); xmlFree(segment); @@ -1006,7 +1801,7 @@ xmlURIEscape(const xmlChar * str) segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@"); NULLCHK(segment) if (uri->user == NULL) - ret = xmlStrcat(ret, BAD_CAST "//"); + ret = xmlStrcat(ret, BAD_CAST "//"); ret = xmlStrcat(ret, segment); xmlFree(segment); } @@ -1027,7 +1822,11 @@ xmlURIEscape(const xmlChar * str) xmlFree(segment); } - if (uri->query) { + if (uri->query_raw) { + ret = xmlStrcat(ret, BAD_CAST "?"); + ret = xmlStrcat(ret, BAD_CAST uri->query_raw); + } + else if (uri->query) { segment = xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$"); NULLCHK(segment) @@ -1059,780 +1858,6 @@ xmlURIEscape(const xmlChar * str) /************************************************************************ * * - * Escaped URI parsing * - * * - ************************************************************************/ - -/** - * xmlParseURIFragment: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI fragment string and fills in the appropriate fields - * of the @uri structure. - * - * fragment = *uric - * - * Returns 0 or the error code - */ -static int -xmlParseURIFragment(xmlURIPtr uri, const char **str) -{ - const char *cur; - - if (str == NULL) - return (-1); - - cur = *str; - - while (IS_URIC(cur) || IS_UNWISE(cur)) - NEXT(cur); - if (uri != NULL) { - if (uri->fragment != NULL) - xmlFree(uri->fragment); - if (uri->cleanup & 2) - uri->fragment = STRNDUP(*str, cur - *str); - else - uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL); - } - *str = cur; - return (0); -} - -/** - * xmlParseURIQuery: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the query part of an URI - * - * query = *uric - * - * Returns 0 or the error code - */ -static int -xmlParseURIQuery(xmlURIPtr uri, const char **str) -{ - const char *cur; - - if (str == NULL) - return (-1); - - cur = *str; - - while ((IS_URIC(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) - NEXT(cur); - if (uri != NULL) { - if (uri->query != NULL) - xmlFree(uri->query); - if (uri->cleanup & 2) - uri->query = STRNDUP(*str, cur - *str); - else - uri->query = xmlURIUnescapeString(*str, cur - *str, NULL); - } - *str = cur; - return (0); -} - -/** - * xmlParseURIScheme: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI scheme - * - * scheme = alpha *( alpha | digit | "+" | "-" | "." ) - * - * Returns 0 or the error code - */ -static int -xmlParseURIScheme(xmlURIPtr uri, const char **str) { - const char *cur; - - if (str == NULL) - return(-1); - - cur = *str; - if (!IS_ALPHA(*cur)) - return(2); - cur++; - while (IS_SCHEME(*cur)) cur++; - if (uri != NULL) { - if (uri->scheme != NULL) xmlFree(uri->scheme); - uri->scheme = STRNDUP(*str, cur - *str); - } - *str = cur; - return(0); -} - -/** - * xmlParseURIOpaquePart: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI opaque part - * - * opaque_part = uric_no_slash *uric - * - * Returns 0 or the error code - */ -static int -xmlParseURIOpaquePart(xmlURIPtr uri, const char **str) -{ - const char *cur; - - if (str == NULL) - return (-1); - - cur = *str; - if (!((IS_URIC_NO_SLASH(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))) { - return (3); - } - NEXT(cur); - while ((IS_URIC(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) - NEXT(cur); - if (uri != NULL) { - if (uri->opaque != NULL) - xmlFree(uri->opaque); - if (uri->cleanup & 2) - uri->opaque = STRNDUP(*str, cur - *str); - else - uri->opaque = xmlURIUnescapeString(*str, cur - *str, NULL); - } - *str = cur; - return (0); -} - -/** - * xmlParseURIServer: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse a server subpart of an URI, it's a finer grain analysis - * of the authority part. - * - * server = [ [ userinfo "@" ] hostport ] - * userinfo = *( unreserved | escaped | - * ";" | ":" | "&" | "=" | "+" | "$" | "," ) - * hostport = host [ ":" port ] - * host = hostname | IPv4address | IPv6reference - * hostname = *( domainlabel "." ) toplabel [ "." ] - * domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum - * toplabel = alpha | alpha *( alphanum | "-" ) alphanum - * IPv6reference = "[" IPv6address "]" - * IPv6address = hexpart [ ":" IPv4address ] - * IPv4address = 1*3digit "." 1*3digit "." 1*3digit "." 1*3digit - * hexpart = hexseq | hexseq "::" [ hexseq ]| "::" [ hexseq ] - * hexseq = hex4 *( ":" hex4) - * hex4 = 1*4hexdig - * port = *digit - * - * Returns 0 or the error code - */ -static int -xmlParseURIServer(xmlURIPtr uri, const char **str) { - const char *cur; - const char *host, *tmp; - const int IPV4max = 4; - const int IPV6max = 8; - int oct; - - if (str == NULL) - return(-1); - - cur = *str; - - /* - * is there a userinfo ? - */ - while (IS_USERINFO(cur)) NEXT(cur); - if (*cur == '@') { - if (uri != NULL) { - if (uri->user != NULL) xmlFree(uri->user); - if (uri->cleanup & 2) - uri->path = STRNDUP(*str, cur - *str); - else - uri->user = xmlURIUnescapeString(*str, cur - *str, NULL); - } - cur++; - } else { - if (uri != NULL) { - if (uri->user != NULL) xmlFree(uri->user); - uri->user = NULL; - } - cur = *str; - } - /* - * This can be empty in the case where there is no server - */ - host = cur; - if (*cur == '/') { - if (uri != NULL) { - if (uri->authority != NULL) xmlFree(uri->authority); - uri->authority = NULL; - if (uri->server != NULL) xmlFree(uri->server); - uri->server = NULL; - uri->port = 0; - } - return(0); - } - /* - * host part of hostport can denote an IPV4 address, an IPV6 address - * or an unresolved name. Check the IP first, its easier to detect - * errors if wrong one. - * An IPV6 address must start with a '[' and end with a ']'. - */ - if (*cur == '[') { - int compress=0; - cur++; - for (oct = 0; oct < IPV6max; ++oct) { - if (*cur == ':') { - if (compress) - return(3); /* multiple compression attempted */ - if (!oct) { /* initial char is compression */ - if (*++cur != ':') - return(3); - } - compress = 1; /* set compression-encountered flag */ - cur++; /* skip over the second ':' */ - continue; - } - while(IS_HEX(*cur)) cur++; - if (oct == (IPV6max-1)) - continue; - if (*cur != ':') - break; - cur++; - } - if ((!compress) && (oct != IPV6max)) - return(3); - if (*cur != ']') - return(3); - if (uri != NULL) { - if (uri->server != NULL) xmlFree(uri->server); - uri->server = (char *)xmlStrndup((xmlChar *)host+1, - (cur-host)-1); - } - cur++; - } else { - /* - * Not IPV6, maybe IPV4 - */ - for (oct = 0; oct < IPV4max; ++oct) { - if (*cur == '.') - return(3); /* e.g. http://.xml/ or http://18.29..30/ */ - while(IS_DIGIT(*cur)) cur++; - if (oct == (IPV4max-1)) - continue; - if (*cur != '.') - break; - cur++; - } - } - if ((host[0] != '[') && (oct < IPV4max || (*cur == '.' && cur++) || - IS_ALPHA(*cur))) { - /* maybe host_name */ - if (!IS_ALPHANUM(*cur)) - return(4); /* e.g. http://xml.$oft */ - do { - do ++cur; while (IS_ALPHANUM(*cur)); - if (*cur == '-') { - --cur; - if (*cur == '.') - return(5); /* e.g. http://xml.-soft */ - ++cur; - continue; - } - if (*cur == '.') { - --cur; - if (*cur == '-') - return(6); /* e.g. http://xml-.soft */ - if (*cur == '.') - return(7); /* e.g. http://xml..soft */ - ++cur; - continue; - } - break; - } while (1); - tmp = cur; - if (tmp[-1] == '.') - --tmp; /* e.g. http://xml.$Oft/ */ - do --tmp; while (tmp >= host && IS_ALPHANUM(*tmp)); - if ((++tmp == host || tmp[-1] == '.') && !IS_ALPHA(*tmp)) - return(8); /* e.g. http://xmlsOft.0rg/ */ - } - if (uri != NULL) { - if (uri->authority != NULL) xmlFree(uri->authority); - uri->authority = NULL; - if (host[0] != '[') { /* it's not an IPV6 addr */ - if (uri->server != NULL) xmlFree(uri->server); - if (uri->cleanup & 2) - uri->server = STRNDUP(host, cur - host); - else - uri->server = xmlURIUnescapeString(host, cur - host, NULL); - } - } - /* - * finish by checking for a port presence. - */ - if (*cur == ':') { - cur++; - if (IS_DIGIT(*cur)) { - if (uri != NULL) - uri->port = 0; - while (IS_DIGIT(*cur)) { - if (uri != NULL) - uri->port = uri->port * 10 + (*cur - '0'); - cur++; - } - } - } - *str = cur; - return(0); -} - -/** - * xmlParseURIRelSegment: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI relative segment - * - * rel_segment = 1*( unreserved | escaped | ";" | "@" | "&" | "=" | - * "+" | "$" | "," ) - * - * Returns 0 or the error code - */ -static int -xmlParseURIRelSegment(xmlURIPtr uri, const char **str) -{ - const char *cur; - - if (str == NULL) - return (-1); - - cur = *str; - if (!((IS_SEGMENT(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))) { - return (3); - } - NEXT(cur); - while ((IS_SEGMENT(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) - NEXT(cur); - if (uri != NULL) { - if (uri->path != NULL) - xmlFree(uri->path); - if (uri->cleanup & 2) - uri->path = STRNDUP(*str, cur - *str); - else - uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); - } - *str = cur; - return (0); -} - -/** - * xmlParseURIPathSegments: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * @slash: should we add a leading slash - * - * Parse an URI set of path segments - * - * path_segments = segment *( "/" segment ) - * segment = *pchar *( ";" param ) - * param = *pchar - * - * Returns 0 or the error code - */ -static int -xmlParseURIPathSegments(xmlURIPtr uri, const char **str, int slash) -{ - const char *cur; - - if (str == NULL) - return (-1); - - cur = *str; - - do { - while ((IS_PCHAR(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) - NEXT(cur); - while (*cur == ';') { - cur++; - while ((IS_PCHAR(cur)) || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) - NEXT(cur); - } - if (*cur != '/') - break; - cur++; - } while (1); - if (uri != NULL) { - int len, len2 = 0; - char *path; - - /* - * Concat the set of path segments to the current path - */ - len = cur - *str; - if (slash) - len++; - - if (uri->path != NULL) { - len2 = strlen(uri->path); - len += len2; - } - path = (char *) xmlMallocAtomic(len + 1); - if (path == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlParseURIPathSegments: out of memory\n"); - *str = cur; - return (-1); - } - if (uri->path != NULL) - memcpy(path, uri->path, len2); - if (slash) { - path[len2] = '/'; - len2++; - } - path[len2] = 0; - if (cur - *str > 0) { - if (uri->cleanup & 2) { - memcpy(&path[len2], *str, cur - *str); - path[len2 + (cur - *str)] = 0; - } else - xmlURIUnescapeString(*str, cur - *str, &path[len2]); - } - if (uri->path != NULL) - xmlFree(uri->path); - uri->path = path; - } - *str = cur; - return (0); -} - -/** - * xmlParseURIAuthority: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse the authority part of an URI. - * - * authority = server | reg_name - * server = [ [ userinfo "@" ] hostport ] - * reg_name = 1*( unreserved | escaped | "$" | "," | ";" | ":" | - * "@" | "&" | "=" | "+" ) - * - * Note : this is completely ambiguous since reg_name is allowed to - * use the full set of chars in use by server: - * - * 3.2.1. Registry-based Naming Authority - * - * The structure of a registry-based naming authority is specific - * to the URI scheme, but constrained to the allowed characters - * for an authority component. - * - * Returns 0 or the error code - */ -static int -xmlParseURIAuthority(xmlURIPtr uri, const char **str) { - const char *cur; - int ret; - - if (str == NULL) - return(-1); - - cur = *str; - - /* - * try first to parse it as a server string. - */ - ret = xmlParseURIServer(uri, str); - if ((ret == 0) && (*str != NULL) && - ((**str == 0) || (**str == '/') || (**str == '?'))) - return(0); - *str = cur; - - /* - * failed, fallback to reg_name - */ - if (!IS_REG_NAME(cur)) { - return(5); - } - NEXT(cur); - while (IS_REG_NAME(cur)) NEXT(cur); - if (uri != NULL) { - if (uri->server != NULL) xmlFree(uri->server); - uri->server = NULL; - if (uri->user != NULL) xmlFree(uri->user); - uri->user = NULL; - if (uri->authority != NULL) xmlFree(uri->authority); - if (uri->cleanup & 2) - uri->authority = STRNDUP(*str, cur - *str); - else - uri->authority = xmlURIUnescapeString(*str, cur - *str, NULL); - } - *str = cur; - return(0); -} - -/** - * xmlParseURIHierPart: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI hierarchical part - * - * hier_part = ( net_path | abs_path ) [ "?" query ] - * abs_path = "/" path_segments - * net_path = "//" authority [ abs_path ] - * - * Returns 0 or the error code - */ -static int -xmlParseURIHierPart(xmlURIPtr uri, const char **str) { - int ret; - const char *cur; - - if (str == NULL) - return(-1); - - cur = *str; - - if ((cur[0] == '/') && (cur[1] == '/')) { - cur += 2; - ret = xmlParseURIAuthority(uri, &cur); - if (ret != 0) - return(ret); - if (cur[0] == '/') { - cur++; - ret = xmlParseURIPathSegments(uri, &cur, 1); - } - } else if (cur[0] == '/') { - cur++; - ret = xmlParseURIPathSegments(uri, &cur, 1); - } else { - return(4); - } - if (ret != 0) - return(ret); - if (*cur == '?') { - cur++; - ret = xmlParseURIQuery(uri, &cur); - if (ret != 0) - return(ret); - } - *str = cur; - return(0); -} - -/** - * xmlParseAbsoluteURI: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an URI reference string and fills in the appropriate fields - * of the @uri structure - * - * absoluteURI = scheme ":" ( hier_part | opaque_part ) - * - * Returns 0 or the error code - */ -static int -xmlParseAbsoluteURI(xmlURIPtr uri, const char **str) { - int ret; - const char *cur; - - if (str == NULL) - return(-1); - - cur = *str; - - ret = xmlParseURIScheme(uri, str); - if (ret != 0) return(ret); - if (**str != ':') { - *str = cur; - return(1); - } - (*str)++; - if (**str == '/') - return(xmlParseURIHierPart(uri, str)); - return(xmlParseURIOpaquePart(uri, str)); -} - -/** - * xmlParseRelativeURI: - * @uri: pointer to an URI structure - * @str: pointer to the string to analyze - * - * Parse an relative URI string and fills in the appropriate fields - * of the @uri structure - * - * relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ] - * abs_path = "/" path_segments - * net_path = "//" authority [ abs_path ] - * rel_path = rel_segment [ abs_path ] - * - * Returns 0 or the error code - */ -static int -xmlParseRelativeURI(xmlURIPtr uri, const char **str) { - int ret = 0; - const char *cur; - - if (str == NULL) - return(-1); - - cur = *str; - if ((cur[0] == '/') && (cur[1] == '/')) { - cur += 2; - ret = xmlParseURIAuthority(uri, &cur); - if (ret != 0) - return(ret); - if (cur[0] == '/') { - cur++; - ret = xmlParseURIPathSegments(uri, &cur, 1); - } - } else if (cur[0] == '/') { - cur++; - ret = xmlParseURIPathSegments(uri, &cur, 1); - } else if (cur[0] != '#' && cur[0] != '?') { - ret = xmlParseURIRelSegment(uri, &cur); - if (ret != 0) - return(ret); - if (cur[0] == '/') { - cur++; - ret = xmlParseURIPathSegments(uri, &cur, 1); - } - } - if (ret != 0) - return(ret); - if (*cur == '?') { - cur++; - ret = xmlParseURIQuery(uri, &cur); - if (ret != 0) - return(ret); - } - *str = cur; - return(ret); -} - -/** - * xmlParseURIReference: - * @uri: pointer to an URI structure - * @str: the string to analyze - * - * Parse an URI reference string and fills in the appropriate fields - * of the @uri structure - * - * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] - * - * Returns 0 or the error code - */ -int -xmlParseURIReference(xmlURIPtr uri, const char *str) { - int ret; - const char *tmp = str; - - if (str == NULL) - return(-1); - xmlCleanURI(uri); - - /* - * Try first to parse absolute refs, then fallback to relative if - * it fails. - */ - ret = xmlParseAbsoluteURI(uri, &str); - if (ret != 0) { - xmlCleanURI(uri); - str = tmp; - ret = xmlParseRelativeURI(uri, &str); - } - if (ret != 0) { - xmlCleanURI(uri); - return(ret); - } - - if (*str == '#') { - str++; - ret = xmlParseURIFragment(uri, &str); - if (ret != 0) return(ret); - } - if (*str != 0) { - xmlCleanURI(uri); - return(1); - } - return(0); -} - -/** - * xmlParseURI: - * @str: the URI string to analyze - * - * Parse an URI - * - * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] - * - * Returns a newly built xmlURIPtr or NULL in case of error - */ -xmlURIPtr -xmlParseURI(const char *str) { - xmlURIPtr uri; - int ret; - - if (str == NULL) - return(NULL); - uri = xmlCreateURI(); - if (uri != NULL) { - ret = xmlParseURIReference(uri, str); - if (ret) { - xmlFreeURI(uri); - return(NULL); - } - } - return(uri); -} - -/** - * xmlParseURIRaw: - * @str: the URI string to analyze - * @raw: if 1 unescaping of URI pieces are disabled - * - * Parse an URI but allows to keep intact the original fragments. - * - * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] - * - * Returns a newly built xmlURIPtr or NULL in case of error - */ -xmlURIPtr -xmlParseURIRaw(const char *str, int raw) { - xmlURIPtr uri; - int ret; - - if (str == NULL) - return(NULL); - uri = xmlCreateURI(); - if (uri != NULL) { - if (raw) { - uri->cleanup |= 2; - } - ret = xmlParseURIReference(uri, str); - if (ret) { - xmlFreeURI(uri); - return(NULL); - } - } - return(uri); -} - -/************************************************************************ - * * * Public functions * * * ************************************************************************/ @@ -1844,7 +1869,7 @@ xmlParseURIRaw(const char *str, int raw) { * * Computes he final URI of the reference done by checking that * the given URI is valid, and building the final URI using the - * base URI. This is processed according to section 5.2 of the + * base URI. This is processed according to section 5.2 of the * RFC 2396 * * 5.2. Resolving Relative References to Absolute Form @@ -1868,7 +1893,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * as a reference to "." rather than as a synonym for the current * URI. Should we do that here? */ - if (URI == NULL) + if (URI == NULL) ret = -1; else { if (*URI) { @@ -1939,12 +1964,16 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { res->server = xmlMemStrdup(bas->server); if (bas->user != NULL) res->user = xmlMemStrdup(bas->user); - res->port = bas->port; + res->port = bas->port; } if (bas->path != NULL) res->path = xmlMemStrdup(bas->path); - if (ref->query != NULL) + if (ref->query_raw != NULL) + res->query_raw = xmlMemStrdup (ref->query_raw); + else if (ref->query != NULL) res->query = xmlMemStrdup(ref->query); + else if (bas->query_raw != NULL) + res->query_raw = xmlMemStrdup(bas->query_raw); else if (bas->query != NULL) res->query = xmlMemStrdup(bas->query); if (ref->fragment != NULL) @@ -1964,8 +1993,10 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { } if (bas->scheme != NULL) res->scheme = xmlMemStrdup(bas->scheme); - - if (ref->query != NULL) + + if (ref->query_raw != NULL) + res->query_raw = xmlMemStrdup(ref->query_raw); + else if (ref->query != NULL) res->query = xmlMemStrdup(ref->query); if (ref->fragment != NULL) res->fragment = xmlMemStrdup(ref->fragment); @@ -1984,7 +2015,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { res->server = xmlMemStrdup(ref->server); if (ref->user != NULL) res->user = xmlMemStrdup(ref->user); - res->port = ref->port; + res->port = ref->port; } if (ref->path != NULL) res->path = xmlMemStrdup(ref->path); @@ -1996,7 +2027,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { res->server = xmlMemStrdup(bas->server); if (bas->user != NULL) res->user = xmlMemStrdup(bas->user); - res->port = bas->port; + res->port = bas->port; } /* @@ -2024,8 +2055,7 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { len += strlen(bas->path); res->path = (char *) xmlMallocAtomic(len); if (res->path == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlBuildURI: out of memory\n"); + xmlURIErrMemory("resolving URI against base\n"); goto done; } res->path[0] = 0; @@ -2209,7 +2239,8 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) uptr = (xmlChar *) ref->path; if (*uptr == '/') uptr++; - val = xmlStrdup(uptr); + /* exception characters from xmlSaveUri */ + val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,"); } goto done; } @@ -2270,10 +2301,11 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) } len = xmlStrlen (uptr) + 1; } - + if (nbslash == 0) { if (uptr != NULL) - val = xmlStrdup (uptr); + /* exception characters from xmlSaveUri */ + val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,"); goto done; } @@ -2284,8 +2316,7 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) */ val = (xmlChar *) xmlMalloc (len + 3 * nbslash); if (val == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlBuildRelativeURI: out of memory\n"); + xmlURIErrMemory("building relative URI\n"); goto done; } vptr = val; @@ -2313,6 +2344,12 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) vptr[len - 1] = 0; } + /* escape the freshly-built path */ + vptr = val; + /* exception characters from xmlSaveUri */ + val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,"); + xmlFree(vptr); + done: /* * Free the working variables @@ -2331,14 +2368,14 @@ done: * xmlCanonicPath: * @path: the resource locator in a filesystem notation * - * Constructs a canonic path from the specified path. + * Constructs a canonic path from the specified path. * - * Returns a new canonic path, or a duplicate of the path parameter if the + * Returns a new canonic path, or a duplicate of the path parameter if the * construction fails. The caller is responsible for freeing the memory occupied - * by the returned string. If there is insufficient memory available, or the + * by the returned string. If there is insufficient memory available, or the * argument is NULL, the function returns NULL. */ -#define IS_WINDOWS_PATH(p) \ +#define IS_WINDOWS_PATH(p) \ ((p != NULL) && \ (((p[0] >= 'a') && (p[0] <= 'z')) || \ ((p[0] >= 'A') && (p[0] <= 'Z'))) && \ @@ -2346,7 +2383,11 @@ done: xmlChar * xmlCanonicPath(const xmlChar *path) { -#if defined(_WIN32) && !defined(__CYGWIN__) +/* + * For Windows implementations, additional work needs to be done to + * replace backslashes in pathnames with "forward slashes" + */ +#if defined(_WIN32) && !defined(__CYGWIN__) int len = 0; int i = 0; xmlChar *p = NULL; @@ -2357,11 +2398,30 @@ xmlCanonicPath(const xmlChar *path) if (path == NULL) return(NULL); + +#if defined(_WIN32) + /* + * We must not change the backslashes to slashes if the the path + * starts with \\?\ + * Those paths can be up to 32k characters long. + * Was added specifically for OpenOffice, those paths can't be converted + * to URIs anyway. + */ + if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && + (path[3] == '\\') ) + return xmlStrdup((const xmlChar *) path); +#endif + + /* sanitize filename starting with // so it can be used as URI */ + if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/')) + path++; + if ((uri = xmlParseURI((const char *) path)) != NULL) { xmlFreeURI(uri); return xmlStrdup(path); } + /* Check if this is an "absolute uri" */ absuri = xmlStrstr(path, BAD_CAST "://"); if (absuri != NULL) { int l, j; @@ -2370,47 +2430,55 @@ xmlCanonicPath(const xmlChar *path) /* * this looks like an URI where some parts have not been - * escaped leading to a parsing problem check that the first + * escaped leading to a parsing problem. Check that the first * part matches a protocol. */ l = absuri - path; + /* Bypass if first part (part before the '://') is > 20 chars */ if ((l <= 0) || (l > 20)) goto path_processing; + /* Bypass if any non-alpha characters are present in first part */ for (j = 0;j < l;j++) { c = path[j]; if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))) goto path_processing; } + /* Escape all except the characters specified in the supplied path */ escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;="); if (escURI != NULL) { + /* Try parsing the escaped path */ uri = xmlParseURI((const char *) escURI); + /* If successful, return the escaped string */ if (uri != NULL) { xmlFreeURI(uri); return escURI; } - xmlFreeURI(uri); } } path_processing: -#if defined(_WIN32) && !defined(__CYGWIN__) +/* For Windows implementations, replace backslashes with 'forward slashes' */ +#if defined(_WIN32) && !defined(__CYGWIN__) /* - * This really need to be cleaned up by someone with a Windows box + * Create a URI structure */ uri = xmlCreateURI(); - if (uri == NULL) { + if (uri == NULL) { /* Guard against 'out of memory' */ return(NULL); } len = xmlStrlen(path); if ((len > 2) && IS_WINDOWS_PATH(path)) { + /* make the scheme 'file' */ uri->scheme = xmlStrdup(BAD_CAST "file"); + /* allocate space for leading '/' + path + string terminator */ uri->path = xmlMallocAtomic(len + 2); if (uri->path == NULL) { - xmlFreeURI(uri); + xmlFreeURI(uri); /* Guard agains 'out of memory' */ return(NULL); } + /* Put in leading '/' plus path */ uri->path[0] = '/'; p = uri->path + 1; strncpy(p, path, len + 1); @@ -2422,18 +2490,15 @@ path_processing: } p = uri->path; } + /* Now change all occurences of '\' to '/' */ while (*p != '\0') { if (*p == '\\') *p = '/'; p++; } - if (uri->path == NULL) { - xmlFreeURI(uri); - return(NULL); - } if (uri->scheme == NULL) { - ret = xmlStrdup((const xmlChar *) path); + ret = xmlStrdup((const xmlChar *) uri->path); } else { ret = xmlSaveUri(uri); } @@ -2451,7 +2516,7 @@ path_processing: * * Constructs an URI expressing the existing path * - * Returns a new URI, or a duplicate of the path parameter if the + * Returns a new URI, or a duplicate of the path parameter if the * construction fails. The caller is responsible for freeing the memory * occupied by the returned string. If there is insufficient memory available, * or the argument is NULL, the function returns NULL. @@ -2473,6 +2538,24 @@ xmlPathToURI(const xmlChar *path) cal = xmlCanonicPath(path); if (cal == NULL) return(NULL); +#if defined(_WIN32) && !defined(__CYGWIN__) + /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?) + If 'cal' is a valid URI allready then we are done here, as continuing would make + it invalid. */ + if ((uri = xmlParseURI((const char *) cal)) != NULL) { + xmlFreeURI(uri); + return cal; + } + /* 'cal' can contain a relative path with backslashes. If that is processed + by xmlSaveURI, they will be escaped and the external entity loader machinery + will fail. So convert them to slashes. Misuse 'ret' for walking. */ + ret = cal; + while (*ret != '\0') { + if (*ret == '\\') + *ret = '/'; + ret++; + } +#endif memset(&temp, 0, sizeof(temp)); temp.path = (char *) cal; ret = xmlSaveUri(&temp); |