summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Holder <thomas@thomas-holder.de>2018-11-05 21:12:05 +0000
committerThomas Holder <thomas@thomas-holder.de>2018-11-05 22:35:47 +0000
commit18f8137798ccf99c1f275fd1f6e2247e1e3e45ed (patch)
tree31208ec2f2d8f7281a49c7a29139ceed4ea94df9
parentfix URI::from_dirname("/") (diff)
downloadinkscape-18f8137798ccf99c1f275fd1f6e2247e1e3e45ed.tar.gz
inkscape-18f8137798ccf99c1f275fd1f6e2247e1e3e45ed.zip
Replacement for xmlBuildRelativeURI
- xmlBuildRelativeURI has a bug - we add a special case: Don't cross filesystem root
-rw-r--r--src/object/uri.cpp73
-rw-r--r--testfiles/src/uri-test.cpp9
2 files changed, 71 insertions, 11 deletions
diff --git a/src/object/uri.cpp b/src/object/uri.cpp
index 7c5ab657e..75d7cf954 100644
--- a/src/object/uri.cpp
+++ b/src/object/uri.cpp
@@ -320,22 +320,73 @@ gchar *URI::Impl::toString() const {
}
}
+/**
+ * Replacement for buggy xmlBuildRelativeURI
+ * https://gitlab.gnome.org/GNOME/libxml2/merge_requests/12
+ *
+ * Special case: Don't cross filesystem root, e.g. drive letter on Windows.
+ * This is an optimization to keep things practical, it's not required for correctness.
+ *
+ * @param uri an absolute URI
+ * @param base an absolute URI without any ".." path segments
+ * @return relative URI if possible, otherwise @a uri unchanged
+ */
+static std::string build_relative_uri(char const *uri, char const *base)
+{
+ size_t n_slash = 0;
+ size_t i = 0;
+
+ // find longest common prefix
+ for (; uri[i]; ++i) {
+ if (uri[i] != base[i]) {
+ break;
+ }
+
+ if (uri[i] == '/') {
+ ++n_slash;
+ }
+ }
+
+ // URIs must share protocol://server/
+ if (n_slash < 3) {
+ return uri;
+ }
+
+ // Don't cross filesystem root
+ if (n_slash == 3 && g_str_has_prefix(base, "file:///") && base[8]) {
+ return uri;
+ }
+
+ std::string relative;
+
+ for (size_t j = i; base[j]; ++j) {
+ if (base[j] == '/') {
+ relative += "../";
+ }
+ }
+
+ while (uri[i - 1] != '/') {
+ --i;
+ }
+
+ relative += (uri + i);
+
+ if (relative.empty() && base[i]) {
+ relative = "./";
+ }
+
+ return relative;
+}
+
std::string URI::str(char const *baseuri) const
{
std::string s;
gchar *save = _impl->toString();
if (save) {
- xmlChar *rel = nullptr;
- const char *latest = save;
- if (baseuri) {
- rel = xmlBuildRelativeURI((xmlChar *)save, (xmlChar *)baseuri);
- if (rel) {
- latest = (const char *)rel;
- }
- }
- s = latest;
- if (rel) {
- xmlFree(rel);
+ if (baseuri && baseuri[0]) {
+ s = build_relative_uri(save, baseuri);
+ } else {
+ s = save;
}
g_free(save);
}
diff --git a/testfiles/src/uri-test.cpp b/testfiles/src/uri-test.cpp
index 6f0787af5..c10012d49 100644
--- a/testfiles/src/uri-test.cpp
+++ b/testfiles/src/uri-test.cpp
@@ -53,6 +53,15 @@ TEST(UriTest, Str)
ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://web/a/"), "b/foo/uri.svg");
ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://other/a/"), "http://web/a/b/foo/uri.svg");
+ ASSERT_EQ(URI("http://web/").str("http://web/"), "");
+ ASSERT_EQ(URI("http://web/").str("http://web/url"), "./");
+
+ // special case: don't cross filesystem root
+ ASSERT_EQ(URI("file:///a").str("file:///"), "a");
+ ASSERT_EQ(URI("file:///ax/b").str("file:///ay/"), "file:///ax/b"); // special case
+ ASSERT_EQ(URI("file:///C:/b").str("file:///D:/"), "file:///C:/b"); // special case
+ ASSERT_EQ(URI("file:///C:/a/b").str("file:///C:/b/"), "../a/b");
+
const char *win_url_unc = "file://laptop/My%20Documents/FileSchemeURIs.doc";
const char *win_url_local = "file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc";