tdf#154546 skip dispatch when presenter controller is not set
[LibreOffice.git] / sal / osl / w32 / file_url.cxx
bloba14f8d4b9b391a7bbb30a216d805bb5e01a64d24
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <algorithm>
24 #include <optional>
25 #include <stack>
26 #include <string_view>
28 #include <systools/win32/uwinapi.h>
30 #include "file_url.hxx"
31 #include "file_error.hxx"
33 #include <rtl/alloc.h>
34 #include <rtl/character.hxx>
35 #include <rtl/strbuf.hxx>
36 #include <rtl/ustring.hxx>
37 #include <rtl/ustrbuf.hxx>
38 #include <osl/mutex.h>
39 #include <o3tl/char16_t2wchar_t.hxx>
40 #include <o3tl/string_view.hxx>
42 #include "path_helper.hxx"
44 // FileURL functions
46 namespace
48 constexpr std::u16string_view WSTR_SYSTEM_ROOT_PATH = u"\\\\.\\";
49 constexpr std::u16string_view WSTR_LONG_PATH_PREFIX = u"\\\\?\\";
50 constexpr std::u16string_view WSTR_LONG_PATH_PREFIX_UNC = u"\\\\?\\UNC\\";
52 // Internal functions that expect only backslashes as path separators
54 bool startsWithDriveColon(std::u16string_view s)
56 return s.length() >= 2 && rtl::isAsciiAlpha(s[0]) && s[1] == ':';
59 bool startsWithDriveColonSlash(std::u16string_view s)
61 return s.length() >= 3 && startsWithDriveColon(s) && s[2] == '\\';
64 bool startsWithSlashSlash(std::u16string_view s) { return o3tl::starts_with(s, u"\\\\"); }
66 // An absolute path starts either with \\ (an UNC or device path like \\.\ or \\?\)
67 // or with a ASCII alpha character followed by colon followed by backslash.
68 bool isAbsolute(std::u16string_view s)
70 return startsWithSlashSlash(s) || startsWithDriveColonSlash(s);
73 bool onSameDrive(std::u16string_view s1, std::u16string_view s2)
75 assert(startsWithDriveColon(s1) && startsWithDriveColon(s2));
76 return rtl::toAsciiUpperCase(s1[0]) == rtl::toAsciiUpperCase(s2[0]) && s1[1] == s2[1];
79 sal_Int32 getRootLength(std::u16string_view path)
81 assert(isAbsolute(path));
82 size_t nResult = 0;
83 if (startsWithSlashSlash(path))
85 // Cases:
86 // 1. Device UNC: \\?\UNC\server\share or \\.\UNC\server\share
87 // 2. Non-device UNC: \\server\share
88 // 3. Device non-UNC: \\?\C: or \\.\C:
89 bool bUNC = false;
90 if (path.length() > 3 && (path[2] == '.' || path[2] == '?') && path[3] == '\\')
92 if (path.substr(4, 4) == u"UNC\\")
94 // \\?\UNC\server\share or \\.\UNC\server\share
95 nResult = 8;
96 bUNC = true;
98 else
100 // \\?\C: or \\.\C:
101 assert(startsWithDriveColon(path.substr(4)));
102 nResult = 6;
105 else
107 // \\server\share
108 nResult = 2;
109 bUNC = true;
111 if (bUNC)
113 // \\?\UNC\server\share or \\.\UNC\server\share or \\server\share
114 assert(nResult < path.length() && path[nResult] != '\\');
115 // Skip server name and share name
116 for (int nSlashes = 0; nResult < path.length(); ++nResult)
118 if (path[nResult] == '\\' && ++nSlashes == 2)
119 break;
123 else
125 // C:
126 assert(startsWithDriveColon(path));
127 nResult = 2;
129 return std::min(nResult, path.length());
132 std::u16string_view pathView(std::u16string_view path, bool bOnlyRoot)
134 return bOnlyRoot ? path.substr(0, getRootLength(path)) : path;
137 OUString combinePath(std::u16string_view basePath, std::u16string_view relPath)
139 const bool needSep = !o3tl::ends_with(basePath, u'\\');
140 const auto sSeparator = needSep ? std::u16string_view(u"\\") : std::u16string_view();
141 if (o3tl::starts_with(relPath, u'\\'))
142 relPath.remove_prefix(1); // avoid two adjacent backslashes
143 return OUString::Concat(basePath) + sSeparator + relPath;
146 OUString removeRelativeParts(const OUString& p)
148 const sal_Int32 rootPos = getRootLength(p);
149 OUStringBuffer buf(p.getLength());
150 buf.append(p.subView(0, rootPos));
151 std::stack<sal_Int32> partPositions;
152 bool bAfterSlash = false;
153 for (sal_Int32 i = rootPos; i < p.getLength(); ++i)
155 sal_Unicode c = p[i];
156 if (c == '\\')
158 if (i + 1 < p.getLength() && p[i + 1] == '.')
160 if (i + 2 == p.getLength() || p[i + 2] == '\\')
162 // 1. Skip current directory (\.\ or trailing \.)
163 ++i; // process next slash: it may start another "\.\"
165 else if (p[i + 2] == '.' && (i + 3 == p.getLength() || p[i + 3] == '\\'))
167 // 2. For parent directory (\..\), drop previous part and skip
168 if (bAfterSlash && partPositions.size())
169 partPositions.pop();
170 sal_Int32 nParentPos = partPositions.size() ? partPositions.top() : rootPos;
171 if (partPositions.size())
172 partPositions.pop();
173 buf.truncate(nParentPos);
174 bAfterSlash = false; // we have just removed slash after parent part
175 i += 2; // process next slash: it may start another "\.\"
178 if (bAfterSlash)
179 continue; // 3. Skip double backslashes (\\)
180 partPositions.push(buf.getLength());
181 bAfterSlash = true;
183 else
184 bAfterSlash = false;
186 buf.append(c);
188 return buf.makeStringAndClear();
192 static bool IsValidFilePathComponent(
193 std::optional<std::u16string_view>& roComponent,
194 DWORD dwFlags)
196 assert(roComponent);
197 auto lpComponentEnd = roComponent->end();
198 auto lpCurrent = roComponent->begin();
199 bool bValid = lpCurrent != lpComponentEnd; // Empty components are not allowed
200 sal_Unicode cLast = 0;
202 while (bValid)
204 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
205 if (lpCurrent - roComponent->begin() >= MAX_PATH)
207 bValid = false;
208 break;
211 switch ( *lpCurrent )
213 /* Both backslash and slash determine the end of a path component */
214 case '\0':
215 case '/':
216 case '\\':
217 switch ( cLast )
219 /* Component must not end with '.' or blank and can't be empty */
221 case '.':
222 if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
224 if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
225 1 == lpCurrent - roComponent->begin() )
227 /* Either do allow periods anywhere, or current directory */
228 lpComponentEnd = lpCurrent;
229 break;
231 else if ( 2 == lpCurrent - roComponent->begin() && '.' == roComponent->front() )
233 /* Parent directory is O.K. */
234 lpComponentEnd = lpCurrent;
235 break;
238 [[fallthrough]];
239 case 0:
240 case ' ':
241 if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
242 lpComponentEnd = lpCurrent;
243 else
244 bValid = false;
245 break;
246 default:
247 lpComponentEnd = lpCurrent;
248 break;
250 break;
251 /* The following characters are reserved */
252 case '?':
253 case '*':
254 case '<':
255 case '>':
256 case '\"':
257 case '|':
258 case ':':
259 bValid = false;
260 break;
261 default:
262 /* Characters below ASCII 32 are not allowed */
263 if ( *lpCurrent < ' ' )
264 bValid = false;
265 break;
268 if (lpCurrent != lpComponentEnd)
269 cLast = *lpCurrent++;
271 if (lpCurrent == lpComponentEnd)
272 break;
275 if ( bValid )
277 // Empty components are not allowed
278 if (lpComponentEnd - roComponent->begin() < 1)
279 bValid = false;
280 // If we reached the end of the string nullopt is returned
281 else if (lpComponentEnd == roComponent->end())
282 roComponent.reset();
283 else
284 roComponent->remove_prefix(lpComponentEnd - roComponent->begin());
287 return bValid;
290 static sal_Int32 countInitialSeparators(std::u16string_view path) {
291 size_t n = 0;
292 while (n < path.length() && (path[n] == '\\' || path[n] == '/'))
293 ++n;
294 return n;
297 DWORD IsValidFilePath(const OUString& path, DWORD dwFlags, OUString* corrected)
299 std::optional<std::u16string_view> oComponent = path;
300 bool bValid = true;
301 DWORD dwPathType = PATHTYPE_ERROR;
303 if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
304 dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
306 DWORD dwCandidatPathType = PATHTYPE_ERROR;
308 if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX_UNC))
310 /* This is long path in UNC notation */
311 oComponent = path.subView(WSTR_LONG_PATH_PREFIX_UNC.size());
312 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
314 else if (path.matchIgnoreAsciiCase(WSTR_LONG_PATH_PREFIX))
316 /* This is long path */
317 oComponent = path.subView(WSTR_LONG_PATH_PREFIX.size());
319 if (startsWithDriveColon(*oComponent))
321 oComponent->remove_prefix(2);
322 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
325 else if ( 2 == countInitialSeparators(path) )
327 /* The UNC path notation */
328 oComponent = path.subView(2);
329 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
331 else if (startsWithDriveColon(path))
333 /* Local path verification. Must start with <drive>: */
334 oComponent = path.subView(2);
335 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
338 if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
340 bValid = IsValidFilePathComponent(oComponent, VALIDATEPATH_ALLOW_ELLIPSE);
342 /* So far we have a valid servername. Now let's see if we also have a network resource */
344 dwPathType = dwCandidatPathType;
346 if ( bValid )
348 if (oComponent)
350 oComponent->remove_prefix(1);
351 if (oComponent->empty())
352 oComponent.reset();
355 if (!oComponent)
357 dwPathType |= PATHTYPE_IS_SERVER;
359 else
361 /* Now test the network resource */
363 bValid = IsValidFilePathComponent(oComponent, 0);
365 /* If we now reached the end of the path, everything is O.K. */
367 if (bValid)
369 if (oComponent)
371 oComponent->remove_prefix(1);
372 if (oComponent->empty())
373 oComponent.reset();
375 if (!oComponent)
376 dwPathType |= PATHTYPE_IS_VOLUME;
381 else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
383 if (1 == countInitialSeparators(*oComponent))
384 oComponent->remove_prefix(1);
385 else if (!oComponent->empty())
386 bValid = false;
388 dwPathType = dwCandidatPathType;
390 /* Now we are behind the backslash or it was a simple drive without backslash */
392 if (bValid && oComponent->empty())
394 oComponent.reset();
395 dwPathType |= PATHTYPE_IS_VOLUME;
398 else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
400 /* Can be a relative path */
401 oComponent = path;
403 /* Relative path can start with a backslash */
405 if (1 == countInitialSeparators(*oComponent))
407 oComponent->remove_prefix(1);
408 if (oComponent->empty())
409 oComponent.reset();
412 dwPathType = PATHTYPE_RELATIVE;
414 else
416 /* Anything else is an error */
417 bValid = false;
420 /* Now validate each component of the path */
421 OUString lastCorrected = path;
422 while (bValid && oComponent)
424 // Correct path by merging consecutive slashes:
425 if (o3tl::starts_with(*oComponent, u"\\") && corrected != nullptr) {
426 sal_Int32 i = oComponent->data() - lastCorrected.getStr();
427 *corrected = lastCorrected.replaceAt(i, 1, {});
428 //TODO: handle out-of-memory
429 lastCorrected = *corrected;
430 oComponent = lastCorrected.subView(i);
433 bValid = IsValidFilePathComponent(oComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
435 if (bValid && oComponent)
437 oComponent->remove_prefix(1);
439 /* If the string behind the backslash is empty, we've done */
441 if (oComponent->empty())
442 oComponent.reset();
446 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
447 if (bValid && !(dwPathType & PATHTYPE_IS_LONGPATH) && path.getLength() >= MAX_PATH)
449 bValid = false;
452 return bValid ? dwPathType : PATHTYPE_ERROR;
455 static std::optional<OUString> osl_decodeURL_(const OString& sUTF8)
457 const char *pSrcEnd;
458 const char *pSrc;
459 bool bValidEncoded = true; /* Assume success */
461 /* The resulting decoded string length is shorter or equal to the source length */
463 const sal_Int32 nSrcLen = sUTF8.getLength();
464 OStringBuffer aBuffer(nSrcLen + 1);
466 pSrc = sUTF8.getStr();
467 pSrcEnd = pSrc + nSrcLen;
469 /* Now decode the URL what should result in a UTF-8 string */
470 while ( bValidEncoded && pSrc < pSrcEnd )
472 switch ( *pSrc )
474 case '%':
476 char aToken[3];
477 char aChar;
479 pSrc++;
480 aToken[0] = *pSrc++;
481 aToken[1] = *pSrc++;
482 aToken[2] = 0;
484 aChar = static_cast<char>(strtoul( aToken, nullptr, 16 ));
486 /* The chars are path delimiters and must not be encoded */
488 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
489 bValidEncoded = false;
490 else
491 aBuffer.append(aChar);
493 break;
494 case '\0':
495 case '#':
496 case '?':
497 bValidEncoded = false;
498 break;
499 default:
500 aBuffer.append(*pSrc++);
501 break;
505 if (!bValidEncoded)
506 return std::nullopt;
508 return OStringToOUString(aBuffer, RTL_TEXTENCODING_UTF8);
511 static OUString osl_encodeURL_(std::u16string_view sURL)
513 /* Encode non ascii characters within the URL */
515 const char *pURLScan;
516 sal_Int32 nURLScanLen;
517 sal_Int32 nURLScanCount;
519 OString sUTF8 = OUStringToOString(sURL, RTL_TEXTENCODING_UTF8);
521 OUStringBuffer sEncodedURL(sUTF8.getLength() * 3 + 1);
522 pURLScan = sUTF8.getStr();
523 nURLScanLen = sUTF8.getLength();
524 nURLScanCount = 0;
526 while ( nURLScanCount < nURLScanLen )
528 char cCurrent = *pURLScan;
529 switch ( cCurrent )
531 default:
532 if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
534 char buf[3];
535 sprintf( buf, "%02X", static_cast<unsigned char>(cCurrent) );
536 sEncodedURL.append('%').appendAscii(buf, 2);
537 break;
539 [[fallthrough]];
540 case '!':
541 case '\'':
542 case '(':
543 case ')':
544 case '*':
545 case '-':
546 case '.':
547 case '_':
548 case '~':
549 case '$':
550 case '&':
551 case '+':
552 case ',':
553 case '=':
554 case '@':
555 case ':':
556 case '/':
557 case '\\':
558 case '|':
559 sEncodedURL.appendAscii(&cCurrent, 1);
560 break;
561 case 0:
562 break;
565 pURLScan++;
566 nURLScanCount++;
569 return sEncodedURL.makeStringAndClear();
572 // A helper that makes sure that for existing part of the path, the case is correct.
573 // Unlike GetLongPathNameW that it wraps, this function does not require the path to exist.
574 static OUString GetCaseCorrectPathName(std::u16string_view sysPath)
576 // Prepare a null-terminated string first.
577 // Neither OUString, nor u16string_view are guaranteed to be null-terminated
578 osl::LongPathBuffer<wchar_t> szPath(sysPath.size() + WSTR_LONG_PATH_PREFIX_UNC.size() + 1);
579 wchar_t* const pPath = szPath;
580 wchar_t* pEnd = pPath;
581 size_t sysPathOffset = 0;
582 if (sysPath.size() >= MAX_PATH && isAbsolute(sysPath)
583 && !o3tl::starts_with(sysPath, WSTR_LONG_PATH_PREFIX))
585 // Allow GetLongPathNameW consume long paths
586 std::u16string_view prefix = WSTR_LONG_PATH_PREFIX;
587 if (startsWithSlashSlash(sysPath))
589 sysPathOffset = 2; // skip leading "\\"
590 prefix = WSTR_LONG_PATH_PREFIX_UNC;
592 pEnd = std::copy(prefix.begin(), prefix.end(), pEnd);
594 wchar_t* const pStart = pEnd;
595 pEnd = std::copy(sysPath.begin() + sysPathOffset, sysPath.end(), pStart);
596 *pEnd = 0;
597 osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
598 while (pEnd > pStart)
600 std::u16string_view curPath(o3tl::toU(pPath), pEnd - pPath);
601 if (curPath == u"\\\\" || curPath == WSTR_SYSTEM_ROOT_PATH
602 || curPath == WSTR_LONG_PATH_PREFIX
603 || o3tl::equalsIgnoreAsciiCase(curPath, WSTR_LONG_PATH_PREFIX_UNC))
604 break; // Do not check if the special path prefix exists itself
606 DWORD nNewLen = GetLongPathNameW(pPath, aBuf, aBuf.getBufSizeInSymbols());
607 if (nNewLen == 0)
609 // Error?
610 const DWORD err = GetLastError();
611 if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
613 // Check the base path; skip possible trailing separator
614 size_t sepPos = curPath.substr(0, curPath.size() - 1).rfind(u'\\');
615 if (sepPos != std::u16string_view::npos)
617 pEnd = pPath + sepPos;
618 *pEnd = 0;
619 continue;
622 else
624 SAL_WARN("sal.osl", "GetLongPathNameW: Windows error code "
625 << err << " processing path " << OUString(curPath));
627 break; // All other errors, or no separators left
629 assert(nNewLen < aBuf.getBufSizeInSymbols());
630 // Combine the case-correct leading part with the non-existing trailing part
631 return OUString::Concat(std::u16string_view(o3tl::toU(aBuf), nNewLen))
632 + sysPath.substr(pEnd - pStart + sysPathOffset);
634 return OUString(sysPath); // We found no existing parts - just assume it's OK
637 oslFileError osl_getSystemPathFromFileURL_(const OUString& strURL, rtl_uString **pustrPath, bool bAllowRelative)
639 OUString sTempPath;
640 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
642 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
643 having a mixed encoded URL later */
645 OString sUTF8 = OUStringToOString(strURL, RTL_TEXTENCODING_UTF8);
647 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
649 SAL_WARN_IF(
650 sUTF8.getLength() != strURL.getLength() &&
651 strURL.matchIgnoreAsciiCase("file:\\")
652 , "sal.osl"
653 ,"osl_getSystemPathFromFileURL: \"" << strURL << "\" is not encoded !!!");
655 if (auto sDecodedURL = osl_decodeURL_(sUTF8))
657 /* Replace backslashes and pipes */
659 sDecodedURL = sDecodedURL->replace('/', '\\').replace('|', ':');
661 /* Must start with "file:/" */
662 if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\") )
664 sal_uInt32 nSkip;
666 if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\\\") )
667 nSkip = 8;
668 else if (
669 sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\localhost\\") ||
670 sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\127.0.0.1\\")
672 nSkip = 17;
673 else if ( sDecodedURL->startsWithIgnoreAsciiCase("file:\\\\") )
674 nSkip = 5;
675 else
676 nSkip = 6;
678 const sal_uInt32 nDecodedLen = sDecodedURL->getLength();
680 /* Indicates local root */
681 if ( nDecodedLen == nSkip )
682 sTempPath = WSTR_SYSTEM_ROOT_PATH;
683 else
685 /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
686 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
688 sTempPath = sDecodedURL->subView(nSkip);
690 else
692 sDecodedURL = GetCaseCorrectPathName(sDecodedURL->subView(nSkip));
693 if (sDecodedURL->getLength() <= MAX_PATH - 12
694 || sDecodedURL->startsWith(WSTR_SYSTEM_ROOT_PATH)
695 || sDecodedURL->startsWith(WSTR_LONG_PATH_PREFIX))
697 sTempPath = *sDecodedURL;
699 else if (sDecodedURL->startsWith("\\\\"))
701 /* it should be an UNC path, use the according prefix */
702 sTempPath = OUString::Concat(WSTR_LONG_PATH_PREFIX_UNC) + sDecodedURL->subView(2);
704 else
706 sTempPath = WSTR_LONG_PATH_PREFIX + *sDecodedURL;
711 if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
712 nError = osl_File_E_None;
714 else if ( bAllowRelative ) /* This maybe a relative file URL */
716 /* In future the relative path could be converted to absolute if it is too long */
717 sTempPath = *sDecodedURL;
719 if (IsValidFilePath(sTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &sTempPath))
720 nError = osl_File_E_None;
722 else
723 SAL_INFO_IF(nError, "sal.osl",
724 "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not an absolute FileURL");
728 if ( osl_File_E_None == nError )
729 rtl_uString_assign(pustrPath, sTempPath.pData);
731 SAL_INFO_IF(nError, "sal.osl",
732 "osl_getSystemPathFromFileURL: \"" << strURL << "\" is not a FileURL");
734 return nError;
737 oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
739 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
740 OUString sTempURL;
741 DWORD dwPathType = PATHTYPE_ERROR;
743 if (strPath)
744 dwPathType = IsValidFilePath(OUString::unacquired(&strPath), VALIDATEPATH_ALLOW_RELATIVE, nullptr);
746 if (dwPathType)
748 OUString sTempPath;
749 const OUString& sPath = OUString::unacquired(&strPath);
751 if ( dwPathType & PATHTYPE_IS_LONGPATH )
753 /* the path has the longpath prefix, lets remove it */
754 switch ( dwPathType & PATHTYPE_MASK_TYPE )
756 case PATHTYPE_ABSOLUTE_UNC:
757 static_assert(WSTR_LONG_PATH_PREFIX_UNC.size() == 8,
758 "Unexpected long path UNC prefix!");
760 /* generate the normal UNC path */
761 sTempPath = "\\\\" + sPath.copy(8).replace('\\', '/');
762 break;
764 case PATHTYPE_ABSOLUTE_LOCAL:
765 static_assert(WSTR_LONG_PATH_PREFIX.size() == 4,
766 "Unexpected long path prefix!");
768 /* generate the normal path */
769 sTempPath = sPath.copy(4).replace('\\', '/');
770 break;
772 default:
773 OSL_FAIL( "Unexpected long path format!" );
774 sTempPath = sPath.replace('\\', '/');
775 break;
778 else
780 /* Replace backslashes */
781 sTempPath = sPath.replace('\\', '/');
784 switch ( dwPathType & PATHTYPE_MASK_TYPE )
786 case PATHTYPE_RELATIVE:
787 sTempURL = sTempPath;
788 nError = osl_File_E_None;
789 break;
790 case PATHTYPE_ABSOLUTE_UNC:
791 sTempURL = "file:" + sTempPath;
792 nError = osl_File_E_None;
793 break;
794 case PATHTYPE_ABSOLUTE_LOCAL:
795 sTempURL = "file:///" + sTempPath;
796 nError = osl_File_E_None;
797 break;
798 default:
799 break;
803 if ( osl_File_E_None == nError )
805 /* Encode the URL */
806 rtl_uString_assign(pstrURL, osl_encodeURL_(sTempURL).pData);
807 OSL_ASSERT(*pstrURL != nullptr);
810 SAL_INFO_IF(nError, "sal.osl",
811 "osl_getFileURLFromSystemPath: \"" << OUString::unacquired(&strPath) << "\" is not a systemPath");
812 return nError;
815 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
816 rtl_uString *ustrURL, rtl_uString **pustrPath)
818 return osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrURL), pustrPath, true);
821 oslFileError SAL_CALL osl_searchFileURL(
822 rtl_uString *ustrFileName,
823 rtl_uString *ustrSystemSearchPath,
824 rtl_uString **pustrPath)
826 OUString ustrUNCPath;
827 OUString ustrSysPath;
828 oslFileError error;
830 /* First try to interpret the file name as a URL even a relative one */
831 error = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrFileName), &ustrUNCPath.pData, true);
833 /* So far we either have an UNC path or something invalid
834 Now create a system path */
835 if ( osl_File_E_None == error )
836 error = osl_getSystemPathFromFileURL_(ustrUNCPath, &ustrSysPath.pData, true);
838 if ( osl_File_E_None == error )
840 DWORD nBufferLength;
841 DWORD dwResult;
842 LPWSTR lpBuffer = nullptr;
843 LPWSTR lpszFilePart;
845 /* Repeat calling SearchPath ...
846 Start with MAX_PATH for the buffer. In most cases this
847 will be enough and does not force the loop to run twice */
848 dwResult = MAX_PATH;
852 /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
853 LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr;
854 LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath.getStr());
856 /* Allocate space for buffer according to previous returned count of required chars */
857 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
858 nBufferLength = dwResult + 1;
859 lpBuffer = lpBuffer ?
860 static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) :
861 static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR)));
863 dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart );
864 } while ( dwResult && dwResult >= nBufferLength );
866 /* ... until an error occurs or buffer is large enough.
867 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
869 if ( dwResult )
871 ustrSysPath = o3tl::toU(lpBuffer);
872 error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
874 else
876 WIN32_FIND_DATAW aFindFileData;
877 HANDLE hFind;
879 /* something went wrong, perhaps the path was absolute */
880 error = oslTranslateFileError( GetLastError() );
882 hFind = FindFirstFileW(o3tl::toW(ustrSysPath.getStr()), &aFindFileData);
884 if ( IsValidHandle(hFind) )
886 error = osl_getFileURLFromSystemPath(ustrSysPath.pData, pustrPath);
887 FindClose( hFind );
891 free( lpBuffer );
894 return error;
897 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
899 oslFileError eError = osl_File_E_None;
900 OUString ustrRelSysPath;
901 OUString ustrBaseSysPath;
903 if ( ustrBaseURL && ustrBaseURL->length )
905 eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrBaseURL), &ustrBaseSysPath.pData, false);
906 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
908 if (eError == osl_File_E_None)
910 eError = osl_getSystemPathFromFileURL_(OUString::unacquired(&ustrRelativeURL), &ustrRelSysPath.pData,
911 !ustrBaseSysPath.isEmpty());
912 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
915 if ( !eError )
917 OUString sResultPath;
918 /*@@@ToDo
919 The whole FileURL implementation should be merged
920 with the rtl/uri class.
922 // If ustrRelSysPath is absolute, we don't need ustrBaseSysPath.
923 if (!ustrBaseSysPath.isEmpty() && !isAbsolute(ustrRelSysPath))
925 // ustrBaseSysPath is known here to be a valid absolute path -> its first two characters
926 // are ASCII (either alpha + colon, or double backslashes)
928 // Don't use SetCurrentDirectoryW together with GetFullPathNameW, because:
929 // (a) it needs synchronization and may affect threads that may access relative paths;
930 // (b) it would give wrong results for non-existing base path (allowed by RFC2396).
932 if (startsWithDriveColon(ustrRelSysPath))
934 // Special case: a path relative to a specific drive's current directory.
935 // Should we error out here?
937 // If ustrBaseSysPath is on the same drive as ustrRelSysPath, then take base path
938 // as is; otherwise, use current directory on ustrRelSysPath's drive as base path
939 if (onSameDrive(ustrRelSysPath, ustrBaseSysPath))
941 sResultPath = combinePath(ustrBaseSysPath, ustrRelSysPath.subView(2));
943 else
945 // Call GetFullPathNameW to get current directory on ustrRelSysPath's drive
946 wchar_t baseDrive[3] = { ustrRelSysPath[0], ':' }; // just "C:"
947 osl::LongPathBuffer<wchar_t> aBuf(MAX_LONG_PATH);
948 DWORD dwResult
949 = GetFullPathNameW(baseDrive, aBuf.getBufSizeInSymbols(), aBuf, nullptr);
950 if (dwResult)
952 if (dwResult >= aBuf.getBufSizeInSymbols())
953 eError = osl_File_E_INVAL;
954 else
955 sResultPath = combinePath(o3tl::toU(aBuf), ustrRelSysPath.subView(2));
957 else
958 eError = oslTranslateFileError(GetLastError());
961 else
963 // Is this a rooted relative path (starting with a backslash)?
964 // Then we need only root from base. E.g.,
965 // ustrBaseSysPath is "\\server\share\path1\" and ustrRelSysPath is "\path2\to\file"
966 // => \\server\share\path2\to\file
967 // ustrBaseSysPath is "D:\path1\" and ustrRelSysPath is "\path2\to\file"
968 // => D:\path2\to\file
969 auto sBaseView(pathView(ustrBaseSysPath, ustrRelSysPath.startsWith("\\")));
970 sResultPath = combinePath(sBaseView, ustrRelSysPath);
973 else
974 sResultPath = ustrRelSysPath;
976 if (eError == osl_File_E_None)
978 sResultPath = removeRelativeParts(sResultPath);
979 eError = osl_getFileURLFromSystemPath(sResultPath.pData, pustrAbsoluteURL);
983 return eError;
986 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
988 rtl_uString_newFromString(strValid, strRequested);
989 return osl_File_E_None;
992 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */