/* -*- 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 .
*/
// 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);
}
staticbool 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;
} elseif ( 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 elseif (lpComponentEnd == roComponent->end())
roComponent.reset(); else
roComponent->remove_prefix(lpComponentEnd - roComponent->begin());
}
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;
} elseif (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;
}
} elseif ( 2 == countInitialSeparators(path) )
{ /* The UNC path notation */
oComponent = path.subView(2);
dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
} elseif (startsWithDriveColon(path))
{ /* Local path verification. Must start with <drive>: */
oComponent = path.subView(2);
dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
}
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;
}
/* Now decode the URL what should result in a UTF-8 string */ while ( bValidEncoded && pSrc < pSrcEnd )
{ switch ( *pSrc )
{ case'%':
{ char aToken[3]; char aChar;
// 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(EXTENDED_MAX_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
}
/* 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;
} elseif (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;
} elseif ( 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");
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, let's 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;
/* 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);
/* 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)));
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], ':', 0 }; // just "C:"
osl::LongPathBuffer<wchar_t> aBuf(EXTENDED_MAX_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;
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.