Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
uri.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Authors:
4 * MenTaLguY <mental@rydia.net>
5 * Jon A. Cruz <jon@joncruz.org>
6 *
7 * Copyright (C) 2003 MenTaLguY
8 *
9 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
10 */
11
12#include "uri.h"
13
14#include <cstring>
15
16#include <giomm/contenttype.h>
17#include <giomm/file.h>
18#include <glibmm/base64.h>
19#include <glibmm/convert.h>
20#include <glibmm/ustring.h>
21#include <glibmm/miscutils.h>
22
23#include "bad-uri-exception.h"
24
25namespace Inkscape {
26
27auto const URI_ALLOWED_NON_ALNUM = "!#$%&'()*+,-./:;=?@_~";
28
35static bool uri_needs_escaping(char const *uri)
36{
37 for (auto *p = uri; *p; ++p) {
38 if (!g_ascii_isalnum(*p) && !strchr(URI_ALLOWED_NON_ALNUM, *p)) {
39 return true;
40 }
41 }
42 return false;
43}
44
46 init(xmlCreateURI());
47}
48
49URI::URI(gchar const *preformed, char const *baseuri)
50{
51 xmlURIPtr uri;
52 if (!preformed) {
54 }
55
56 // check for invalid characters, escape if needed
57 xmlChar *escaped = nullptr;
58 if (uri_needs_escaping(preformed)) {
59 escaped = xmlURIEscapeStr( //
60 (xmlChar const *)preformed, //
61 (xmlChar const *)URI_ALLOWED_NON_ALNUM);
62 preformed = (decltype(preformed))escaped;
63 }
64
65 // make absolute
66 xmlChar *full = nullptr;
67 if (baseuri) {
68 full = xmlBuildURI( //
69 (xmlChar const *)preformed, //
70 (xmlChar const *)baseuri);
71#if LIBXML_VERSION < 20905
72 // libxml2 bug: "file:/some/file" instead of "file:///some/file"
73 auto f = (gchar const *)full;
74 if (f && g_str_has_prefix(f, "file:/") && f[6] != '/') {
75 auto fixed = std::string(f, 6) + "//" + std::string(f + 6);
76 xmlFree(full);
77 full = (xmlChar *)xmlMemStrdup(fixed.c_str());
78 }
79#endif
80 preformed = (decltype(preformed))full;
81 }
82
83 uri = xmlParseURI(preformed);
84
85 if (full) {
86 xmlFree(full);
87 }
88 if (escaped) {
89 xmlFree(escaped);
90 }
91 if (!uri) {
92 throw MalformedURIException();
93 }
94 init(uri);
95}
96
97URI::URI(char const *preformed, URI const &baseuri)
98 : URI::URI(preformed, baseuri.str().c_str())
99{
100}
101
102// From RFC 2396:
103//
104// URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
105// absoluteURI = scheme ":" ( hier_part | opaque_part )
106// relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
107//
108// hier_part = ( net_path | abs_path ) [ "?" query ]
109// opaque_part = uric_no_slash *uric
110//
111// uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
112// "&" | "=" | "+" | "$" | ","
113//
114// net_path = "//" authority [ abs_path ]
115// abs_path = "/" path_segments
116// rel_path = rel_segment [ abs_path ]
117//
118// rel_segment = 1*( unreserved | escaped |
119// ";" | "@" | "&" | "=" | "+" | "$" | "," )
120//
121// authority = server | reg_name
122
123bool URI::isOpaque() const {
124 return getOpaque() != nullptr;
125}
126
127bool URI::isRelative() const {
128 return !_xmlURIPtr()->scheme;
129}
130
131bool URI::isNetPath() const {
132 return isRelative() && _xmlURIPtr()->server;
133}
134
136 if (isRelative() && !_xmlURIPtr()->server) {
137 const gchar *path = getPath();
138 return path && path[0] != '/';
139 }
140 return false;
141}
142
144 if (isRelative() && !_xmlURIPtr()->server) {
145 const gchar *path = getPath();
146 return path && path[0] == '/';
147 }
148 return false;
149}
150
151const gchar *URI::getScheme() const {
152 return (gchar *)_xmlURIPtr()->scheme;
153}
154
155const gchar *URI::getPath() const {
156 return (gchar *)_xmlURIPtr()->path;
157}
158
159const gchar *URI::getQuery() const {
160 return (gchar *)_xmlURIPtr()->query;
161}
162
163const gchar *URI::getFragment() const {
164 return (gchar *)_xmlURIPtr()->fragment;
165}
166
167const gchar *URI::getOpaque() const {
168 if (!isRelative() && !_xmlURIPtr()->server) {
169 const gchar *path = getPath();
170 if (path && path[0] != '/') {
171 return path;
172 }
173 }
174 return nullptr;
175}
176
177std::string URI::toNativeFilename() const
178{ //
179 auto uristr = str();
180
181 // remove fragment identifier
182 if (getFragment() != nullptr) {
183 uristr.resize(uristr.find('#'));
184 }
185
186 return Glib::filename_from_uri(uristr);
187}
188
189/* TODO !!! proper error handling */
190URI URI::from_native_filename(gchar const *path) {
191 gchar *uri = g_filename_to_uri(path, nullptr, nullptr);
192 URI result(uri);
193 g_free( uri );
194 return result;
195}
196
197URI URI::from_dirname(gchar const *path)
198{
199 std::string pathstr = path ? path : ".";
200
201 if (!Glib::path_is_absolute(pathstr)) {
202 pathstr = Glib::build_filename(Glib::get_current_dir(), pathstr);
203 }
204
205 auto uristr = Glib::filename_to_uri(pathstr);
206
207 if (uristr[uristr.size() - 1] != '/') {
208 uristr.push_back('/');
209 }
210
211 return URI(uristr.c_str());
212}
213
214URI URI::from_href_and_basedir(char const *href, char const *basedir)
215{
216 try {
217 return URI(href, URI::from_dirname(basedir));
218 } catch (...) {
219 return URI();
220 }
221}
222
234static std::string build_relative_uri(char const *uri, char const *base)
235{
236 size_t n_slash = 0;
237 size_t i = 0;
238
239 // find longest common prefix
240 for (; uri[i]; ++i) {
241 if (uri[i] != base[i]) {
242 break;
243 }
244
245 if (uri[i] == '/') {
246 ++n_slash;
247 }
248 }
249
250 // URIs must share protocol://server/
251 if (n_slash < 3) {
252 return uri;
253 }
254
255 // Don't cross filesystem root
256 if (n_slash == 3 && g_str_has_prefix(base, "file:///") && base[8]) {
257 return uri;
258 }
259
260 std::string relative;
261
262 for (size_t j = i; base[j]; ++j) {
263 if (base[j] == '/') {
264 relative += "../";
265 }
266 }
267
268 while (uri[i - 1] != '/') {
269 --i;
270 }
271
272 relative += (uri + i);
273
274 if (relative.empty() && base[i]) {
275 relative = "./";
276 }
277
278 return relative;
279}
280
281std::string URI::str(char const *baseuri) const
282{
283 std::string s;
284 auto saveuri = xmlSaveUri(_xmlURIPtr());
285 if (saveuri) {
286 auto save = (const char *)saveuri;
287 if (baseuri && baseuri[0]) {
288 s = build_relative_uri(save, baseuri);
289 } else {
290 s = save;
291 }
292 xmlFree(saveuri);
293 }
294 return s;
295}
296
297std::string URI::getMimeType() const
298{
299 const char *path = getPath();
300
301 if (path) {
302 if (hasScheme("data")) {
303 for (const char *p = path; *p; ++p) {
304 if (*p == ';' || *p == ',') {
305 return std::string(path, p);
306 }
307 }
308 } else {
309 bool uncertain;
310 auto type = Gio::content_type_guess(path, nullptr, 0, uncertain);
311 return Gio::content_type_get_mime_type(type).raw();
312 }
313 }
314
315 return "unknown/unknown";
316}
317
318std::string URI::getContents() const
319{
320 if (hasScheme("data")) {
321 // handle data URIs
322
323 const char *p = getPath();
324 const char *tok = nullptr;
325
326 // scan "[<media type>][;base64]," header
327 for (; *p && *p != ','; ++p) {
328 if (*p == ';') {
329 tok = p + 1;
330 }
331 }
332
333 // body follows after comma
334 if (*p != ',') {
335 g_critical("data URI misses comma");
336 } else if (tok && strncmp("base64", tok, p - tok) == 0) {
337 // base64 encoded body
338 return Glib::Base64::decode(p + 1);
339 } else {
340 // raw body
341 return p + 1;
342 }
343 } else {
344 // handle non-data URIs with GVfs
345 auto file = Gio::File::create_for_uri(str());
346
347 gsize length = 0;
348 char *buffer = nullptr;
349
350 if (file->load_contents(buffer, length)) {
351 auto contents = std::string(buffer, buffer + length);
352 g_free(buffer);
353 return contents;
354 } else {
355 g_critical("failed to load contents from %.100s", str().c_str());
356 }
357 }
358
359 return "";
360}
361
362bool URI::hasScheme(const char *scheme) const
363{
364 const char *s = getScheme();
365 return s && g_ascii_strcasecmp(s, scheme) == 0;
366}
367
371static int uri_unescape_triplet(const char *s)
372{
373 int H1, H2;
374
375 if (s[0] == '%' //
376 && (H1 = g_ascii_xdigit_value(s[1])) != -1 //
377 && (H2 = g_ascii_xdigit_value(s[2])) != -1) {
378 return (H1 << 4) | H2;
379 }
380
381 return 0;
382}
383
393static int uri_unescape_utf8_codepoint(const char *s, char *out)
394{
395 int n = 0;
396 int value = uri_unescape_triplet(s);
397
398 if ((value >> 5) == /* 0b110 */ 0x6) {
399 // 110xxxxx 10xxxxxx
400 n = 2;
401 } else if ((value >> 4) == /* 0b1110 */ 0xE) {
402 // 1110xxxx 10xxxxxx 10xxxxxx
403 n = 3;
404 } else if ((value >> 3) == /* 0b11110 */ 0x1E) {
405 // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
406 n = 4;
407 } else {
408 return 0;
409 }
410
411 out[0] = value;
412 out[n] = 0;
413
414 for (int i = 1; i < n; ++i) {
415 value = uri_unescape_triplet(s + (i * 3));
416
417 if ((value >> 6) != /* 0b10 */ 0x2) {
418 return 0;
419 }
420
421 out[i] = value;
422 }
423
424 return n * 3;
425}
426
427std::string uri_to_iri(const char *uri)
428{
429 std::string iri;
430
431 char utf8buf[5];
432
433 for (const char *p = uri; *p;) {
434 int n = uri_unescape_utf8_codepoint(p, utf8buf);
435 if (n) {
436 iri.append(utf8buf);
437 p += n;
438 } else {
439 iri += *p;
440 p += 1;
441 }
442 }
443
444 return iri;
445}
446
447} // namespace Inkscape
448
449
450/*
451 Local Variables:
452 mode:c++
453 c-file-style:"stroustrup"
454 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
455 indent-tabs-mode:nil
456 fill-column:99
457 End:
458*/
459// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
TODO: insert short description here.
Represents an URI as per RFC 2396.
Definition uri.h:36
bool isOpaque() const
Determines if the URI represented is an 'opaque' URI.
Definition uri.cpp:123
const char * getQuery() const
Return the query, which is the part between "?" and the optional fragment hash ("#")
Definition uri.cpp:159
bool isRelative() const
Determines if the URI represented is 'relative' as per RFC 2396.
Definition uri.cpp:127
const char * getScheme() const
Return the scheme, e.g. "http", or NULL if this is not an absolute URI.
Definition uri.cpp:151
bool hasScheme(const char *scheme) const
True if the scheme equals the given string (not case sensitive)
Definition uri.cpp:362
xmlURI * _xmlURIPtr() const
Definition uri.h:190
static URI from_dirname(char const *path)
URI of a local directory.
Definition uri.cpp:197
std::string getMimeType() const
Get the MIME type (e.g. "image/png")
Definition uri.cpp:297
static URI from_href_and_basedir(char const *href, char const *basedir)
Convenience function for the common use case given a xlink:href attribute and a local directory as th...
Definition uri.cpp:214
std::string str(char const *baseuri=nullptr) const
Return the string representation of this URI.
Definition uri.cpp:281
const char * getPath() const
Return the path.
Definition uri.cpp:155
std::string toNativeFilename() const
Convert this URI to a native filename.
Definition uri.cpp:177
bool isNetPath() const
Determines if the relative URI represented is a 'net-path' as per RFC 2396.
Definition uri.cpp:131
const char * getOpaque() const
For an opaque URI, return everything between the scheme colon (":") and the optional fragment hash ("...
Definition uri.cpp:167
bool isAbsolutePath() const
Determines if the relative URI represented is a 'absolute-path' as per RFC 2396.
Definition uri.cpp:143
static URI from_native_filename(char const *path)
Construct a "file" URI from an absolute filename.
Definition uri.cpp:190
void init(xmlURI *ptr)
Definition uri.h:188
const char * getFragment() const
Return the fragment, which is everything after "#".
Definition uri.cpp:163
std::string getContents() const
Return the contents of the file.
Definition uri.cpp:318
bool isRelativePath() const
Determines if the relative URI represented is a 'relative-path' as per RFC 2396.
Definition uri.cpp:135
Css & result
Helper class to stream background task notifications as a series of messages.
std::string uri_to_iri(const char *uri)
Unescape the UTF-8 parts of the given URI.
Definition uri.cpp:427
static int uri_unescape_utf8_codepoint(const char *s, char *out)
If s starts with a percent-escaped UTF-8 sequence, unescape one code point and store it in out variab...
Definition uri.cpp:393
auto const URI_ALLOWED_NON_ALNUM
Definition uri.cpp:27
static int uri_unescape_triplet(const char *s)
If s starts with a "%XX" triplet, return its byte value, 0 otherwise.
Definition uri.cpp:371
static std::string build_relative_uri(char const *uri, char const *base)
Replacement for buggy xmlBuildRelativeURI https://gitlab.gnome.org/GNOME/libxml2/merge_requests/12.
Definition uri.cpp:234
static bool uri_needs_escaping(char const *uri)
Return true if the given URI string contains characters that need escaping.
Definition uri.cpp:35