1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-03-13 20:58:16 +03:00

uri: Handle filesystem paths in xmlBuildRelativeURISafe

This mainly fixes issues on Windows but should also fix a few general
corner cases.

Should fix #745.
This commit is contained in:
Nick Wellnhofer 2024-06-23 16:24:46 +02:00
parent 7655ed2cc0
commit 2ce70cde46
2 changed files with 289 additions and 66 deletions

View File

@ -6,6 +6,7 @@
#include "libxml.h"
#include <libxml/parser.h>
#include <libxml/uri.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlwriter.h>
#include <libxml/HTMLparser.h>
@ -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;
}

224
uri.c
View File

@ -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
*/