diff --git a/testparser.c b/testparser.c index 6c51a2cf..0e9ee5a3 100644 --- a/testparser.c +++ b/testparser.c @@ -6,6 +6,7 @@ #include "libxml.h" #include +#include #include #include #include @@ -387,6 +388,135 @@ testWriterClose(void){ } #endif +typedef struct { + const char *uri; + const char *base; + const char *result; +} xmlRelativeUriTest; + +static int +testBuildRelativeUri(void) { + xmlChar *res; + int err = 0; + int i; + + static const xmlRelativeUriTest tests[] = { + { + "/a/b1/c1", + "/a/b2/c2", + "../b1/c1" + }, { + "a/b1/c1", + "a/b2/c2", + "../b1/c1" + }, { + "a/././b1/x/y/../z/../.././c1", + "./a/./b2/././b2", + "../b1/c1" + }, { + "file:///a/b1/c1", + "/a/b2/c2", + "../b1/c1" + }, { + "/a/b1/c1", + "file:///a/b2/c2", + "../b1/c1" + }, { + "a/b1/c1", + "/a/b2/c2", + NULL + }, { + "/a/b1/c1", + "a/b2/c2", + "file:///a/b1/c1" + }, { + "http://example.org/a/b1/c1", + "http://example.org/a/b2/c2", + "../b1/c1" + }, { + "http://example.org/a/b1/c1", + "https://example.org/a/b2/c2", + NULL + }, { + "http://example.org/a/b1/c1", + "http://localhost/a/b2/c2", + NULL + }, { + "with space/x x/y y", + "with space/b2/c2", + "../x%20x/y%20y" + }, { + "with space/x x/y y", + "/b2/c2", + "with%20space/x%20x/y%20y" + } +#if defined(_WIN32) || defined(__CYGWIN__) + , { + "\\a\\b1\\c1", + "\\a\\b2\\c2", + "../b1/c1" + }, { + "\\a\\b1\\c1", + "/a/b2/c2", + "../b1/c1" + }, { + "a\\b1\\c1", + "a/b2/c2", + "../b1/c1" + }, { + "file://server/a/b1/c1", + "\\\\?\\UNC\\server\\a\\b2\\c2", + "../b1/c1" + }, { + "file://server/a/b1/c1", + "\\\\server\\a\\b2\\c2", + "../b1/c1" + }, { + "file:///x:/a/b1/c1", + "x:\\a\\b2\\c2", + "../b1/c1" + }, { + "file:///x:/a/b1/c1", + "\\\\?\\x:\\a\\b2\\c2", + "../b1/c1" + }, { + "file:///x:/a/b1/c1", + "file:///y:/a/b2/c2", + NULL + }, { + "x:/a/b1/c1", + "y:/a/b2/c2", + "file:///x:/a/b1/c1" + }, { + "/a/b1/c1", + "y:/a/b2/c2", + "file:///a/b1/c1" + }, { + "\\server\\a\\b1\\c1", + "a/b2/c2", + "file:///server/a/b1/c1" + } +#endif + }; + + for (i = 0; (size_t) i < sizeof(tests) / sizeof(tests[0]); i++) { + const xmlRelativeUriTest *test = tests + i; + const char *expect; + + res = xmlBuildRelativeURI(BAD_CAST test->uri, BAD_CAST test->base); + expect = test->result ? test->result : test->uri; + if (!xmlStrEqual(res, BAD_CAST expect)) { + fprintf(stderr, "xmlBuildRelativeURI failed uri=%s base=%s " + "result=%s expected=%s\n", test->uri, test->base, + res, expect); + err = 1; + } + xmlFree(res); + } + + return err; +} + int main(void) { int err = 0; @@ -414,6 +544,7 @@ main(void) { #ifdef LIBXML_WRITER_ENABLED err |= testWriterClose(); #endif + err |= testBuildRelativeUri(); return err; } diff --git a/uri.c b/uri.c index 7034f03f..d5302785 100644 --- a/uri.c +++ b/uri.c @@ -2365,6 +2365,121 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { return(out); } +static int +xmlParseUriOrPath(const char *str, xmlURIPtr *out, int *drive) { + xmlURIPtr uri; + char *buf = NULL; + int ret; + + *out = NULL; + *drive = 0; + + uri = xmlCreateURI(); + if (uri == NULL) { + ret = -1; + goto done; + } + + if (xmlStrstr(BAD_CAST str, BAD_CAST "://") == NULL) { + const char *path; + size_t pathSize; + int prependSlash = 0; + + buf = xmlMemStrdup(str); + if (buf == NULL) { + ret = -1; + goto done; + } + xmlNormalizePath(buf, /* isFile */ 1); + + path = buf; + + if (xmlIsAbsolutePath(BAD_CAST buf)) { +#if defined(_WIN32) || defined(__CYGWIN__) + const char *server = NULL; +#endif + + uri->scheme = (char *) xmlStrdup(BAD_CAST "file"); + if (uri->scheme == NULL) { + ret = -1; + goto done; + } + +#if defined(_WIN32) || defined(__CYGWIN__) + if (strncmp(buf, "//?/UNC/", 8) == 0) { + server = buf + 8; + } else if (strncmp(buf, "//?/", 4) == 0) { + path = buf + 3; + } else if (strncmp(buf, "//", 2) == 0) { + server = buf + 2; + } + + if (server != NULL) { + const char *end = strchr(server, '/'); + + if (end == NULL) { + uri->server = xmlMemStrdup(server); + path = "/"; + } else { + uri->server = (char *) xmlStrndup(BAD_CAST server, + end - server); + path = end; + } + if (uri->server == NULL) { + ret = -1; + goto done; + } + } + + if ((((path[0] >= 'A') && (path[0] <= 'Z')) || + ((path[0] >= 'a') && (path[0] <= 'z'))) && + (path[1] == ':')) + prependSlash = 1; +#endif + + if (uri->server == NULL) + uri->port = PORT_EMPTY_SERVER; + } + + pathSize = strlen(path); + uri->path = xmlMalloc(pathSize + prependSlash + 1); + if (uri->path == NULL) { + ret = -1; + goto done; + } + if (prependSlash) { + uri->path[0] = '/'; + memcpy(uri->path + 1, path, pathSize + 1); + } else { + memcpy(uri->path, path, pathSize + 1); + } + } else { + ret = xmlParseURIReference(uri, str); + if (ret != 0) + goto done; + + xmlNormalizePath(uri->path, /* isFile */ 0); + } + +#if defined(_WIN32) || defined(__CYGWIN__) + if ((uri->path[0] == '/') && + (((uri->path[1] >= 'A') && (uri->path[1] <= 'Z')) || + ((uri->path[1] >= 'a') && (uri->path[1] <= 'z'))) && + (uri->path[2] == ':')) + *drive = uri->path[1]; +#endif + + *out = uri; + uri = NULL; + ret = 0; + +done: + xmlFreeURI(uri); + xmlFree(buf); + + return(ret); +} + /** * xmlBuildRelativeURISafe: * @URI: the URI reference under consideration @@ -2411,8 +2526,10 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, int len; xmlURIPtr ref = NULL; xmlURIPtr bas = NULL; - xmlChar *bptr, *uptr, *vptr; + const xmlChar *bptr, *uptr, *rptr; + xmlChar *vptr; int remove_path = 0; + int refDrive, baseDrive; if (valPtr == NULL) return(1); @@ -2420,65 +2537,39 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, if ((URI == NULL) || (*URI == 0)) return(1); - /* - * First parse URI into a standard form - */ - ref = xmlCreateURI (); - if (ref == NULL) { - ret = -1; - goto done; - } - /* If URI not already in "relative" form */ - if (URI[0] != '.') { - ret = xmlParseURIReference (ref, (const char *) URI); - if (ret != 0) - goto done; /* Error in URI, return NULL */ - } else { - ref->path = (char *)xmlStrdup(URI); - if (ref->path == NULL) { - ret = -1; - goto done; - } - } - - /* - * Next parse base into the same standard form - */ - if ((base == NULL) || (*base == 0)) { - val = xmlStrdup (URI); + ret = xmlParseUriOrPath((char *) URI, &ref, &refDrive); + if (ret < 0) + goto done; + if (ret != 0) { + /* Return URI if URI is invalid */ + ret = 0; + val = xmlStrdup(URI); if (val == NULL) ret = -1; - goto done; + goto done; } - bas = xmlCreateURI (); - if (bas == NULL) { - ret = -1; - goto done; - } - if (base[0] != '.') { - ret = xmlParseURIReference (bas, (const char *) base); - if (ret != 0) - goto done; /* Error in base, return NULL */ - } else { - bas->path = (char *)xmlStrdup(base); - if (bas->path == NULL) { - ret = -1; - goto done; - } + + /* Return URI if base is empty */ + if ((base == NULL) || (*base == 0)) + goto done; + + ret = xmlParseUriOrPath((char *) base, &bas, &baseDrive); + if (ret < 0) + goto done; + if (ret != 0) { + /* Return URI if base is invalid */ + ret = 0; + goto done; } /* * If the scheme / server on the URI differs from the base, * just return the URI */ - if ((ref->scheme != NULL) && - ((bas->scheme == NULL) || - (xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) || - (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)) || - (bas->port != ref->port))) { - val = xmlStrdup (URI); - if (val == NULL) - ret = -1; + if ((xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) || + (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)) || + (bas->port != ref->port) || + (baseDrive != refDrive)) { goto done; } if (xmlStrEqual((xmlChar *)bas->path, (xmlChar *)ref->path)) { @@ -2489,9 +2580,11 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, } if (bas->path == NULL) { val = xmlStrdup((xmlChar *)ref->path); - if (val == NULL) + if (val == NULL) { ret = -1; - goto done; + goto done; + } + goto escape; } if (ref->path == NULL) { ref->path = (char *) "/"; @@ -2499,25 +2592,17 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, } /* - * At this point (at last!) we can compare the two paths - * - * First we take care of the special case where either of the - * two path components may be missing (bug 316224) + * At this point we can compare the two paths */ - bptr = (xmlChar *)bas->path; { - xmlChar *rptr = (xmlChar *) ref->path; int pos = 0; + bptr = (xmlChar *) bas->path; + rptr = (xmlChar *) ref->path; + /* * Next we compare the two strings and find where they first differ */ - if ((*rptr == '.') && (rptr[1] == '/')) - rptr += 2; - if ((*bptr == '.') && (bptr[1] == '/')) - bptr += 2; - else if ((*bptr == '/') && (*rptr != '/')) - bptr++; while ((bptr[pos] == rptr[pos]) && (bptr[pos] != 0)) pos++; @@ -2605,9 +2690,10 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, vptr[len - 1] = 0; } +escape: /* escape the freshly-built path */ vptr = val; - /* exception characters from xmlSaveUri */ + /* exception characters from xmlSaveUri */ val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,"); if (val == NULL) ret = -1; @@ -2616,6 +2702,12 @@ xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, xmlFree(vptr); done: + if ((ret == 0) && (val == NULL)) { + val = xmlSaveUri(ref); + if (val == NULL) + ret = -1; + } + /* * Free the working variables */