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:
parent
7655ed2cc0
commit
2ce70cde46
131
testparser.c
131
testparser.c
@ -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
224
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
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user