summaryrefslogtreecommitdiffstats
path: root/sal/osl/w32/file_url.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sal/osl/w32/file_url.cxx')
-rw-r--r--sal/osl/w32/file_url.cxx990
1 files changed, 990 insertions, 0 deletions
diff --git a/sal/osl/w32/file_url.cxx b/sal/osl/w32/file_url.cxx
new file mode 100644
index 000000000..8c525042d
--- /dev/null
+++ b/sal/osl/w32/file_url.cxx
@@ -0,0 +1,990 @@
+/* -*- 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 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+#include <optional>
+#include <stack>
+#include <string_view>
+
+#include <systools/win32/uwinapi.h>
+
+#include "file_url.hxx"
+#include "file_error.hxx"
+
+#include <rtl/alloc.h>
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.h>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "path_helper.hxx"
+
+// FileURL functions
+
+namespace
+{
+constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH = u"\\\\.\\";
+constexpr std::u16string_view WSTR_LONG_PATH_PREFIX = u"\\\\?\\";
+constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC = u"\\\\?\\UNC\\";
+
+// Internal functions that expect only backslashes as path separators
+
+bool startsWithDriveColon(std::u16string_view s)
+{
+ return s.length() >= 2 && rtl::isAsciiAlpha(s[0]) && s[1] == ':';
+}
+
+bool startsWithDriveColonSlash(std::u16string_view s)
+{
+ return s.length() >= 3 && startsWithDriveColon(s) && s[2] == '\\';
+}
+
+bool startsWithSlashSlash(std::u16string_view s) { return o3tl::starts_with(s, u"\\\\"); }
+
+// An absolute path starts either with \\ (an UNC or device path like \\.\ or \\?\)
+// or with a ASCII alpha character followed by colon followed by backslash.
+bool isAbsolute(std::u16string_view s)
+{
+ return startsWithSlashSlash(s) || startsWithDriveColonSlash(s);
+}
+
+bool onSameDrive(std::u16string_view s1, std::u16string_view s2)
+{
+ assert(startsWithDriveColon(s1) && startsWithDriveColon(s2));
+ return rtl::toAsciiUpperCase(s1[0]) == rtl::toAsciiUpperCase(s2[0]) && s1[1] == s2[1];
+}
+
+sal_Int32 getRootLength(std::u16string_view path)
+{
+ assert(isAbsolute(path));
+ size_t nResult = 0;
+ if (startsWithSlashSlash(path))
+ {
+ // Cases:
+ // 1. Device UNC: \\?\UNC\server\share or \\.\UNC\server\share
+ // 2. Non-device UNC: \\server\share
+ // 3. Device non-UNC: \\?\C: or \\.\C:
+ bool bUNC = false;
+ if (path.length() > 3 && (path[2] == '.' || path[2] == '?') && path[3] == '\\')
+ {
+ if (path.substr(4, 4) == u"UNC\\")
+ {
+ // \\?\UNC\server\share or \\.\UNC\server\share
+ nResult = 8;
+ bUNC = true;
+ }
+ else
+ {
+ // \\?\C: or \\.\C:
+ assert(startsWithDriveColon(path.substr(4)));
+ nResult = 6;
+ }
+ }
+ else
+ {
+ // \\server\share
+ nResult = 2;
+ bUNC = true;
+ }
+ if (bUNC)
+ {
+ // \\?\UNC\server\share or \\.\UNC\server\share or \\server\share
+ assert(nResult < path.length() && path[nResult] != '\\');
+ // Skip server name and share name
+ for (int nSlashes = 0; nResult < path.length(); ++nResult)
+ {
+ if (path[nResult] == '\\' && ++nSlashes == 2)
+ break;
+ }
+ }
+ }
+ else
+ {
+ // C:
+ assert(startsWithDriveColon(path));
+ nResult = 2;
+ }
+ return std::min(nResult, path.length());
+}
+
+std::u16string_view pathView(std::u16string_view path, bool bOnlyRoot)
+{
+ return bOnlyRoot ? path.substr(0, getRootLength(path)) : path;
+}
+
+OUString combinePath(std::u16string_view basePath, std::u16string_view relPath)
+{
+ const bool needSep = !o3tl::ends_with(basePath, u'\\');
+ const auto sSeparator = needSep ? std::u16string_view(u"\\") : std::u16string_view();
+ if (o3tl::starts_with(relPath, u'\\'))
+ relPath.remove_prefix(1); // avoid two adjacent backslashes
+ return OUString::Concat(basePath) + sSeparator + relPath;
+}
+
+OUString removeRelativeParts(const OUString& p)
+{
+ const sal_Int32 rootPos = getRootLength(p);
+ OUStringBuffer buf(p.getLength());
+ buf.append(p.subView(0, rootPos));
+ std::stack<sal_Int32> partPositions;
+ bool bAfterSlash = false;
+ for (sal_Int32 i = rootPos; i < p.getLength(); ++i)
+ {
+ sal_Unicode c = p[i];
+ if (c == '\\')
+ {
+ if (i + 1 < p.getLength() && p[i + 1] == '.')
+ {
+ if (i + 2 == p.getLength() || p[i + 2] == '\\')
+ {
+ // 1. Skip current directory (\.\ or trailing \.)
+ ++i; // process next slash: it may start another "\.\"
+ }
+ else if (p[i + 2] == '.' && (i + 3 == p.getLength() || p[i + 3] == '\\'))
+ {
+ // 2. For parent directory (\..\), drop previous part and skip
+ if (bAfterSlash && partPositions.size())
+ partPositions.pop();
+ sal_Int32 nParentPos = partPositions.size() ? partPositions.top() : rootPos;
+ if (partPositions.size())
+ partPositions.pop();
+ buf.truncate(nParentPos);
+ bAfterSlash = false; // we have just removed slash after parent part
+ i += 2; // process next slash: it may start another "\.\"
+ }
+ }
+ if (bAfterSlash)
+ continue; // 3. Skip double backslashes (\\)
+ partPositions.push(buf.getLength());
+ bAfterSlash = true;
+ }
+ else
+ bAfterSlash = false;
+
+ buf.append(c);
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+static bool IsValidFilePathComponent(
+ std::optional<std::u16string_view>& roComponent,
+ DWORD dwFlags)
+{
+ assert(roComponent);
+ auto lpComponentEnd = roComponent->end();
+ auto lpCurrent = roComponent->begin();
+ bool bValid = lpCurrent != lpComponentEnd; // Empty components are not allowed
+ sal_Unicode cLast = 0;
+
+ while (bValid)
+ {
+ /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
+ if (lpCurrent - roComponent->begin() >= MAX_PATH)
+ {
+ bValid = false;
+ break;
+ }
+
+ switch ( *lpCurrent )
+ {
+ /* Both backslash and slash determine the end of a path component */
+ case '\0':
+ case '/':
+ case '\\':
+ switch ( cLast )
+ {
+ /* Component must not end with '.' or blank and can't be empty */
+
+ case '.':
+ if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
+ {
+ if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
+ 1 == lpCurrent - roComponent->begin() )
+ {
+ /* Either do allow periods anywhere, or current directory */
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ else if ( 2 == lpCurrent - roComponent->begin() && '.' == roComponent->front() )
+ {
+ /* Parent directory is O.K. */
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ }
+ [[fallthrough]];
+ case 0:
+ case ' ':
+ if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
+ lpComponentEnd = lpCurrent;
+ else
+ bValid = false;
+ break;
+ default:
+ lpComponentEnd = lpCurrent;
+ break;
+ }
+ break;
+ /* The following characters are reserved */
+ case '?':
+ case '*':
+ case '<':
+ case '>':
+ case '\"':
+ case '|':
+ case ':':
+ bValid = false;
+ break;
+ default:
+ /* Characters below ASCII 32 are not allowed */
+ if ( *lpCurrent < ' ' )
+ bValid = false;
+ break;
+ }
+
+ if (lpCurrent != lpComponentEnd)
+ cLast = *lpCurrent++;
+
+ if (lpCurrent == lpComponentEnd)
+ break;
+ }
+
+ if ( bValid )
+ {
+ // Empty components are not allowed
+ if (lpComponentEnd - roComponent->begin() < 1)
+ bValid = false;
+ // If we reached the end of the string nullopt is returned
+ else if (lpComponentEnd == roComponent->end())
+ roComponent.reset();
+ else
+ roComponent->remove_prefix(lpComponentEnd - roComponent->begin());
+ }
+
+ return bValid;
+}
+
+static sal_Int32 countInitialSeparators(std::u16string_view path) {
+ size_t n = 0;
+ while (n < path.length() && (path[n] == '\\' || path[n] == '/'))
+ ++n;
+ return n;
+}
+
+DWORD IsValidFilePath(const OUString& path, DWORD dwFlags, OUString* corrected)
+{
+ std::optional<std::u16string_view> oComponent = path;
+ bool bValid = true;
+ DWORD dwPathType = PATHTYPE_ERROR;
+
+ if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
+ dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
+
+ DWORD dwCandidatPathType = PATHTYPE_ERROR;
+
+ if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC))
+ {
+ /* This is long path in UNC notation */
+ oComponent = path.subView(WSTR_LONG_PATH_PREFIX_UNC.size());
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
+ }
+ else if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX))
+ {
+ /* This is long path */
+ oComponent = path.subView(WSTR_LONG_PATH_PREFIX.size());
+
+ if (startsWithDriveColon(*oComponent))
+ {
+ oComponent->remove_prefix(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
+ }
+ }
+ else if ( 2 == countInitialSeparators(path) )
+ {
+ /* The UNC path notation */
+ oComponent = path.subView(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
+ }
+ else if (startsWithDriveColon(path))
+ {
+ /* Local path verification. Must start with <drive>: */
+ oComponent = path.subView(2);
+ dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
+ }
+
+ if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
+ {
+ bValid = IsValidFilePathComponent(oComponent, VALIDATEPATH_ALLOW_ELLIPSE);
+
+ /* So far we have a valid servername. Now let's see if we also have a network resource */
+
+ dwPathType = dwCandidatPathType;
+
+ if ( bValid )
+ {
+ if (oComponent)
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+
+ if (!oComponent)
+ {
+ dwPathType |= PATHTYPE_IS_SERVER;
+ }
+ else
+ {
+ /* Now test the network resource */
+
+ bValid = IsValidFilePathComponent(oComponent, 0);
+
+ /* If we now reached the end of the path, everything is O.K. */
+
+ if (bValid)
+ {
+ if (oComponent)
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+ if (!oComponent)
+ dwPathType |= PATHTYPE_IS_VOLUME;
+ }
+ }
+ }
+ }
+ else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
+ {
+ if (1 == countInitialSeparators(*oComponent))
+ oComponent->remove_prefix(1);
+ else if (!oComponent->empty())
+ bValid = false;
+
+ dwPathType = dwCandidatPathType;
+
+ /* Now we are behind the backslash or it was a simple drive without backslash */
+
+ if (bValid && oComponent->empty())
+ {
+ oComponent.reset();
+ dwPathType |= PATHTYPE_IS_VOLUME;
+ }
+ }
+ else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
+ {
+ /* Can be a relative path */
+ oComponent = path;
+
+ /* Relative path can start with a backslash */
+
+ if (1 == countInitialSeparators(*oComponent))
+ {
+ oComponent->remove_prefix(1);
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+
+ dwPathType = PATHTYPE_RELATIVE;
+ }
+ else
+ {
+ /* Anything else is an error */
+ bValid = false;
+ }
+
+ /* Now validate each component of the path */
+ OUString lastCorrected = path;
+ while (bValid && oComponent)
+ {
+ // Correct path by merging consecutive slashes:
+ if (o3tl::starts_with(*oComponent, u"\\") && corrected != nullptr) {
+ sal_Int32 i = oComponent->data() - lastCorrected.getStr();
+ *corrected = lastCorrected.replaceAt(i, 1, {});
+ //TODO: handle out-of-memory
+ lastCorrected = *corrected;
+ oComponent = lastCorrected.subView(i);
+ }
+
+ bValid = IsValidFilePathComponent(oComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
+
+ if (bValid && oComponent)
+ {
+ oComponent->remove_prefix(1);
+
+ /* If the string behind the backslash is empty, we've done */
+
+ if (oComponent->empty())
+ oComponent.reset();
+ }
+ }
+
+ /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
+ if (bValid && !(dwPathType & PATHTYPE_IS_LONGPATH) && path.getLength() >= MAX_PATH)
+ {
+ bValid = false;
+ }
+
+ return bValid ? dwPathType : PATHTYPE_ERROR;
+}
+
+static std::optional<OUString> osl_decodeURL_(const OString& sUTF8)
+{
+ const char *pSrcEnd;
+ const char *pSrc;
+ bool bValidEncoded = true; /* Assume success */
+
+ /* The resulting decoded string length is shorter or equal to the source length */
+
+ const sal_Int32 nSrcLen = sUTF8.getLength();
+ OStringBuffer aBuffer(nSrcLen + 1);
+
+ pSrc = sUTF8.getStr();
+ pSrcEnd = pSrc + nSrcLen;
+
+ /* Now decode the URL what should result in a UTF-8 string */
+ while ( bValidEncoded && pSrc < pSrcEnd )
+ {
+ switch ( *pSrc )
+ {
+ case '%':
+ {
+ char aToken[3];
+ char aChar;
+
+ pSrc++;
+ aToken[0] = *pSrc++;
+ aToken[1] = *pSrc++;
+ aToken[2] = 0;
+
+ aChar = static_cast<char>(strtoul( aToken, nullptr, 16 ));
+
+ /* The chars are path delimiters and must not be encoded */
+
+ if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
+ bValidEncoded = false;
+ else
+ aBuffer.append(aChar);
+ }
+ break;
+ case '\0':
+ case '#':
+ case '?':
+ bValidEncoded = false;
+ break;
+ default:
+ aBuffer.append(*pSrc++);
+ break;
+ }
+ }
+
+ return bValidEncoded ? OUString(aBuffer.getStr(), aBuffer.getLength(), RTL_TEXTENCODING_UTF8)
+ : std::optional<OUString>();
+}
+
+static OUString osl_encodeURL_(std::u16string_view sURL)
+{
+ /* Encode non ascii characters within the URL */
+
+ const char *pURLScan;
+ sal_Int32 nURLScanLen;
+ sal_Int32 nURLScanCount;
+
+ OString sUTF8 = OUStringToOString(sURL, RTL_TEXTENCODING_UTF8);
+
+ OUStringBuffer sEncodedURL(sUTF8.getLength() * 3 + 1);
+ pURLScan = sUTF8.getStr();
+ nURLScanLen = sUTF8.getLength();
+ nURLScanCount = 0;
+
+ while ( nURLScanCount < nURLScanLen )
+ {
+ char cCurrent = *pURLScan;
+ switch ( cCurrent )
+ {
+ default:
+ if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
+ {
+ char buf[3];
+ sprintf( buf, "%02X", static_cast<unsigned char>(cCurrent) );
+ sEncodedURL.append('%').appendAscii(buf, 2);
+ break;
+ }
+ [[fallthrough]];
+ case '!':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '-':
+ case '.':
+ case '_':
+ case '~':
+ case '$':
+ case '&':
+ case '+':
+ case ',':
+ case '=':
+ case '@':
+ case ':':
+ case '/':
+ case '\\':
+ case '|':
+ sEncodedURL.appendAscii(&cCurrent, 1);
+ break;
+ case 0:
+ break;
+ }
+
+ pURLScan++;
+ nURLScanCount++;
+ }
+
+ return sEncodedURL.makeStringAndClear();
+}
+
+// A helper that makes sure that for existing part of the path, the case is correct.
+// Unlike GetLongPathNameW that it wraps, this function does not require the path to exist.
+static OUString GetCaseCorrectPathName(std::u16string_view sysPath)
+{
+ // Prepare a null-terminated string first.
+ // Neither OUString, nor u16string_view are guaranteed to be null-terminated
+ osl::LongPathBuffer<wchar_t> szPath(sysPath.size() + WSTR_LONG_PATH_PREFIX_UNC.size() + 1);
+ wchar_t* const pPath = szPath;
+ wchar_t* pEnd = pPath;
+ size_t sysPathOffset = 0;
+ if (sysPath.size() >= MAX_PATH && isAbsolute(sysPath)
+ && !o3tl::starts_with(sysPath, WSTR_LONG_PATH_PREFIX))
+ {
+ // Allow GetLongPathNameW consume long paths
+ std::u16string_view prefix = WSTR_LONG_PATH_PREFIX;
+ if (startsWithSlashSlash(sysPath))
+ {
+ sysPathOffset = 2; // skip leading "\\"
+ prefix = WSTR_LONG_PATH_PREFIX_UNC;
+ }
+ pEnd = std::copy(prefix.begin(), prefix.end(), pEnd);
+ }
+ wchar_t* const pStart = pEnd;
+ pEnd = std::copy(sysPath.begin() + sysPathOffset, sysPath.end(), pStart);
+ *pEnd = 0;
+ osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
+ while (pEnd > pStart)
+ {
+ std::u16string_view curPath(o3tl::toU(pPath), pEnd - pPath);
+ if (curPath == u"\\\\" || curPath == WSTR_SYSTEM_ROOT_PATH
+ || curPath == WSTR_LONG_PATH_PREFIX
+ || o3tl::equalsIgnoreAsciiCase(curPath, WSTR_LONG_PATH_PREFIX_UNC))
+ break; // Do not check if the special path prefix exists itself
+
+ DWORD nNewLen = GetLongPathNameW(pPath, aBuf, aBuf.getBufSizeInSymbols());
+ if (nNewLen == 0)
+ {
+ // Error?
+ const DWORD err = GetLastError();
+ if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ {
+ // Check the base path; skip possible trailing separator
+ size_t sepPos = curPath.substr(0, curPath.size() - 1).rfind(u'\\');
+ if (sepPos != std::u16string_view::npos)
+ {
+ pEnd = pPath + sepPos;
+ *pEnd = 0;
+ continue;
+ }
+ }
+ else
+ {
+ SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code "
+ << err << " processing path " << OUString(curPath));
+ }
+ break; // All other errors, or no separators left
+ }
+ assert(nNewLen < aBuf.getBufSizeInSymbols());
+ // Combine the case-correct leading part with the non-existing trailing part
+ return OUString::Concat(std::u16string_view(o3tl::toU(aBuf), nNewLen))
+ + sysPath.substr(pEnd - pStart + sysPathOffset);
+ };
+ return OUString(sysPath); // We found no existing parts - just assume it's OK
+}
+
+oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString **pustrPath, bool bAllowRelative)
+{
+ OUString sTempPath;
+ oslFileError nError = osl_File_E_INVAL; /* Assume failure */
+
+ /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
+ having a mixed encoded URL later */
+
+ OString sUTF8 = OUStringToOString(strURL, RTL_TEXTENCODING_UTF8);
+
+ /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
+
+ SAL_WARN_IF(
+ sUTF8.getLength() != strURL.getLength() &&
+ strURL.matchIgnoreAsciiCase("file:\\")
+ , "sal.osl"
+ ,"osl_getSystemPathFromFileURL: \"" << strURL << "\" is not encoded !!!");
+
+ if (auto sDecodedURL = osl_decodeURL_(sUTF8))
+ {
+ /* Replace backslashes and pipes */
+
+ sDecodedURL = sDecodedURL->replace('/', '\\').replace('|', ':');
+
+ /* Must start with "file:/" */
+ if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\") )
+ {
+ sal_uInt32 nSkip;
+
+ if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\\\") )
+ nSkip = 8;
+ else if (
+ sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\localhost\\") ||
+ sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\127.0.0.1\\")
+ )
+ nSkip = 17;
+ else if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\") )
+ nSkip = 5;
+ else
+ nSkip = 6;
+
+ const sal_uInt32 nDecodedLen = sDecodedURL->getLength();
+
+ /* Indicates local root */
+ if ( nDecodedLen == nSkip )
+ sTempPath = WSTR_SYSTEM_ROOT_PATH;
+ else
+ {
+ /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
+ if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
+ {
+ sTempPath = sDecodedURL->subView(nSkip);
+ }
+ else
+ {
+ sDecodedURL = GetCaseCorrectPathName(sDecodedURL->subView(nSkip));
+ if (sDecodedURL->getLength() <= MAX_PATH - 12
+ || sDecodedURL->startsWith(WSTR_SYSTEM_ROOT_PATH)
+ || sDecodedURL->startsWith(WSTR_LONG_PATH_PREFIX))
+ {
+ sTempPath = *sDecodedURL;
+ }
+ else if (sDecodedURL->startsWith("\\\\"))
+ {
+ /* it should be an UNC path, use the according prefix */
+ sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + sDecodedURL->subView(2);
+ }
+ else
+ {
+ sTempPath = WSTR_LONG_PATH_PREFIX + *sDecodedURL;
+ }
+ }
+ }
+
+ if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
+ nError = osl_File_E_None;
+ }
+ else if ( bAllowRelative ) /* This maybe a relative file URL */
+ {
+ /* In future the relative path could be converted to absolute if it is too long */
+ sTempPath = *sDecodedURL;
+
+ if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
+ nError = osl_File_E_None;
+ }
+ else
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not an absolute FileURL");
+
+ }
+
+ if ( osl_File_E_None == nError )
+ rtl_uString_assign(pustrPath, sTempPath.pData);
+
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not a FileURL");
+
+ return nError;
+}
+
+oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
+{
+ oslFileError nError = osl_File_E_INVAL; /* Assume failure */
+ OUString sTempURL;
+ DWORD dwPathType = PATHTYPE_ERROR;
+
+ if (strPath)
+ dwPathType = IsValidFilePath(OUString::unacquired(&strPath), VALIDATEPATH_ALLOW_RELATIVE, nullptr);
+
+ if (dwPathType)
+ {
+ OUString sTempPath;
+ const OUString& sPath = OUString::unacquired(&strPath);
+
+ if ( dwPathType & PATHTYPE_IS_LONGPATH )
+ {
+ /* the path has the longpath prefix, lets remove it */
+ switch ( dwPathType & PATHTYPE_MASK_TYPE )
+ {
+ case PATHTYPE_ABSOLUTE_UNC:
+ static_assert(WSTR_LONG_PATH_PREFIX_UNC.size() == 8,
+ "Unexpected long path UNC prefix!");
+
+ /* generate the normal UNC path */
+ sTempPath = "\\\\" + sPath.copy(8).replace('\\', '/');
+ break;
+
+ case PATHTYPE_ABSOLUTE_LOCAL:
+ static_assert(WSTR_LONG_PATH_PREFIX.size() == 4,
+ "Unexpected long path prefix!");
+
+ /* generate the normal path */
+ sTempPath = sPath.copy(4).replace('\\', '/');
+ break;
+
+ default:
+ OSL_FAIL( "Unexpected long path format!" );
+ sTempPath = sPath.replace('\\', '/');
+ break;
+ }
+ }
+ else
+ {
+ /* Replace backslashes */
+ sTempPath = sPath.replace('\\', '/');
+ }
+
+ switch ( dwPathType & PATHTYPE_MASK_TYPE )
+ {
+ case PATHTYPE_RELATIVE:
+ sTempURL = sTempPath;
+ nError = osl_File_E_None;
+ break;
+ case PATHTYPE_ABSOLUTE_UNC:
+ sTempURL = "file:" + sTempPath;
+ nError = osl_File_E_None;
+ break;
+ case PATHTYPE_ABSOLUTE_LOCAL:
+ sTempURL = "file:///" + sTempPath;
+ nError = osl_File_E_None;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ( osl_File_E_None == nError )
+ {
+ /* Encode the URL */
+ rtl_uString_assign(pstrURL, osl_encodeURL_(sTempURL).pData);
+ OSL_ASSERT(*pstrURL != nullptr);
+ }
+
+ SAL_INFO_IF(nError, "sal.osl",
+ "osl_getFileURLFromSystemPath: \"" << OUString::unacquired(&strPath) << "\" is not a systemPath");
+ return nError;
+}
+
+oslFileError SAL_CALL osl_getSystemPathFromFileURL(
+ rtl_uString *ustrURL, rtl_uString **pustrPath)
+{
+ return osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), pustrPath, true);
+}
+
+oslFileError SAL_CALL osl_searchFileURL(
+ rtl_uString *ustrFileName,
+ rtl_uString *ustrSystemSearchPath,
+ rtl_uString **pustrPath)
+{
+ OUString ustrUNCPath;
+ OUString ustrSysPath;
+ oslFileError error;
+
+ /* First try to interpret the file name as a URL even a relative one */
+ error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileName), &ustrUNCPath.pData, true);
+
+ /* So far we either have an UNC path or something invalid
+ Now create a system path */
+ if ( osl_File_E_None == error )
+ error = osl_getSystemPathFromFileURL_(ustrUNCPath, &ustrSysPath.pData, true);
+
+ if ( osl_File_E_None == error )
+ {
+ DWORD nBufferLength;
+ DWORD dwResult;
+ LPWSTR lpBuffer = nullptr;
+ LPWSTR lpszFilePart;
+
+ /* Repeat calling SearchPath ...
+ Start with MAX_PATH for the buffer. In most cases this
+ will be enough and does not force the loop to run twice */
+ dwResult = MAX_PATH;
+
+ do
+ {
+ /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
+ LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr;
+ LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath.getStr());
+
+ /* Allocate space for buffer according to previous returned count of required chars */
+ /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
+ nBufferLength = dwResult + 1;
+ lpBuffer = lpBuffer ?
+ static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) :
+ static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR)));
+
+ dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart );
+ } while ( dwResult && dwResult >= nBufferLength );
+
+ /* ... until an error occurs or buffer is large enough.
+ dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
+
+ if ( dwResult )
+ {
+ ustrSysPath = o3tl::toU(lpBuffer);
+ error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
+ }
+ else
+ {
+ WIN32_FIND_DATAW aFindFileData;
+ HANDLE hFind;
+
+ /* something went wrong, perhaps the path was absolute */
+ error = oslTranslateFileError( GetLastError() );
+
+ hFind = FindFirstFileW(o3tl::toW(ustrSysPath.getStr()), &aFindFileData);
+
+ if ( IsValidHandle(hFind) )
+ {
+ error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
+ FindClose( hFind );
+ }
+ }
+
+ free( lpBuffer );
+ }
+
+ return error;
+}
+
+oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
+{
+ oslFileError eError = osl_File_E_None;
+ OUString ustrRelSysPath;
+ OUString ustrBaseSysPath;
+
+ if ( ustrBaseURL && ustrBaseURL->length )
+ {
+ eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrBaseURL), &ustrBaseSysPath.pData, false);
+ OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
+ }
+ if (eError == osl_File_E_None)
+ {
+ eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrRelativeURL), &ustrRelSysPath.pData,
+ !ustrBaseSysPath.isEmpty());
+ OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
+ }
+
+ if ( !eError )
+ {
+ OUString sResultPath;
+/*@@@ToDo
+ The whole FileURL implementation should be merged
+ with the rtl/uri class.
+*/
+ // If ustrRelSysPath is absolute, we don't need ustrBaseSysPath.
+ if (!ustrBaseSysPath.isEmpty() && !isAbsolute(ustrRelSysPath))
+ {
+ // ustrBaseSysPath is known here to be a valid absolute path -> its first two characters
+ // are ASCII (either alpha + colon, or double backslashes)
+
+ // Don't use SetCurrentDirectoryW together with GetFullPathNameW, because:
+ // (a) it needs synchronization and may affect threads that may access relative paths;
+ // (b) it would give wrong results for non-existing base path (allowed by RFC2396).
+
+ if (startsWithDriveColon(ustrRelSysPath))
+ {
+ // Special case: a path relative to a specific drive's current directory.
+ // Should we error out here?
+
+ // If ustrBaseSysPath is on the same drive as ustrRelSysPath, then take base path
+ // as is; otherwise, use current directory on ustrRelSysPath's drive as base path
+ if (onSameDrive(ustrRelSysPath, ustrBaseSysPath))
+ {
+ sResultPath = combinePath(ustrBaseSysPath, ustrRelSysPath.subView(2));
+ }
+ else
+ {
+ // Call GetFullPathNameW to get current directory on ustrRelSysPath's drive
+ wchar_t baseDrive[3] = { ustrRelSysPath[0], ':' }; // just "C:"
+ osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
+ DWORD dwResult
+ = GetFullPathNameW(baseDrive, aBuf.getBufSizeInSymbols(), aBuf, nullptr);
+ if (dwResult)
+ {
+ if (dwResult >= aBuf.getBufSizeInSymbols())
+ eError = osl_File_E_INVAL;
+ else
+ sResultPath = combinePath(o3tl::toU(aBuf), ustrRelSysPath.subView(2));
+ }
+ else
+ eError = oslTranslateFileError(GetLastError());
+ }
+ }
+ else
+ {
+ // Is this a rooted relative path (starting with a backslash)?
+ // Then we need only root from base. E.g.,
+ // ustrBaseSysPath is "\\server\share\path1\" and ustrRelSysPath is "\path2\to\file"
+ // => \\server\share\path2\to\file
+ // ustrBaseSysPath is "D:\path1\" and ustrRelSysPath is "\path2\to\file"
+ // => D:\path2\to\file
+ auto sBaseView(pathView(ustrBaseSysPath, ustrRelSysPath.startsWith("\\")));
+ sResultPath = combinePath(sBaseView, ustrRelSysPath);
+ }
+ }
+ else
+ sResultPath = ustrRelSysPath;
+
+ if (eError == osl_File_E_None)
+ {
+ sResultPath = removeRelativeParts(sResultPath);
+ eError = osl_getFileURLFromSystemPath(sResultPath.pData, pustrAbsoluteURL);
+ }
+ }
+
+ return eError;
+}
+
+oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
+{
+ rtl_uString_newFromString(strValid, strRequested);
+ return osl_File_E_None;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */