/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
This file contains the part that handles File URLs.
File URLs as scheme specific notion of URIs (RFC2396) may be handled platform independent, but will not in osl which is considered wrong. Future version of osl should handle File URLs this way. In rtl/uri there is already a URI parser etc. so this code should be consolidated.
*/
usingnamespace osl;
namespace {
// A slightly modified version of Pchar in rtl/source/uri.c, but without // encoding slashes:
constexpr auto uriCharClass = rtl::createUriCharClass(
u8"!$&'()*+,-./0123456789:=@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~");
bool convert(OUStringBuffer const & in, OStringBuffer * append) {
assert(append != nullptr); for (sal_Size nConvert = in.getLength();;) { autoconst oldLen = append->getLength(); auto n = std::min(
std::max(nConvert, sal_Size(PATH_MAX)),
sal_Size(std::numeric_limits<sal_Int32>::max() - oldLen)); // approximation of required converted size auto s = append->appendUninitialized(n);
sal_uInt32 info;
sal_Size converted; //TODO: context, for reliable treatment of DESTBUFFERTOSMALL:
n = UnicodeToTextConverter_Impl::getInstance().convert(
in.getStr() + in.getLength() - nConvert, nConvert, s, n,
(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR
| RTL_UNICODETOTEXT_FLAGS_FLUSH),
&info, &converted); if ((info & RTL_UNICODETOTEXT_INFO_ERROR) != 0) { returnfalse;
}
append->setLength(oldLen + n);
assert(converted <= nConvert);
nConvert -= converted;
assert((nConvert == 0) == ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0)); if ((info & RTL_UNICODETOTEXT_INFO_DESTBUFFERTOSMALL) == 0) { break;
}
} returntrue;
}
bool decodeFromUtf8(std::u16string_view text, OString * result) {
assert(result != nullptr); auto p = text.data(); autoconst end = p + text.size();
OUStringBuffer ubuf(static_cast<int>(text.size()));
OStringBuffer bbuf(PATH_MAX); while (p < end) {
rtl::uri::detail::EscapeType t;
sal_uInt32 c = rtl::uri::detail::readUcs4(&p, end, true, RTL_TEXTENCODING_UTF8, &t); switch (t) { case rtl::uri::detail::EscapeNo: if (c == '%') { returnfalse;
}
[[fallthrough]]; case rtl::uri::detail::EscapeChar: if (rtl::isSurrogate(c)) { returnfalse;
}
ubuf.appendUtf32(c); break; case rtl::uri::detail::EscapeOctet: if (!convert(ubuf, &bbuf)) { returnfalse;
}
ubuf.setLength(0);
assert(c <= 0xFF);
bbuf.append(char(c)); break;
}
} if (!convert(ubuf, &bbuf)) { returnfalse;
}
*result = bbuf.makeStringAndClear(); returntrue;
}
template<typename T> oslFileError getSystemPathFromFileUrl(
OUString const & url, T * path, bool resolveHome)
{
assert(path != nullptr); // For compatibility with assumptions in other parts of the code base, // assume that anything starting with a slash is a system path instead of a // (relative) file URL (except if it starts with two slashes, in which case // it is a relative URL with an authority component): if (url.isEmpty()
|| (url[0] == '/' && (url.getLength() == 1 || url[1] != '/')))
{ return osl_File_E_INVAL;
} // Check for non file scheme:
sal_Int32 i = 0; if (rtl::isAsciiAlpha(url[0])) { for (sal_Int32 j = 1; j != url.getLength(); ++j) { auto c = url[j]; if (c == ':') { if (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
url.pData->buffer, j,
RTL_CONSTASCII_STRINGPARAM("file"))
!= 0)
{ return osl_File_E_INVAL;
}
i = j + 1; break;
} if (!rtl::isAsciiAlphanumeric(c) && c != '+' && c != '-'
&& c != '.')
{ break;
}
}
} // Handle query or fragment: if (url.indexOf('?', i) != -1 || url.indexOf('#', i) != -1) return osl_File_E_INVAL; // Handle authority, supporting a host of "localhost", "127.0.0.1", or the exact value (e.g., // not supporting an additional final dot, for simplicity) reported by osl_getLocalHostnameFQDN // (and, in each case, ignoring case of ASCII letters): if (url.getLength() - i >= 2 && url[i] == '/' && url[i + 1] == '/')
{
i += 2;
sal_Int32 j = url.indexOf('/', i); if (j == -1)
j = url.getLength(); if (j != i
&& (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
url.pData->buffer + i, j - i,
RTL_CONSTASCII_STRINGPARAM("localhost"))
!= 0)
&& (rtl_ustr_ascii_compareIgnoreAsciiCase_WithLengths(
url.pData->buffer + i, j - i,
RTL_CONSTASCII_STRINGPARAM("127.0.0.1"))
!= 0))
{
OUString hostname; // The 'file' URI Scheme does imply that we want a FQDN in this case // See https://tools.ietf.org/html/rfc8089#section-3 if (osl_getLocalHostnameFQDN(&hostname.pData) != osl_Socket_Ok
|| (rtl_ustr_compareIgnoreAsciiCase_WithLength(
url.pData->buffer + i, j - i, hostname.getStr(), hostname.getLength())
!= 0))
{ return osl_File_E_INVAL;
}
}
i = j;
} // Handle empty path: if (i == url.getLength())
{
*path = "/"; return osl_File_E_None;
} // Path must not contain %2F: if (url.indexOf("%2F", i) != -1 || url.indexOf("%2f", i) != -1) return osl_File_E_INVAL;
if constexpr (std::is_same_v<T, rtl::OString>) { if (!decodeFromUtf8(url.subView(i), path)) { return osl_File_E_INVAL;
}
} elseif constexpr (std::is_same_v<T, rtl::OUString>) {
*path = rtl::Uri::decode(
url.copy(i), rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8);
} else {
static_assert(std::is_same_v<T, rtl::OString> || std::is_same_v<T, rtl::OUString>);
} // Path must not contain %2F: if (path->indexOf('\0') != -1) return osl_File_E_INVAL;
/* check if system path starts with ~ or ~user and replace it with the appropriate home dir */ if( systemPath.startsWith("~") )
{ /* check if another user is specified */ if( ( systemPath.getLength() == 1 ) ||
( systemPath[1] == '/' ) )
{ /* osl_getHomeDir returns file URL */
oslSecurity pSecurity = osl_getCurrentSecurity();
osl_getHomeDir( pSecurity , &pTmp );
osl_freeSecurityHandle( pSecurity );
bool _islastchr(sal_Unicode* pStr, sal_Unicode Chr)
{
sal_Unicode* p = ustrtoend(pStr); if (p > pStr)
p--; return (*p == Chr);
}
/** Remove the last part of a path, a path that has only a '/' or no '/' at all will be returned unmodified
*/
sal_Unicode* _rmlastpathtoken(sal_Unicode* aPath)
{ /* we may always skip -2 because we may at least stand on a '/' but either there is no other character before this '/' or it's another character than the '/'
*/
sal_Unicode* p = ustrtoend(aPath) - 2;
/* move back to the next path separator
or to the start of the string */ while ((p > aPath) && (*p != '/'))
p--;
/** Works even with non existing paths. The resulting path must not exceed PATH_MAX else osl_File_E_NAMETOOLONG is the result
*/
oslFileError osl_getAbsoluteFileURL_impl_(const OUString& unresolved_path, OUString& resolved_path)
{ /* the given unresolved path must not exceed PATH_MAX */ if (unresolved_path.getLength() >= (PATH_MAX - 2)) return oslTranslateFileError(ENAMETOOLONG);
/* reserve space for leading '/' and trailing '\0'
do not exceed this limit */
sal_Unicode* sentinel = path_resolved_so_far + PATH_MAX - 2;
/* if realpath fails with error ENOTDIR, EACCES or ENOENT we will not call it again, because _osl_realpath should also
work with non existing directories etc. */ bool realpath_failed = false;
oslFileError ferr;
path_resolved_so_far[0] = '\0';
while (*punresolved != '\0')
{ /* ignore '/.' , skip one part back when '/..' */ if ((*punresolved == '.') && (*presolvedsf == '/'))
{ if (*(punresolved + 1) == '\0')
{
punresolved++; continue;
} if (*(punresolved + 1) == '/')
{
punresolved += 2; continue;
} if ((*(punresolved + 1) == '.') && (*(punresolved + 2) == '\0' || (*(punresolved + 2) == '/')))
{
_rmlastpathtoken(path_resolved_so_far);
/* a file or directory name may start with '.' */ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(ENAMETOOLONG);
if (!realpath_failed)
{
ferr = _osl_resolvepath(
path_resolved_so_far,
&realpath_failed);
if (ferr != osl_File_E_None) return ferr;
if (!_islastchr(path_resolved_so_far, '/'))
{ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(ENAMETOOLONG);
ustrchrcat('/', path_resolved_so_far);
}
}
} else// any other character
{ if ((presolvedsf = ustrtoend(path_resolved_so_far)) > sentinel) return oslTranslateFileError(ENAMETOOLONG);
oslFileError osl_getAbsoluteFileURL(
rtl_uString* ustrBaseDirURL,
rtl_uString* ustrRelativeURL,
rtl_uString** pustrAbsoluteURL)
{ /* Work around the below call to getSystemPathFromFileURL rejecting input that starts with "/" (for whatever reason it behaves that way; but
changing that would start to break lots of tests at least) */
OUString relUrl(ustrRelativeURL); if (relUrl.startsWith("//"))
relUrl = "file:" + relUrl; elseif (relUrl.startsWith("/"))
relUrl = "file://" + relUrl;
/** No separate error code if unicode to text conversion or getenv fails because for the caller there is no difference why a file could not be found in $PATH
*/ bool find_in_PATH(const OUString& file_path, OUString& result)
{ bool bfound = false;
OUString path(u"PATH"_ustr);
OUString env_path;
if (osl_getEnvironment(path.pData, &env_path.pData) == osl_Process_E_None)
bfound = osl::searchPath(file_path, env_path, result);
return bfound;
}
}
namespace
{ /** No separate error code if unicode to text conversion or getcwd fails because for the caller there is no difference why a file could not be found in CDW
*/ bool find_in_CWD(const OUString& file_path, OUString& result)
{ bool bfound = false;
OUString cwd_url;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.