Add a test for relocations that are valid paths but would be invalid URIs if mis-interpreted
2 * Copyright (C) 2014, 2016 J. Ali Harlow <ali@juiblex.co.uk>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "razor-internal.h"
27 static int valid_unicode(unsigned unicode)
30 * Within the U+0000..U+10FFFF range defined by RFC3629
31 * but not in the U+D800..U+DFFF range prohibited in UTF-8.
33 return unicode < 0xD800 || (unicode >= 0xE000 && unicode < 0x110000);
36 char *razor_path_from_parsed_uri(const struct razor_uri *ru,
37 struct razor_error **error)
39 int continuation_bytes = 0;
40 char *path, *p, *s, *uri;
45 uri = razor_uri_recompose(ru);
46 razor_set_error(error, RAZOR_GENERAL_ERROR,
47 RAZOR_GENERAL_ERROR_BAD_URI, uri,
48 "URI does not include a scheme");
53 if (strcmp(ru->scheme, "file")) {
54 uri = razor_uri_recompose(ru);
55 razor_set_error(error, RAZOR_GENERAL_ERROR,
56 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, uri,
62 if (ru->host && *ru->host && strcmp(ru->host, "localhost") ||
63 ru->userinfo || ru->port) {
64 uri = razor_uri_recompose(ru);
65 razor_set_error(error, RAZOR_GENERAL_ERROR,
66 RAZOR_GENERAL_ERROR_UNSUPPORTED_URI, uri,
67 "URI refers to a non-local file");
75 * Under MS-Windows, a path of /c:/xxx maps to c:/xxx
76 * Note that PathCreateFromUrl converts / to \ as well.
78 if (s[0] == '/' && is_alpha(s[1]) && s[2] == ':' && s[3] == '/')
82 p = path = malloc(strlen(s) + 1);
85 if (*s >= 0x7F || *s < 0x20)
88 if (continuation_bytes)
93 c = pchar_get_char(s);
95 if (c == '/' || c == '\\')
100 else if (!continuation_bytes) {
101 if (c >= 0xF5 || c == 0xC0 || c == 0xC1)
103 else if (c >= 0xF0) {
105 continuation_bytes = 3;
106 } else if (c >= 0xE0) {
108 continuation_bytes = 2;
109 } else if (c >= 0xC0) {
111 continuation_bytes = 1;
113 } else if ((c & 0xC0) != 0x80)
117 unicode |= (c & 0x3F);
119 if (!--continuation_bytes &&
120 !valid_unicode(unicode))
129 if (*s || continuation_bytes) {
130 uri = razor_uri_recompose(ru);
131 razor_set_error(error, RAZOR_GENERAL_ERROR,
132 RAZOR_GENERAL_ERROR_BAD_URI,
133 uri, "Illegal character in file URI path");
141 return realloc(path, p - path);
144 RAZOR_EXPORT char *razor_path_from_uri(const char *uri,
145 struct razor_error **error)
150 if (razor_uri_parse(&ru, uri, error))
153 path = razor_path_from_parsed_uri(&ru, error);
155 razor_uri_destroy(&ru);
160 RAZOR_EXPORT char *razor_path_to_uri(const char *path)
164 int check_dotdot, len;
167 uri = malloc(5 + (4 - 3) + 4 + 3 * strlen(path) + 1);
169 strcpy(uri, "file:");
174 check_dotdot = path[0] != '/' && path[0] != '\\';
176 check_dotdot = path[0] != '/';
183 * Under MS-Windows, c:/xxx maps to a path of /c:/xxx
184 * Relative paths that include a drive letter (eg., c:xxx)
185 * can't be handled directly and have to be converted
188 if (is_alpha(p[0]) && p[1] == ':') {
189 if (p[2] == '/' || p[2] == '\\') {
196 * We need to take care that ".." segments don't remove
197 * the drive letter (eg., c:/../xxx -> file:/c:/../xxx
198 * which normalizes to file:/xxx).
202 s = razor_abspath(p);
203 uri = razor_path_to_uri(s);
211 * Relative paths are complicated. URIs can't have dot segments
212 * so these will be removed during normalization. That often does
213 * the right thing, but where a relative path traverses up the
214 * tree then the result is a URI that points to somewhere quite
215 * different to path: eg., file:../dir normalizes to file:dir
216 * We solve this by inserting a sentinel segment at the beginning.
217 * If the segment is still present after normalization, then it
218 * can just be removed. If it is missing, then we need to create
219 * an absolute path and redo the conversion.
229 if (*p == '/' || is_unreserved(*p) || is_sub_delim(*p) ||
230 *p == ':' || *p == '@')
238 *s++ = "0123456789ABCDEF"[(*(unsigned char *)p)/16];
239 *s++ = "0123456789ABCDEF"[(*(unsigned char *)p)%16];
245 if (razor_uri_parse(&ru, uri, NULL) < 0) {
251 razor_uri_normalize(&ru);
253 uri = razor_uri_recompose(&ru);
255 razor_uri_destroy(&ru);
257 if (check_dotdot == 2) {
258 s = strdup("file:/x:/%2F/");
260 if (str_has_prefix(uri, s)) {
262 memmove(uri + 5 + 3, uri + 9 + 3,
263 strlen(uri + 9 + 3) + 1);
264 uri = realloc(uri, strlen(uri) + 1);
268 s = razor_abspath(path);
269 uri = razor_path_to_uri(s);
272 } else if (check_dotdot) {
273 if (str_has_prefix(uri, "file:%2F/")) {
274 memmove(uri + 5, uri + 9, strlen(uri + 9) + 1);
275 uri = realloc(uri, strlen(uri) + 1);
278 s = razor_abspath(path);
279 uri = razor_path_to_uri(s);
288 razor_path_relative_to_uri(const char *uri, const char *path,
289 struct razor_error **error)
291 char *rel_uri, *result;
293 /* Strictly wrong if uri isn't a file URI, but probably okay */
294 rel_uri = razor_path_to_uri(path);
296 result = razor_resolve_uri_root(uri, rel_uri + 5, 1, error);