Coverage Report

Created: 2026-11-06 11:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */