summaryrefslogtreecommitdiffstats
path: root/src/object/uri.cpp
diff options
context:
space:
mode:
authorThomas Holder <thomas@thomas-holder.de>2018-11-03 19:44:23 +0000
committerThomas Holder <thomas@thomas-holder.de>2018-11-03 19:44:23 +0000
commit89129c4e71a5a2af0b4d249c261e30e8e6532158 (patch)
tree0f6ad42b76ac2be46d049a23c61d7e85a21c4bdf /src/object/uri.cpp
parentMinor yaxis fix for meassure tool (diff)
downloadinkscape-89129c4e71a5a2af0b4d249c261e30e8e6532158.tar.gz
inkscape-89129c4e71a5a2af0b4d249c261e30e8e6532158.zip
Inkscape::URI API enhancements
Diffstat (limited to 'src/object/uri.cpp')
-rw-r--r--src/object/uri.cpp222
1 files changed, 197 insertions, 25 deletions
diff --git a/src/object/uri.cpp b/src/object/uri.cpp
index 91878512a..499e4456c 100644
--- a/src/object/uri.cpp
+++ b/src/object/uri.cpp
@@ -10,6 +10,10 @@
#include "uri.h"
+#include <giomm/contenttype.h>
+#include <giomm/file.h>
+#include <glibmm/base64.h>
+#include <glibmm/convert.h>
#include <glibmm/ustring.h>
#include <glibmm/miscutils.h>
@@ -17,6 +21,24 @@
namespace Inkscape {
+auto const URI_ALLOWED_NON_ALNUM = "!#$%&'()*+,-./:;=?@_~";
+
+/**
+ * Return true if the given URI string contains characters that need escaping.
+ *
+ * Note: It does not check if valid characters appear in invalid context (e.g.
+ * '%' not followed by two hex digits).
+ */
+static bool uri_needs_escaping(char const *uri)
+{
+ for (auto *p = uri; *p; ++p) {
+ if (!g_ascii_isalnum(*p) && !strchr(URI_ALLOWED_NON_ALNUM, *p)) {
+ return true;
+ }
+ }
+ return false;
+}
+
URI::URI() {
const gchar *in = "";
_impl = Impl::create(xmlParseURI(in));
@@ -27,18 +49,59 @@ URI::URI(const URI &uri) {
_impl = uri._impl;
}
-URI::URI(gchar const *preformed) {
+URI::URI(gchar const *preformed, char const *baseuri)
+{
xmlURIPtr uri;
if (!preformed) {
throw MalformedURIException();
}
+
+ // check for invalid characters, escape if needed
+ xmlChar *escaped = nullptr;
+ if (uri_needs_escaping(preformed)) {
+ escaped = xmlURIEscapeStr( //
+ (xmlChar const *)preformed, //
+ (xmlChar const *)URI_ALLOWED_NON_ALNUM);
+ preformed = (decltype(preformed))escaped;
+ }
+
+ // make absolute
+ xmlChar *full = nullptr;
+ if (baseuri) {
+ full = xmlBuildURI( //
+ (xmlChar const *)preformed, //
+ (xmlChar const *)baseuri);
+#if LIBXML_VERSION < 20905
+ // libxml2 bug: "file:/some/file" instead of "file:///some/file"
+ auto f = (gchar const *)full;
+ if (f && g_str_has_prefix(f, "file:/") && f[6] != '/') {
+ auto fixed = std::string(f, 6) + "//" + std::string(f + 6);
+ xmlFree(full);
+ full = (xmlChar *)xmlMemStrdup(fixed.c_str());
+ }
+#endif
+ preformed = (decltype(preformed))full;
+ }
+
uri = xmlParseURI(preformed);
+
+ if (full) {
+ xmlFree(full);
+ }
+ if (escaped) {
+ xmlFree(escaped);
+ }
if (!uri) {
throw MalformedURIException();
}
_impl = Impl::create(uri);
}
+URI::URI(char const *preformed, URI const &baseuri)
+ : URI::URI(preformed, baseuri.str().c_str())
+{
+}
+
URI::~URI() {
_impl->unreference();
}
@@ -134,13 +197,6 @@ const gchar *URI::Impl::getOpaque() const {
return (gchar *)_uri->opaque;
}
-gchar *URI::to_native_filename(gchar const* uri)
-{
- gchar *filename = nullptr;
- URI tmp(uri);
- filename = tmp.toNativeFilename();
- return filename;
-}
/*
* Returns the absolute path to an existing file referenced in this URI,
* if the uri is data, the path is empty or the file doesn't exist, then
@@ -148,16 +204,28 @@ gchar *URI::to_native_filename(gchar const* uri)
*
* Does not check if the returned path is the local document's path (local)
* and thus redundent. Caller is expected to check against the document's path.
+ *
+ * @param base directory name to use as base if this is not an absolute URL
*/
const std::string URI::getFullPath(std::string const &base) const {
if (!_impl->getPath()) {
return "";
}
- std::string path = std::string(_impl->getPath());
- // Calculate the absolute path from an available base
- if(!base.empty() && !path.empty() && path[0] != '/') {
- path = Glib::build_filename(base, path);
+
+ URI url;
+
+ if (!base.empty() && !getScheme()) {
+ url = Inkscape::URI::from_href_and_basedir(str().c_str(), base.c_str());
+ } else {
+ url = *this;
+ }
+
+ if (!url.hasScheme("file")) {
+ return "";
}
+
+ auto path = Glib::filename_from_uri(url.str());
+
// Check the existence of the file
if(! g_file_test(path.c_str(), G_FILE_TEST_EXISTS)
|| g_file_test(path.c_str(), G_FILE_TEST_IS_DIR) ) {
@@ -168,19 +236,9 @@ const std::string URI::getFullPath(std::string const &base) const {
/* TODO !!! proper error handling */
-gchar *URI::toNativeFilename() const {
- gchar *uriString = toString();
- if (isRelativePath()) {
- return uriString;
- } else {
- gchar *filename = g_filename_from_uri(uriString, nullptr, nullptr);
- g_free(uriString);
- if (filename) {
- return filename;
- } else {
- throw MalformedURIException();
- }
- }
+std::string URI::toNativeFilename() const
+{ //
+ return Glib::filename_from_uri(str());
}
URI URI::fromUtf8( gchar const* path ) {
@@ -222,6 +280,27 @@ URI URI::from_native_filename(gchar const *path) {
return result;
}
+URI URI::from_dirname(gchar const *path)
+{
+ std::string pathstr = path ? path : ".";
+
+ if (!Glib::path_is_absolute(pathstr)) {
+ pathstr = Glib::build_filename(Glib::get_current_dir(), pathstr);
+ }
+
+ auto uristr = Glib::filename_to_uri(pathstr) + "/";
+ return URI(uristr.c_str());
+}
+
+URI URI::from_href_and_basedir(char const *href, char const *basedir)
+{
+ try {
+ return URI(href, URI::from_dirname(basedir));
+ } catch (...) {
+ return URI();
+ }
+}
+
gchar *URI::Impl::toString() const {
xmlChar *string = xmlSaveUri(_uri);
if (string) {
@@ -234,8 +313,101 @@ gchar *URI::Impl::toString() const {
}
}
+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);
+ }
+ g_free(save);
+ }
+ return s;
+}
+
+std::string URI::getMimeType() const
+{
+ const char *path = getPath();
+
+ if (path) {
+ if (hasScheme("data")) {
+ for (const char *p = path; *p; ++p) {
+ if (*p == ';' || *p == ',') {
+ return std::string(path, p);
+ }
+ }
+ } else {
+ bool uncertain;
+ auto type = Gio::content_type_guess(path, nullptr, 0, uncertain);
+ return Gio::content_type_get_mime_type(type).raw();
+ }
+ }
+
+ return "unknown/unknown";
+}
+
+std::string URI::getContents() const
+{
+ if (hasScheme("data")) {
+ // handle data URIs
+
+ const char *p = getPath();
+ const char *tok = nullptr;
+
+ // scan "[<media type>][;base64]," header
+ for (; *p && *p != ','; ++p) {
+ if (*p == ';') {
+ tok = p + 1;
+ }
+ }
+
+ // body follows after comma
+ if (*p != ',') {
+ g_critical("data URI misses comma");
+ } else if (tok && strncmp("base64", tok, p - tok) == 0) {
+ // base64 encoded body
+ return Glib::Base64::decode(p + 1);
+ } else {
+ // raw body
+ return p + 1;
+ }
+ } else {
+ // handle non-data URIs with GVfs
+ auto file = Gio::File::create_for_uri(str());
+
+ gsize length = 0;
+ char *buffer = nullptr;
+
+ if (file->load_contents(buffer, length)) {
+ auto contents = std::string(buffer, buffer + length);
+ g_free(buffer);
+ return contents;
+ } else {
+ g_critical("failed to load contents from %.100s", str().c_str());
+ }
+ }
+
+ return "";
}
+bool URI::hasScheme(const char *scheme) const
+{
+ const char *s = getScheme();
+ return s && g_ascii_strcasecmp(s, scheme) == 0;
+}
+
+} // namespace Inkscape
+
/*
Local Variables: