/src/libreoffice/sal/osl/unx/file_url.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* |
3 | | * This file is part of the LibreOffice project. |
4 | | * |
5 | | * This Source Code Form is subject to the terms of the Mozilla Public |
6 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
7 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
8 | | * |
9 | | * This file incorporates work covered by the following license notice: |
10 | | * |
11 | | * Licensed to the Apache Software Foundation (ASF) under one or more |
12 | | * contributor license agreements. See the NOTICE file distributed |
13 | | * with this work for additional information regarding copyright |
14 | | * ownership. The ASF licenses this file to you under the Apache |
15 | | * License, Version 2.0 (the "License"); you may not use this file |
16 | | * except in compliance with the License. You may obtain a copy of |
17 | | * the License at http://www.apache.org/licenses/LICENSE-2.0 . |
18 | | */ |
19 | | |
20 | | #include "file_url.hxx" |
21 | | |
22 | | #include <algorithm> |
23 | | #include <cassert> |
24 | | #include <cstring> |
25 | | #include <stdexcept> |
26 | | #include <string_view> |
27 | | #include <limits.h> |
28 | | #include <errno.h> |
29 | | |
30 | | #include <o3tl/safeint.hxx> |
31 | | #include <osl/file.hxx> |
32 | | #include <osl/security.hxx> |
33 | | #include <osl/socket.h> |
34 | | #include <oslsocket.hxx> |
35 | | #include <osl/diagnose.h> |
36 | | #include <osl/thread.h> |
37 | | #include <osl/process.h> |
38 | | |
39 | | #include <rtl/character.hxx> |
40 | | #include <rtl/strbuf.hxx> |
41 | | #include <rtl/uri.h> |
42 | | #include <rtl/uri.hxx> |
43 | | #include <rtl/ustring.hxx> |
44 | | #include <rtl/ustrbuf.hxx> |
45 | | #include <rtl/textcvt.h> |
46 | | #include <sal/log.hxx> |
47 | | |
48 | | #include <uri_internal.hxx> |
49 | | |
50 | | #include "file_error_transl.hxx" |
51 | | #include "file_path_helper.hxx" |
52 | | |
53 | | #include "uunxapi.hxx" |
54 | | |
55 | | /** @file |
56 | | |
57 | | General note |
58 | | |
59 | | This file contains the part that handles File URLs. |
60 | | |
61 | | File URLs as scheme specific notion of URIs |
62 | | (RFC2396) may be handled platform independent, but |
63 | | will not in osl which is considered wrong. |
64 | | Future version of osl should handle File URLs this |
65 | | way. In rtl/uri there is already a URI parser etc. |
66 | | so this code should be consolidated. |
67 | | |
68 | | */ |
69 | | |
70 | | using namespace osl; |
71 | | |
72 | | namespace { |
73 | | |
74 | | // A slightly modified version of Pchar in rtl/source/uri.c, but without |
75 | | // encoding slashes: |
76 | | constexpr auto uriCharClass = rtl::createUriCharClass( |
77 | | u8"!$&'()*+,-./0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~"); |
78 | | |
79 | | } |
80 | | |
81 | | oslFileError SAL_CALL osl_getCanonicalName( rtl_uString* ustrFileURL, rtl_uString** pustrValidURL ) |
82 | 0 | { |
83 | 0 | OSL_FAIL("osl_getCanonicalName not implemented"); |
84 | |
|
85 | 0 | rtl_uString_newFromString(pustrValidURL, ustrFileURL); |
86 | 0 | return osl_File_E_None; |
87 | 0 | } |
88 | | |
89 | | namespace { |
90 | | |
91 | | class UnicodeToTextConverter_Impl |
92 | | { |
93 | | rtl_UnicodeToTextConverter m_converter; |
94 | | |
95 | | UnicodeToTextConverter_Impl() |
96 | 103 | : m_converter (rtl_createUnicodeToTextConverter (osl_getThreadTextEncoding())) |
97 | 103 | {} |
98 | | |
99 | | ~UnicodeToTextConverter_Impl() |
100 | 103 | { |
101 | 103 | rtl_destroyUnicodeToTextConverter (m_converter); |
102 | 103 | } |
103 | | public: |
104 | | static UnicodeToTextConverter_Impl & getInstance() |
105 | 2.47M | { |
106 | 2.47M | static UnicodeToTextConverter_Impl g_theConverter; |
107 | 2.47M | return g_theConverter; |
108 | 2.47M | } |
109 | | |
110 | | sal_Size convert( |
111 | | sal_Unicode const * pSrcBuf, sal_Size nSrcChars, char * pDstBuf, sal_Size nDstBytes, |
112 | | sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtChars) |
113 | 2.47M | { |
114 | 2.47M | OSL_ASSERT(m_converter != nullptr); |
115 | 2.47M | return rtl_convertUnicodeToText ( |
116 | 2.47M | m_converter, nullptr, pSrcBuf, nSrcChars, pDstBuf, nDstBytes, nFlags, pInfo, pSrcCvtChars); |
117 | 2.47M | } |
118 | | }; |
119 | | |
120 | 2.47M | bool convert(OUStringBuffer const & in, OStringBuffer * append) { |
121 | 2.47M | assert(append != nullptr); |
122 | 2.47M | for (sal_Size nConvert = in.getLength();;) { |
123 | 2.47M | auto const oldLen = append->getLength(); |
124 | 2.47M | auto n = std::min( |
125 | 2.47M | std::max(nConvert, sal_Size(PATH_MAX)), |
126 | 2.47M | sal_Size(std::numeric_limits<sal_Int32>::max() - oldLen)); |
127 | | // approximation of required converted size |
128 | 2.47M | auto s = append->appendUninitialized(n); |
129 | 2.47M | sal_uInt32 info; |
130 | 2.47M | sal_Size converted; |
131 | | //TODO: context, for reliable treatment of DESTBUFFERTOSMALL: |
132 | 2.47M | n = UnicodeToTextConverter_Impl::getInstance().convert( |
133 | 2.47M | in.getStr() + in.getLength() - nConvert, nConvert, s, n, |
134 | 2.47M | (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR |
135 | 2.47M | | RTL_UNICODETOTEXT_FLAGS_FLUSH), |
136 | 2.47M | &info, &converted); |
137 | 2.47M | if ((info & RTL_UNICODETOTEXT_INFO_ERROR) != 0) { |
138 | 87 | return false; |
139 | 87 | } |
140 | 2.47M | append->setLength(oldLen + n); |
141 | 2.47M | assert(converted <= nConvert); |
142 | 2.47M | nConvert -= converted; |
143 | 2.47M | assert((nConvert == 0) == ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0)); |
144 | 2.47M | if ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0) { |
145 | 2.47M | break; |
146 | 2.47M | } |
147 | 2.47M | } |
148 | 2.47M | return true; |
149 | 2.47M | } |
150 | | |
151 | 2.47M | bool decodeFromUtf8(std::u16string_view text, OString * result) { |
152 | 2.47M | assert(result != nullptr); |
153 | 2.47M | auto p = text.data(); |
154 | 2.47M | auto const end = p + text.size(); |
155 | 2.47M | OUStringBuffer ubuf(static_cast<int>(text.size())); |
156 | 2.47M | OStringBuffer bbuf(PATH_MAX); |
157 | 51.5M | while (p < end) { |
158 | 49.0M | rtl::uri::detail::EscapeType t; |
159 | 49.0M | sal_uInt32 c = rtl::uri::detail::readUcs4(&p, end, true, RTL_TEXTENCODING_UTF8, &t); |
160 | 49.0M | switch (t) { |
161 | 48.6M | case rtl::uri::detail::EscapeNo: |
162 | 48.6M | if (c == '%') { |
163 | 0 | return false; |
164 | 0 | } |
165 | 48.6M | [[fallthrough]]; |
166 | 49.0M | case rtl::uri::detail::EscapeChar: |
167 | 49.0M | if (rtl::isSurrogate(c)) { |
168 | 0 | return false; |
169 | 0 | } |
170 | 49.0M | ubuf.appendUtf32(c); |
171 | 49.0M | break; |
172 | 9 | case rtl::uri::detail::EscapeOctet: |
173 | 9 | if (!convert(ubuf, &bbuf)) { |
174 | 9 | return false; |
175 | 9 | } |
176 | 0 | ubuf.setLength(0); |
177 | 0 | assert(c <= 0xFF); |
178 | 0 | bbuf.append(char(c)); |
179 | 0 | break; |
180 | 49.0M | } |
181 | 49.0M | } |
182 | 2.47M | if (!convert(ubuf, &bbuf)) { |
183 | 78 | return false; |
184 | 78 | } |
185 | 2.47M | *result = bbuf.makeStringAndClear(); |
186 | 2.47M | return true; |
187 | 2.47M | } |
188 | | |
189 | | template<typename T> oslFileError getSystemPathFromFileUrl( |
190 | | OUString const & url, T * path, bool resolveHome) |
191 | 3.75M | { |
192 | 3.75M | assert(path != nullptr); |
193 | | // For compatibility with assumptions in other parts of the code base, |
194 | | // assume that anything starting with a slash is a system path instead of a |
195 | | // (relative) file URL (except if it starts with two slashes, in which case |
196 | | // it is a relative URL with an authority component): |
197 | 3.75M | if (url.isEmpty() |
198 | 3.75M | || (url[0] == '/' && (url.getLength() == 1 || url[1] != '/'))) |
199 | 432k | { |
200 | 432k | return osl_File_E_INVAL; |
201 | 432k | } |
202 | | // Check for non file scheme: |
203 | 3.32M | sal_Int32 i = 0; |
204 | 3.32M | if (rtl::isAsciiAlpha(url[0])) { |
205 | 13.2M | for (sal_Int32 j = 1; j != url.getLength(); ++j) { |
206 | 13.2M | auto c = url[j]; |
207 | 13.2M | if (c == ':') { |
208 | 3.32M | if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( |
209 | 3.32M | url.pData->buffer, j, |
210 | 3.32M | RTL_CONSTASCII_STRINGPARAM("file")) |
211 | 3.32M | != 0) |
212 | 8.23k | { |
213 | 8.23k | return osl_File_E_INVAL; |
214 | 8.23k | } |
215 | 3.31M | i = j + 1; |
216 | 3.31M | break; |
217 | 3.32M | } |
218 | 9.97M | if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-' |
219 | 328 | && c != '.') |
220 | 270 | { |
221 | 270 | break; |
222 | 270 | } |
223 | 9.97M | } |
224 | 3.32M | } |
225 | | // Handle query or fragment: |
226 | 3.31M | if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1) |
227 | 407 | return osl_File_E_INVAL; |
228 | | // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g., |
229 | | // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN |
230 | | // (and, in each case, ignoring case of ASCII letters): |
231 | 3.31M | if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/') |
232 | 3.31M | { |
233 | 3.31M | i += 2; |
234 | 3.31M | sal_Int32 j = url.indexOf('/', i); |
235 | 3.31M | if (j == -1) |
236 | 0 | j = url.getLength(); |
237 | 3.31M | if (j != i |
238 | 230 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( |
239 | 230 | url.pData->buffer + i, j - i, |
240 | 230 | RTL_CONSTASCII_STRINGPARAM("localhost")) |
241 | 230 | != 0) |
242 | 230 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( |
243 | 230 | url.pData->buffer + i, j - i, |
244 | 230 | RTL_CONSTASCII_STRINGPARAM("127.0.0.1")) |
245 | 230 | != 0)) |
246 | 230 | { |
247 | 230 | OUString hostname; |
248 | | // The 'file' URI Scheme does imply that we want a FQDN in this case |
249 | | // See https://tools.ietf.org/html/rfc8089#section-3 |
250 | 230 | if (osl_getLocalHostnameFQDN(&hostname.pData) != osl_Socket_Ok |
251 | 230 | || (rtl_ustr_compareIgnoreAsciiCase_WithLength( |
252 | 230 | url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength()) |
253 | 230 | != 0)) |
254 | 230 | { |
255 | 230 | return osl_File_E_INVAL; |
256 | 230 | } |
257 | 230 | } |
258 | 3.31M | i = j; |
259 | 3.31M | } |
260 | | // Handle empty path: |
261 | 3.31M | if (i == url.getLength()) |
262 | 12 | { |
263 | 12 | *path = "/"_tstr; |
264 | 12 | return osl_File_E_None; |
265 | 12 | } |
266 | | // Path must not contain %2F: |
267 | 3.31M | if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1) |
268 | 18 | return osl_File_E_INVAL; |
269 | | |
270 | 3.31M | if constexpr (std::is_same_v<T, rtl::OString>) { |
271 | 2.47M | if (!decodeFromUtf8(url.subView(i), path)) { |
272 | 87 | return osl_File_E_INVAL; |
273 | 87 | } |
274 | 2.47M | } else if constexpr (std::is_same_v<T, rtl::OUString>) { |
275 | 839k | *path = rtl::Uri::decode( |
276 | 839k | url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); |
277 | | } else { |
278 | | static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>); |
279 | | } |
280 | | // Path must not contain %2F: |
281 | 3.31M | if (path->indexOf('\0') != -1) |
282 | 0 | return osl_File_E_INVAL; |
283 | | |
284 | | // Handle ~ notation: |
285 | 3.31M | if (resolveHome && path->getLength() >= 2 && (*path)[1] == '~') |
286 | 41 | { |
287 | 41 | sal_Int32 j = path->indexOf('/', 2); |
288 | 41 | if (j == -1) |
289 | 41 | j = path->getLength(); |
290 | | |
291 | 41 | if (j == 2) |
292 | 29 | { |
293 | 29 | OUString home; |
294 | 29 | if (!osl::Security().getHomeDir(home)) |
295 | 0 | { |
296 | 0 | SAL_WARN("sal.file", "osl::Security::getHomeDir failed"); |
297 | 0 | return osl_File_E_INVAL; |
298 | 0 | } |
299 | | |
300 | 29 | i = url.indexOf('/', i + 1); |
301 | | |
302 | 29 | if (i == -1) |
303 | 29 | i = url.getLength(); |
304 | 0 | else |
305 | 0 | ++i; |
306 | | |
307 | | //TODO: cheesy way of ensuring home's path ends in slash: |
308 | 29 | if (!home.isEmpty() && home[home.getLength() - 1] != '/') |
309 | 29 | home += "/"; |
310 | 29 | try |
311 | 29 | { |
312 | 29 | home = rtl::Uri::convertRelToAbs(home, url.copy(i)); |
313 | 29 | } |
314 | 29 | catch (rtl::MalformedUriException & e) |
315 | 29 | { |
316 | 0 | SAL_WARN("sal.file", "rtl::MalformedUriException " << e.getMessage()); |
317 | 0 | return osl_File_E_INVAL; |
318 | 0 | } |
319 | 29 | return getSystemPathFromFileUrl(home, path, false); |
320 | 29 | } |
321 | | // FIXME: replace ~user with user's home directory |
322 | 12 | return osl_File_E_INVAL; |
323 | 41 | } |
324 | 3.31M | return osl_File_E_None; |
325 | 3.31M | } file_url.cxx:oslFileError (anonymous namespace)::getSystemPathFromFileUrl<rtl::OUString>(rtl::OUString const&, rtl::OUString*, bool) Line | Count | Source | 191 | 844k | { | 192 | 844k | assert(path != nullptr); | 193 | | // For compatibility with assumptions in other parts of the code base, | 194 | | // assume that anything starting with a slash is a system path instead of a | 195 | | // (relative) file URL (except if it starts with two slashes, in which case | 196 | | // it is a relative URL with an authority component): | 197 | 844k | if (url.isEmpty() | 198 | 844k | || (url[0] == '/' && (url.getLength() == 1 || url[1] != '/'))) | 199 | 8 | { | 200 | 8 | return osl_File_E_INVAL; | 201 | 8 | } | 202 | | // Check for non file scheme: | 203 | 844k | sal_Int32 i = 0; | 204 | 844k | if (rtl::isAsciiAlpha(url[0])) { | 205 | 3.37M | for (sal_Int32 j = 1; j != url.getLength(); ++j) { | 206 | 3.37M | auto c = url[j]; | 207 | 3.37M | if (c == ':') { | 208 | 843k | if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 209 | 843k | url.pData->buffer, j, | 210 | 843k | RTL_CONSTASCII_STRINGPARAM("file")) | 211 | 843k | != 0) | 212 | 4.13k | { | 213 | 4.13k | return osl_File_E_INVAL; | 214 | 4.13k | } | 215 | 839k | i = j + 1; | 216 | 839k | break; | 217 | 843k | } | 218 | 2.53M | if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-' | 219 | 312 | && c != '.') | 220 | 270 | { | 221 | 270 | break; | 222 | 270 | } | 223 | 2.53M | } | 224 | 843k | } | 225 | | // Handle query or fragment: | 226 | 840k | if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1) | 227 | 382 | return osl_File_E_INVAL; | 228 | | // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g., | 229 | | // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN | 230 | | // (and, in each case, ignoring case of ASCII letters): | 231 | 839k | if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/') | 232 | 839k | { | 233 | 839k | i += 2; | 234 | 839k | sal_Int32 j = url.indexOf('/', i); | 235 | 839k | if (j == -1) | 236 | 0 | j = url.getLength(); | 237 | 839k | if (j != i | 238 | 115 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 239 | 115 | url.pData->buffer + i, j - i, | 240 | 115 | RTL_CONSTASCII_STRINGPARAM("localhost")) | 241 | 115 | != 0) | 242 | 115 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 243 | 115 | url.pData->buffer + i, j - i, | 244 | 115 | RTL_CONSTASCII_STRINGPARAM("127.0.0.1")) | 245 | 115 | != 0)) | 246 | 115 | { | 247 | 115 | OUString hostname; | 248 | | // The 'file' URI Scheme does imply that we want a FQDN in this case | 249 | | // See https://tools.ietf.org/html/rfc8089#section-3 | 250 | 115 | if (osl_getLocalHostnameFQDN(&hostname.pData) != osl_Socket_Ok | 251 | 115 | || (rtl_ustr_compareIgnoreAsciiCase_WithLength( | 252 | 115 | url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength()) | 253 | 115 | != 0)) | 254 | 115 | { | 255 | 115 | return osl_File_E_INVAL; | 256 | 115 | } | 257 | 115 | } | 258 | 839k | i = j; | 259 | 839k | } | 260 | | // Handle empty path: | 261 | 839k | if (i == url.getLength()) | 262 | 12 | { | 263 | 12 | *path = "/"_tstr; | 264 | 12 | return osl_File_E_None; | 265 | 12 | } | 266 | | // Path must not contain %2F: | 267 | 839k | if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1) | 268 | 9 | return osl_File_E_INVAL; | 269 | | | 270 | | if constexpr (std::is_same_v<T, rtl::OString>) { | 271 | | if (!decodeFromUtf8(url.subView(i), path)) { | 272 | | return osl_File_E_INVAL; | 273 | | } | 274 | 839k | } else if constexpr (std::is_same_v<T, rtl::OUString>) { | 275 | 839k | *path = rtl::Uri::decode( | 276 | 839k | url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); | 277 | | } else { | 278 | | static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>); | 279 | | } | 280 | | // Path must not contain %2F: | 281 | 839k | if (path->indexOf('\0') != -1) | 282 | 0 | return osl_File_E_INVAL; | 283 | | | 284 | | // Handle ~ notation: | 285 | 839k | if (resolveHome && path->getLength() >= 2 && (*path)[1] == '~') | 286 | 41 | { | 287 | 41 | sal_Int32 j = path->indexOf('/', 2); | 288 | 41 | if (j == -1) | 289 | 41 | j = path->getLength(); | 290 | | | 291 | 41 | if (j == 2) | 292 | 29 | { | 293 | 29 | OUString home; | 294 | 29 | if (!osl::Security().getHomeDir(home)) | 295 | 0 | { | 296 | 0 | SAL_WARN("sal.file", "osl::Security::getHomeDir failed"); | 297 | 0 | return osl_File_E_INVAL; | 298 | 0 | } | 299 | | | 300 | 29 | i = url.indexOf('/', i + 1); | 301 | | | 302 | 29 | if (i == -1) | 303 | 29 | i = url.getLength(); | 304 | 0 | else | 305 | 0 | ++i; | 306 | | | 307 | | //TODO: cheesy way of ensuring home's path ends in slash: | 308 | 29 | if (!home.isEmpty() && home[home.getLength() - 1] != '/') | 309 | 29 | home += "/"; | 310 | 29 | try | 311 | 29 | { | 312 | 29 | home = rtl::Uri::convertRelToAbs(home, url.copy(i)); | 313 | 29 | } | 314 | 29 | catch (rtl::MalformedUriException & e) | 315 | 29 | { | 316 | 0 | SAL_WARN("sal.file", "rtl::MalformedUriException " << e.getMessage()); | 317 | 0 | return osl_File_E_INVAL; | 318 | 0 | } | 319 | 29 | return getSystemPathFromFileUrl(home, path, false); | 320 | 29 | } | 321 | | // FIXME: replace ~user with user's home directory | 322 | 12 | return osl_File_E_INVAL; | 323 | 41 | } | 324 | 839k | return osl_File_E_None; | 325 | 839k | } |
file_url.cxx:oslFileError (anonymous namespace)::getSystemPathFromFileUrl<rtl::OString>(rtl::OUString const&, rtl::OString*, bool) Line | Count | Source | 191 | 2.91M | { | 192 | 2.91M | assert(path != nullptr); | 193 | | // For compatibility with assumptions in other parts of the code base, | 194 | | // assume that anything starting with a slash is a system path instead of a | 195 | | // (relative) file URL (except if it starts with two slashes, in which case | 196 | | // it is a relative URL with an authority component): | 197 | 2.91M | if (url.isEmpty() | 198 | 2.91M | || (url[0] == '/' && (url.getLength() == 1 || url[1] != '/'))) | 199 | 432k | { | 200 | 432k | return osl_File_E_INVAL; | 201 | 432k | } | 202 | | // Check for non file scheme: | 203 | 2.47M | sal_Int32 i = 0; | 204 | 2.47M | if (rtl::isAsciiAlpha(url[0])) { | 205 | 9.92M | for (sal_Int32 j = 1; j != url.getLength(); ++j) { | 206 | 9.92M | auto c = url[j]; | 207 | 9.92M | if (c == ':') { | 208 | 2.47M | if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 209 | 2.47M | url.pData->buffer, j, | 210 | 2.47M | RTL_CONSTASCII_STRINGPARAM("file")) | 211 | 2.47M | != 0) | 212 | 4.10k | { | 213 | 4.10k | return osl_File_E_INVAL; | 214 | 4.10k | } | 215 | 2.47M | i = j + 1; | 216 | 2.47M | break; | 217 | 2.47M | } | 218 | 7.44M | if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-' | 219 | 16 | && c != '.') | 220 | 0 | { | 221 | 0 | break; | 222 | 0 | } | 223 | 7.44M | } | 224 | 2.47M | } | 225 | | // Handle query or fragment: | 226 | 2.47M | if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1) | 227 | 25 | return osl_File_E_INVAL; | 228 | | // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g., | 229 | | // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN | 230 | | // (and, in each case, ignoring case of ASCII letters): | 231 | 2.47M | if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/') | 232 | 2.47M | { | 233 | 2.47M | i += 2; | 234 | 2.47M | sal_Int32 j = url.indexOf('/', i); | 235 | 2.47M | if (j == -1) | 236 | 0 | j = url.getLength(); | 237 | 2.47M | if (j != i | 238 | 115 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 239 | 115 | url.pData->buffer + i, j - i, | 240 | 115 | RTL_CONSTASCII_STRINGPARAM("localhost")) | 241 | 115 | != 0) | 242 | 115 | && (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths( | 243 | 115 | url.pData->buffer + i, j - i, | 244 | 115 | RTL_CONSTASCII_STRINGPARAM("127.0.0.1")) | 245 | 115 | != 0)) | 246 | 115 | { | 247 | 115 | OUString hostname; | 248 | | // The 'file' URI Scheme does imply that we want a FQDN in this case | 249 | | // See https://tools.ietf.org/html/rfc8089#section-3 | 250 | 115 | if (osl_getLocalHostnameFQDN(&hostname.pData) != osl_Socket_Ok | 251 | 115 | || (rtl_ustr_compareIgnoreAsciiCase_WithLength( | 252 | 115 | url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength()) | 253 | 115 | != 0)) | 254 | 115 | { | 255 | 115 | return osl_File_E_INVAL; | 256 | 115 | } | 257 | 115 | } | 258 | 2.47M | i = j; | 259 | 2.47M | } | 260 | | // Handle empty path: | 261 | 2.47M | if (i == url.getLength()) | 262 | 0 | { | 263 | 0 | *path = "/"_tstr; | 264 | 0 | return osl_File_E_None; | 265 | 0 | } | 266 | | // Path must not contain %2F: | 267 | 2.47M | if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1) | 268 | 9 | return osl_File_E_INVAL; | 269 | | | 270 | 2.47M | if constexpr (std::is_same_v<T, rtl::OString>) { | 271 | 2.47M | if (!decodeFromUtf8(url.subView(i), path)) { | 272 | 87 | return osl_File_E_INVAL; | 273 | 87 | } | 274 | | } else if constexpr (std::is_same_v<T, rtl::OUString>) { | 275 | | *path = rtl::Uri::decode( | 276 | | url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8); | 277 | | } else { | 278 | | static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>); | 279 | | } | 280 | | // Path must not contain %2F: | 281 | 2.47M | if (path->indexOf('\0') != -1) | 282 | 0 | return osl_File_E_INVAL; | 283 | | | 284 | | // Handle ~ notation: | 285 | 2.47M | if (resolveHome && path->getLength() >= 2 && (*path)[1] == '~') | 286 | 0 | { | 287 | 0 | sal_Int32 j = path->indexOf('/', 2); | 288 | 0 | if (j == -1) | 289 | 0 | j = path->getLength(); | 290 | |
| 291 | 0 | if (j == 2) | 292 | 0 | { | 293 | 0 | OUString home; | 294 | 0 | if (!osl::Security().getHomeDir(home)) | 295 | 0 | { | 296 | 0 | SAL_WARN("sal.file", "osl::Security::getHomeDir failed"); | 297 | 0 | return osl_File_E_INVAL; | 298 | 0 | } | 299 | | | 300 | 0 | i = url.indexOf('/', i + 1); | 301 | |
| 302 | 0 | if (i == -1) | 303 | 0 | i = url.getLength(); | 304 | 0 | else | 305 | 0 | ++i; | 306 | | | 307 | | //TODO: cheesy way of ensuring home's path ends in slash: | 308 | 0 | if (!home.isEmpty() && home[home.getLength() - 1] != '/') | 309 | 0 | home += "/"; | 310 | 0 | try | 311 | 0 | { | 312 | 0 | home = rtl::Uri::convertRelToAbs(home, url.copy(i)); | 313 | 0 | } | 314 | 0 | catch (rtl::MalformedUriException & e) | 315 | 0 | { | 316 | 0 | SAL_WARN("sal.file", "rtl::MalformedUriException " << e.getMessage()); | 317 | 0 | return osl_File_E_INVAL; | 318 | 0 | } | 319 | 0 | return getSystemPathFromFileUrl(home, path, false); | 320 | 0 | } | 321 | | // FIXME: replace ~user with user's home directory | 322 | 0 | return osl_File_E_INVAL; | 323 | 0 | } | 324 | 2.47M | return osl_File_E_None; | 325 | 2.47M | } |
|
326 | | |
327 | | } |
328 | | |
329 | | oslFileError SAL_CALL osl_getSystemPathFromFileURL( rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath ) |
330 | 844k | { |
331 | 844k | OUString path; |
332 | 844k | oslFileError e; |
333 | 844k | try |
334 | 844k | { |
335 | 844k | e = getSystemPathFromFileUrl( |
336 | 844k | OUString::unacquired(&ustrFileURL), &path, true); |
337 | 844k | } |
338 | 844k | catch (std::length_error &) |
339 | 844k | { |
340 | 0 | e = osl_File_E_RANGE; |
341 | 0 | } |
342 | | |
343 | 844k | if (e == osl_File_E_None) |
344 | 839k | rtl_uString_assign(pustrSystemPath, path.pData); |
345 | | |
346 | 844k | return e; |
347 | 844k | } |
348 | | |
349 | | oslFileError SAL_CALL osl_getFileURLFromSystemPath( rtl_uString *ustrSystemPath, rtl_uString **pustrFileURL ) |
350 | 701k | { |
351 | 701k | rtl_uString *pTmp = nullptr; |
352 | 701k | sal_Int32 nIndex; |
353 | | |
354 | 701k | auto const & systemPath = OUString::unacquired(&ustrSystemPath); |
355 | | |
356 | 701k | if( systemPath.isEmpty() ) |
357 | 21 | return osl_File_E_INVAL; |
358 | | |
359 | 701k | if( systemPath.startsWith( "file:" ) ) |
360 | 15 | return osl_File_E_INVAL; |
361 | | |
362 | | /* check if system path starts with ~ or ~user and replace it with the appropriate home dir */ |
363 | 701k | if( systemPath.startsWith("~") ) |
364 | 3 | { |
365 | | /* check if another user is specified */ |
366 | 3 | if( ( systemPath.getLength() == 1 ) || |
367 | 3 | ( systemPath[1] == '/' ) ) |
368 | 0 | { |
369 | | /* osl_getHomeDir returns file URL */ |
370 | 0 | oslSecurity pSecurity = osl_getCurrentSecurity(); |
371 | 0 | osl_getHomeDir( pSecurity , &pTmp ); |
372 | 0 | osl_freeSecurityHandle( pSecurity ); |
373 | |
|
374 | 0 | if (!pTmp) |
375 | 0 | return osl_File_E_INVAL; |
376 | | |
377 | | /* remove "file://" prefix */ |
378 | 0 | rtl_uString_newFromStr_WithLength( &pTmp, pTmp->buffer + 7, pTmp->length - 7 ); |
379 | | |
380 | | /* replace '~' in original string */ |
381 | 0 | rtl_uString_newReplaceStrAt( &pTmp, systemPath.pData, 0, 1, pTmp ); |
382 | 0 | } |
383 | 3 | else |
384 | 3 | { |
385 | | /* FIXME: replace ~user with users home directory */ |
386 | 3 | return osl_File_E_INVAL; |
387 | 3 | } |
388 | 3 | } |
389 | | |
390 | | /* check if initial string contains repeated '/' characters */ |
391 | 701k | nIndex = systemPath.indexOf( "//" ); |
392 | 701k | if (nIndex >= 0) |
393 | 1.18k | { |
394 | 1.18k | sal_Int32 nSrcIndex; |
395 | 1.18k | sal_Int32 nDeleted = 0; |
396 | | |
397 | | /* if pTmp is not already allocated, copy systemPath for modification */ |
398 | 1.18k | if( pTmp == nullptr ) |
399 | 1.18k | rtl_uString_newFromString( &pTmp, systemPath.pData ); |
400 | | |
401 | | /* adapt index to pTmp */ |
402 | 1.18k | nIndex += pTmp->length - systemPath.getLength(); |
403 | | |
404 | | /* replace repeated '/' characters with a single '/' */ |
405 | 104k | for( nSrcIndex = nIndex + 1; nSrcIndex < pTmp->length; nSrcIndex++ ) |
406 | 103k | { |
407 | 103k | if( (pTmp->buffer[nSrcIndex] == '/') && (pTmp->buffer[nIndex] == '/') ) |
408 | 1.58k | nDeleted++; |
409 | 101k | else |
410 | 101k | pTmp->buffer[++nIndex] = pTmp->buffer[nSrcIndex]; |
411 | 103k | } |
412 | | |
413 | | /* adjust length member */ |
414 | 1.18k | pTmp->length -= nDeleted; |
415 | 1.18k | } |
416 | | |
417 | 701k | if( pTmp == nullptr ) |
418 | 700k | rtl_uString_assign( &pTmp, systemPath.pData ); |
419 | | |
420 | | /* file URLs must be URI encoded */ |
421 | 701k | rtl_uriEncode( pTmp, uriCharClass.data(), rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8, pustrFileURL ); |
422 | | |
423 | 701k | rtl_uString_release( pTmp ); |
424 | | |
425 | | /* absolute urls should start with 'file://' */ |
426 | 701k | if( (*pustrFileURL)->buffer[0] == '/' ) |
427 | 700k | { |
428 | 700k | rtl_uString *pProtocol = nullptr; |
429 | | |
430 | 700k | rtl_uString_newFromAscii( &pProtocol, "file://" ); |
431 | 700k | rtl_uString_newConcat( pustrFileURL, pProtocol, *pustrFileURL ); |
432 | 700k | rtl_uString_release( pProtocol ); |
433 | 700k | } |
434 | | |
435 | 701k | return osl_File_E_None; |
436 | 701k | } |
437 | | |
438 | | /* |
439 | | * relative URLs are not accepted |
440 | | */ |
441 | | oslFileError getSystemPathFromFileURL_Ex( |
442 | | rtl_uString *ustrFileURL, rtl_uString **pustrSystemPath) |
443 | 83 | { |
444 | 83 | rtl_uString* temp = nullptr; |
445 | 83 | oslFileError osl_error = osl_getSystemPathFromFileURL(ustrFileURL, &temp); |
446 | | |
447 | 83 | if (osl_error == osl_File_E_None) |
448 | 83 | { |
449 | 83 | if (temp->buffer[0] == '/') |
450 | 83 | { |
451 | 83 | *pustrSystemPath = temp; |
452 | 83 | } |
453 | 0 | else |
454 | 0 | { |
455 | 0 | rtl_uString_release(temp); |
456 | 0 | osl_error = osl_File_E_INVAL; |
457 | 0 | } |
458 | 83 | } |
459 | | |
460 | 83 | return osl_error; |
461 | 83 | } |
462 | | |
463 | | namespace |
464 | | { |
465 | | |
466 | | /** Helper function, return a pointer to the final '\0' |
467 | | of a string |
468 | | */ |
469 | | |
470 | | sal_Unicode* ustrtoend(sal_Unicode* pStr) |
471 | 16.0k | { |
472 | 16.0k | return (pStr + rtl_ustr_getLength(pStr)); |
473 | 16.0k | } |
474 | | |
475 | | sal_Unicode* ustrchrcat(const sal_Unicode chr, sal_Unicode* d) |
476 | 7.60k | { |
477 | 7.60k | sal_Unicode* p = ustrtoend(d); |
478 | 7.60k | *p++ = chr; |
479 | 7.60k | *p = 0; |
480 | 7.60k | return d; |
481 | 7.60k | } |
482 | | |
483 | | bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr) |
484 | 826 | { |
485 | 826 | sal_Unicode* p = ustrtoend(pStr); |
486 | 826 | if (p > pStr) |
487 | 826 | p--; |
488 | 826 | return (*p == Chr); |
489 | 826 | } |
490 | | |
491 | | /** |
492 | | Remove the last part of a path, a path that has |
493 | | only a '/' or no '/' at all will be returned |
494 | | unmodified |
495 | | */ |
496 | | |
497 | | sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath) |
498 | 0 | { |
499 | | /* we may always skip -2 because we |
500 | | may at least stand on a '/' but |
501 | | either there is no other character |
502 | | before this '/' or it's another |
503 | | character than the '/' |
504 | | */ |
505 | 0 | sal_Unicode* p = ustrtoend(aPath) - 2; |
506 | | |
507 | | /* move back to the next path separator |
508 | | or to the start of the string */ |
509 | 0 | while ((p > aPath) && (*p != '/')) |
510 | 0 | p--; |
511 | |
|
512 | 0 | if (p >= aPath) |
513 | 0 | { |
514 | 0 | if (*p == '/') |
515 | 0 | { |
516 | 0 | p++; |
517 | 0 | *p = '\0'; |
518 | 0 | } |
519 | 0 | else |
520 | 0 | { |
521 | 0 | *p = '\0'; |
522 | 0 | } |
523 | 0 | } |
524 | |
|
525 | 0 | return aPath; |
526 | 0 | } |
527 | | |
528 | | oslFileError _osl_resolvepath( |
529 | | /*inout*/ sal_Unicode* path, |
530 | | /*inout*/ bool* failed) |
531 | 915 | { |
532 | 915 | oslFileError ferr = osl_File_E_None; |
533 | | |
534 | 915 | if (!*failed) |
535 | 915 | { |
536 | 915 | char unresolved_path[PATH_MAX]; |
537 | 915 | if (!UnicodeToText(unresolved_path, sizeof(unresolved_path), path, rtl_ustr_getLength(path))) |
538 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
539 | | |
540 | 915 | char resolved_path[PATH_MAX]; |
541 | 915 | if (realpath(unresolved_path, resolved_path)) |
542 | 805 | { |
543 | 805 | if (!TextToUnicode(resolved_path, strlen(resolved_path), path, PATH_MAX)) |
544 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
545 | 805 | } |
546 | 110 | else |
547 | 110 | { |
548 | 110 | if (EACCES == errno || ENOTDIR == errno || ENOENT == errno) |
549 | 110 | *failed = true; |
550 | 0 | else |
551 | 0 | ferr = oslTranslateFileError(errno); |
552 | 110 | } |
553 | 915 | } |
554 | | |
555 | 915 | return ferr; |
556 | 915 | } |
557 | | |
558 | | /** |
559 | | Works even with non existing paths. The resulting path must not exceed |
560 | | PATH_MAX else osl_File_E_NAMETOOLONG is the result |
561 | | */ |
562 | | |
563 | | oslFileError osl_getAbsoluteFileURL_impl_(const OUString& unresolved_path, OUString& resolved_path) |
564 | 239 | { |
565 | | /* the given unresolved path must not exceed PATH_MAX */ |
566 | 239 | if (unresolved_path.getLength() >= (PATH_MAX - 2)) |
567 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
568 | | |
569 | 239 | sal_Unicode path_resolved_so_far[PATH_MAX]; |
570 | 239 | const sal_Unicode* punresolved = unresolved_path.getStr(); |
571 | 239 | sal_Unicode* presolvedsf = path_resolved_so_far; |
572 | | |
573 | | /* reserve space for leading '/' and trailing '\0' |
574 | | do not exceed this limit */ |
575 | 239 | sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2; |
576 | | |
577 | | /* if realpath fails with error ENOTDIR, EACCES or ENOENT |
578 | | we will not call it again, because _osl_realpath should also |
579 | | work with non existing directories etc. */ |
580 | 239 | bool realpath_failed = false; |
581 | 239 | oslFileError ferr; |
582 | | |
583 | 239 | path_resolved_so_far[0] = '\0'; |
584 | | |
585 | 7.27k | while (*punresolved != '\0') |
586 | 7.04k | { |
587 | | /* ignore '/.' , skip one part back when '/..' */ |
588 | 7.04k | if ((*punresolved == '.') && (*presolvedsf == '/')) |
589 | 2 | { |
590 | 2 | if (*(punresolved + 1) == '\0') |
591 | 0 | { |
592 | 0 | punresolved++; |
593 | 0 | continue; |
594 | 0 | } |
595 | 2 | if (*(punresolved + 1) == '/') |
596 | 2 | { |
597 | 2 | punresolved += 2; |
598 | 2 | continue; |
599 | 2 | } |
600 | 0 | if ((*(punresolved + 1) == '.') && (*(punresolved + 2) == '\0' || (*(punresolved + 2) == '/'))) |
601 | 0 | { |
602 | 0 | _rmlastpathtoken(path_resolved_so_far); |
603 | |
|
604 | 0 | presolvedsf = ustrtoend(path_resolved_so_far) - 1; |
605 | |
|
606 | 0 | if (*(punresolved + 2) == '/') |
607 | 0 | punresolved += 3; |
608 | 0 | else |
609 | 0 | punresolved += 2; |
610 | |
|
611 | 0 | continue; |
612 | 0 | } |
613 | | |
614 | | /* a file or directory name may start with '.' */ |
615 | 0 | if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) |
616 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
617 | | |
618 | 0 | ustrchrcat(*punresolved++, path_resolved_so_far); |
619 | |
|
620 | 0 | if (*punresolved == '\0' && !realpath_failed) |
621 | 0 | { |
622 | 0 | ferr = _osl_resolvepath( |
623 | 0 | path_resolved_so_far, |
624 | 0 | &realpath_failed); |
625 | |
|
626 | 0 | if (ferr != osl_File_E_None) |
627 | 0 | return ferr; |
628 | 0 | } |
629 | 0 | } |
630 | 7.03k | else if (*punresolved == '/') |
631 | 866 | { |
632 | 866 | if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) |
633 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
634 | | |
635 | 866 | ustrchrcat(*punresolved++, path_resolved_so_far); |
636 | | |
637 | 866 | if (!realpath_failed) |
638 | 826 | { |
639 | 826 | ferr = _osl_resolvepath( |
640 | 826 | path_resolved_so_far, |
641 | 826 | &realpath_failed); |
642 | | |
643 | 826 | if (ferr != osl_File_E_None) |
644 | 0 | return ferr; |
645 | | |
646 | 826 | if (!_islastchr(path_resolved_so_far, '/')) |
647 | 566 | { |
648 | 566 | if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) |
649 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
650 | | |
651 | 566 | ustrchrcat('/', path_resolved_so_far); |
652 | 566 | } |
653 | 826 | } |
654 | 866 | } |
655 | 6.17k | else // any other character |
656 | 6.17k | { |
657 | 6.17k | if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) |
658 | 0 | return oslTranslateFileError(ENAMETOOLONG); |
659 | | |
660 | 6.17k | ustrchrcat(*punresolved++, path_resolved_so_far); |
661 | | |
662 | 6.17k | if (*punresolved == '\0' && !realpath_failed) |
663 | 89 | { |
664 | 89 | ferr = _osl_resolvepath( |
665 | 89 | path_resolved_so_far, |
666 | 89 | &realpath_failed); |
667 | | |
668 | 89 | if (ferr != osl_File_E_None) |
669 | 0 | return ferr; |
670 | 89 | } |
671 | 6.17k | } |
672 | 7.04k | } |
673 | | |
674 | 239 | sal_Int32 len = rtl_ustr_getLength(path_resolved_so_far); |
675 | | |
676 | 239 | OSL_ASSERT(len < PATH_MAX); |
677 | | |
678 | 239 | resolved_path = OUString(path_resolved_so_far, len); |
679 | | |
680 | 239 | return osl_File_E_None; |
681 | 239 | } |
682 | | |
683 | | } |
684 | | |
685 | | oslFileError osl_getAbsoluteFileURL( |
686 | | rtl_uString* ustrBaseDirURL, |
687 | | rtl_uString* ustrRelativeURL, |
688 | | rtl_uString** pustrAbsoluteURL) |
689 | 668 | { |
690 | | /* Work around the below call to getSystemPathFromFileURL rejecting input |
691 | | that starts with "/" (for whatever reason it behaves that way; but |
692 | | changing that would start to break lots of tests at least) */ |
693 | 668 | OUString relUrl(ustrRelativeURL); |
694 | 668 | if (relUrl.startsWith("//")) |
695 | 0 | relUrl = "file:" + relUrl; |
696 | 668 | else if (relUrl.startsWith("/")) |
697 | 103 | relUrl = "file://" + relUrl; |
698 | | |
699 | 668 | OUString unresolved_path; |
700 | | |
701 | 668 | FileBase::RC frc = FileBase::getSystemPathFromFileURL(relUrl, unresolved_path); |
702 | 668 | if (frc != FileBase::E_None) |
703 | 429 | return oslFileError(frc); |
704 | | |
705 | 239 | if (systemPathIsRelativePath(unresolved_path)) |
706 | 83 | { |
707 | 83 | OUString base_path; |
708 | 83 | oslFileError rc = getSystemPathFromFileURL_Ex(ustrBaseDirURL, &base_path.pData); |
709 | 83 | if (rc != osl_File_E_None) |
710 | 0 | return rc; |
711 | | |
712 | 83 | unresolved_path = systemPathMakeAbsolutePath(base_path, unresolved_path); |
713 | 83 | } |
714 | | |
715 | 239 | OUString resolved_path; |
716 | 239 | oslFileError rc = osl_getAbsoluteFileURL_impl_(unresolved_path, resolved_path); |
717 | 239 | if (rc == osl_File_E_None) |
718 | 239 | { |
719 | 239 | rc = osl_getFileURLFromSystemPath(resolved_path.pData, pustrAbsoluteURL); |
720 | 239 | OSL_ASSERT(osl_File_E_None == rc); |
721 | 239 | } |
722 | | |
723 | 239 | return rc; |
724 | 239 | } |
725 | | |
726 | | namespace osl::detail { |
727 | | |
728 | | /** |
729 | | No separate error code if unicode to text conversion or getenv fails because for the |
730 | | caller there is no difference why a file could not be found in $PATH |
731 | | */ |
732 | | bool find_in_PATH(const OUString& file_path, OUString& result) |
733 | 0 | { |
734 | 0 | bool bfound = false; |
735 | 0 | OUString path(u"PATH"_ustr); |
736 | 0 | OUString env_path; |
737 | |
|
738 | 0 | if (osl_getEnvironment(path.pData, &env_path.pData) == osl_Process_E_None) |
739 | 0 | bfound = osl::searchPath(file_path, env_path, result); |
740 | |
|
741 | 0 | return bfound; |
742 | 0 | } |
743 | | } |
744 | | |
745 | | namespace |
746 | | { |
747 | | /** |
748 | | No separate error code if unicode to text conversion or getcwd fails because for the |
749 | | caller there is no difference why a file could not be found in CWD |
750 | | */ |
751 | | bool find_in_CWD(const OUString& file_path, OUString& result) |
752 | 0 | { |
753 | 0 | bool bfound = false; |
754 | 0 | OUString cwd_url; |
755 | |
|
756 | 0 | if (osl_getProcessWorkingDir(&cwd_url.pData) == osl_Process_E_None) |
757 | 0 | { |
758 | 0 | OUString cwd; |
759 | 0 | if (FileBase::getSystemPathFromFileURL(cwd_url, cwd) == FileBase::E_None) |
760 | 0 | bfound = osl::searchPath(file_path, cwd, result); |
761 | 0 | } |
762 | 0 | return bfound; |
763 | 0 | } |
764 | | |
765 | | bool find_in_searchPath(const OUString& file_path, rtl_uString* search_path, OUString& result) |
766 | 0 | { |
767 | 0 | return (search_path && osl::searchPath(file_path, OUString(search_path), result)); |
768 | 0 | } |
769 | | |
770 | | } |
771 | | |
772 | | oslFileError osl_searchFileURL(rtl_uString* ustrFilePath, rtl_uString* ustrSearchPath, rtl_uString** pustrURL) |
773 | 0 | { |
774 | 0 | OSL_PRECOND(ustrFilePath && pustrURL, "osl_searchFileURL: invalid parameter"); |
775 | |
|
776 | 0 | FileBase::RC rc; |
777 | 0 | OUString file_path; |
778 | | |
779 | | // try to interpret search path as file url else assume it's a system path list |
780 | 0 | rc = FileBase::getSystemPathFromFileURL(ustrFilePath, file_path); |
781 | 0 | if (rc == FileBase::E_INVAL) |
782 | 0 | file_path = ustrFilePath; |
783 | 0 | else if (rc != FileBase::E_None) |
784 | 0 | return oslFileError(rc); |
785 | | |
786 | 0 | bool bfound = false; |
787 | 0 | OUString result; |
788 | |
|
789 | 0 | if (find_in_searchPath(file_path, ustrSearchPath, result) || |
790 | 0 | osl::detail::find_in_PATH(file_path, result) || |
791 | 0 | find_in_CWD(file_path, result)) |
792 | 0 | { |
793 | 0 | OUString resolved; |
794 | |
|
795 | 0 | if (osl::realpath(result, resolved)) |
796 | 0 | { |
797 | 0 | oslFileError osl_error = osl_getFileURLFromSystemPath(resolved.pData, pustrURL); |
798 | 0 | SAL_WARN_IF(osl_File_E_None != osl_error, "sal.file", "osl_getFileURLFromSystemPath failed"); |
799 | 0 | bfound = true; |
800 | 0 | } |
801 | 0 | } |
802 | 0 | return bfound ? osl_File_E_None : osl_File_E_NOENT; |
803 | 0 | } |
804 | | |
805 | | oslFileError FileURLToPath(char * buffer, size_t bufLen, rtl_uString* ustrFileURL) |
806 | 1.80M | { |
807 | 1.80M | OString strSystemPath; |
808 | 1.80M | oslFileError osl_error = osl::detail::convertUrlToPathname( |
809 | 1.80M | OUString::unacquired(&ustrFileURL), &strSystemPath); |
810 | | |
811 | 1.80M | if(osl_error != osl_File_E_None) |
812 | 576 | return osl_error; |
813 | | |
814 | 1.80M | osl_systemPathRemoveSeparator(strSystemPath.pData); |
815 | | |
816 | 1.80M | if (o3tl::make_unsigned(strSystemPath.getLength()) >= bufLen) { |
817 | 0 | return osl_File_E_OVERFLOW; |
818 | 0 | } |
819 | 1.80M | std::strcpy(buffer, strSystemPath.getStr()); |
820 | | |
821 | 1.80M | return osl_error; |
822 | 1.80M | } |
823 | | |
824 | | int UnicodeToText( char * buffer, size_t bufLen, const sal_Unicode * uniText, sal_Int32 uniTextLen ) |
825 | 915 | { |
826 | 915 | sal_uInt32 nInfo = 0; |
827 | 915 | sal_Size nSrcChars = 0; |
828 | | |
829 | 915 | sal_Size nDestBytes = UnicodeToTextConverter_Impl::getInstance().convert ( |
830 | 915 | uniText, uniTextLen, buffer, bufLen, |
831 | 915 | OUSTRING_TO_OSTRING_CVTFLAGS | RTL_UNICODETOTEXT_FLAGS_FLUSH, &nInfo, &nSrcChars); |
832 | | |
833 | 915 | if( nInfo & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL ) |
834 | 0 | { |
835 | 0 | errno = EOVERFLOW; |
836 | 0 | return 0; |
837 | 0 | } |
838 | | |
839 | | /* ensure trailing '\0' */ |
840 | 915 | buffer[nDestBytes] = '\0'; |
841 | 915 | return nDestBytes; |
842 | 915 | } |
843 | | |
844 | | namespace |
845 | | { |
846 | | class TextToUnicodeConverter_Impl |
847 | | { |
848 | | rtl_TextToUnicodeConverter m_converter; |
849 | | |
850 | | TextToUnicodeConverter_Impl() |
851 | 103 | : m_converter (rtl_createTextToUnicodeConverter (osl_getThreadTextEncoding())) |
852 | 103 | {} |
853 | | |
854 | | ~TextToUnicodeConverter_Impl() |
855 | 103 | { |
856 | 103 | rtl_destroyTextToUnicodeConverter (m_converter); |
857 | 103 | } |
858 | | |
859 | | public: |
860 | | static TextToUnicodeConverter_Impl & getInstance() |
861 | 91.9k | { |
862 | 91.9k | static TextToUnicodeConverter_Impl g_theConverter; |
863 | 91.9k | return g_theConverter; |
864 | 91.9k | } |
865 | | |
866 | | sal_Size convert( |
867 | | char const * pSrcBuf, sal_Size nSrcBytes, sal_Unicode * pDstBuf, sal_Size nDstChars, |
868 | | sal_uInt32 nFlags, sal_uInt32 * pInfo, sal_Size * pSrcCvtBytes) |
869 | 91.9k | { |
870 | 91.9k | OSL_ASSERT(m_converter != nullptr); |
871 | 91.9k | return rtl_convertTextToUnicode ( |
872 | 91.9k | m_converter, nullptr, pSrcBuf, nSrcBytes, pDstBuf, nDstChars, nFlags, pInfo, pSrcCvtBytes); |
873 | 91.9k | } |
874 | | }; |
875 | | } |
876 | | |
877 | | int TextToUnicode( |
878 | | const char* text, |
879 | | size_t text_buffer_size, |
880 | | sal_Unicode* unic_text, |
881 | | sal_Int32 unic_text_buffer_size) |
882 | 805 | { |
883 | 805 | sal_uInt32 nInfo = 0; |
884 | 805 | sal_Size nSrcChars = 0; |
885 | | |
886 | 805 | sal_Size nDestBytes = TextToUnicodeConverter_Impl::getInstance().convert( |
887 | 805 | text, text_buffer_size, unic_text, unic_text_buffer_size, |
888 | 805 | OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_FLUSH, &nInfo, &nSrcChars); |
889 | | |
890 | 805 | if (nInfo & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) |
891 | 0 | { |
892 | 0 | errno = EOVERFLOW; |
893 | 0 | return 0; |
894 | 0 | } |
895 | | |
896 | | /* ensure trailing '\0' */ |
897 | 805 | unic_text[nDestBytes] = '\0'; |
898 | 805 | return nDestBytes; |
899 | 805 | } |
900 | | |
901 | 2.91M | oslFileError osl::detail::convertUrlToPathname(OUString const & url, OString * pathname) { |
902 | 2.91M | assert(pathname != nullptr); |
903 | 2.91M | oslFileError e; |
904 | 2.91M | try { |
905 | 2.91M | e = getSystemPathFromFileUrl(url, pathname, true); |
906 | 2.91M | } catch (std::length_error &) { |
907 | 0 | e = osl_File_E_RANGE; |
908 | 0 | } |
909 | 2.91M | if (e == osl_File_E_None && !pathname->startsWith("/")) { |
910 | 0 | e = osl_File_E_INVAL; |
911 | 0 | } |
912 | 2.91M | return e; |
913 | 2.91M | } |
914 | | |
915 | 91.1k | oslFileError osl::detail::convertPathnameToUrl(OString const & pathname, OUString * url) { |
916 | 91.1k | assert(url != nullptr); |
917 | 91.1k | OUStringBuffer buf(10+pathname.getLength()); |
918 | 91.1k | buf.append("file:"); |
919 | 91.1k | if (pathname.startsWith("/")) { |
920 | 91.1k | buf.append("//"); |
921 | | // so if pathname should ever start with "//" that isn't mistaken for an authority |
922 | | // component |
923 | 91.1k | } |
924 | 91.1k | for (sal_Size convert = pathname.getLength();;) { |
925 | 91.1k | auto n = std::max(convert, sal_Size(PATH_MAX)); // approximation of required converted size |
926 | 91.1k | OUStringBuffer ubuf(static_cast<int>(n)); |
927 | 91.1k | auto s = ubuf.appendUninitialized(n); |
928 | 91.1k | sal_uInt32 info; |
929 | 91.1k | sal_Size converted; |
930 | | //TODO: context, for reliable treatment of DESTBUFFERTOSMALL: |
931 | 91.1k | n = TextToUnicodeConverter_Impl::getInstance().convert( |
932 | 91.1k | pathname.getStr() + pathname.getLength() - convert, convert, s, n, |
933 | 91.1k | (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR |
934 | 91.1k | | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR | RTL_TEXTTOUNICODE_FLAGS_FLUSH), |
935 | 91.1k | &info, &converted); |
936 | 91.1k | ubuf.setLength(n); |
937 | 91.1k | buf.append( |
938 | 91.1k | rtl::Uri::encode( |
939 | 91.1k | ubuf.makeStringAndClear(), uriCharClass.data(), rtl_UriEncodeIgnoreEscapes, |
940 | 91.1k | RTL_TEXTENCODING_UTF8)); |
941 | 91.1k | assert(converted <= convert); |
942 | 91.1k | convert -= converted; |
943 | 91.1k | if ((info & RTL_TEXTTOUNICODE_INFO_ERROR) != 0) { |
944 | 0 | assert(convert > 0); |
945 | | //TODO: see writeEscapeOctet in sal/rtl/uri.cxx |
946 | 0 | buf.append("%"); |
947 | 0 | unsigned char c = pathname[pathname.getLength() - convert]; |
948 | 0 | assert(c >= 0x80); |
949 | 0 | static sal_Unicode const aHex[16] |
950 | 0 | = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, |
951 | 0 | 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; /* '0'--'9', 'A'--'F' */ |
952 | 0 | buf.append(OUStringChar(aHex[c >> 4]) + OUStringChar(aHex[c & 15])); |
953 | 0 | --convert; |
954 | 0 | continue; |
955 | 0 | } |
956 | 91.1k | assert((convert == 0) == ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0)); |
957 | 91.1k | if ((info & RTL_TEXTTOUNICODE_INFO_DESTBUFFERTOOSMALL) == 0) { |
958 | 91.1k | break; |
959 | 91.1k | } |
960 | 91.1k | } |
961 | 91.1k | *url = buf.makeStringAndClear(); |
962 | 91.1k | return osl_File_E_None; |
963 | 91.1k | } |
964 | | |
965 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |