Branch libreoffice-6-3
[LibreOffice.git] / sal / osl / w32 / file_url.cxx
blob9ff2f95568af18ec4d0d18060319f0e4a533c93b
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>
25 #include <systools/win32/uwinapi.h>
27 #include "file_url.hxx"
28 #include "file_error.hxx"
30 #include <rtl/alloc.h>
31 #include <rtl/ustring.hxx>
32 #include <osl/mutex.h>
33 #include <o3tl/char16_t2wchar_t.hxx>
35 #include "path_helper.hxx"
37 #define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
38 #define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
39 #define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
41 // FileURL functions
43 oslMutex g_CurrentDirectoryMutex = nullptr; /* Initialized in dllentry.c */
45 static bool IsValidFilePathComponent(
46 sal_Unicode const * lpComponent, sal_Unicode const **lppComponentEnd,
47 DWORD dwFlags)
49 sal_Unicode const * lpComponentEnd = nullptr;
50 sal_Unicode const * lpCurrent = lpComponent;
51 bool bValid = true; /* Assume success */
52 sal_Unicode cLast = 0;
54 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
56 while ( !lpComponentEnd && lpCurrent && lpCurrent - lpComponent < MAX_PATH )
58 switch ( *lpCurrent )
60 /* Both backslash and slash determine the end of a path component */
61 case '\0':
62 case '/':
63 case '\\':
64 switch ( cLast )
66 /* Component must not end with '.' or blank and can't be empty */
68 case '.':
69 if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
71 if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
72 1 == lpCurrent - lpComponent )
74 /* Either do allow periods anywhere, or current directory */
75 lpComponentEnd = lpCurrent;
76 break;
78 else if ( 2 == lpCurrent - lpComponent && '.' == *lpComponent )
80 /* Parent directory is O.K. */
81 lpComponentEnd = lpCurrent;
82 break;
85 [[fallthrough]];
86 case 0:
87 case ' ':
88 if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
89 lpComponentEnd = lpCurrent;
90 else
92 lpComponentEnd = lpCurrent - 1;
93 bValid = false;
95 break;
96 default:
97 lpComponentEnd = lpCurrent;
98 break;
100 break;
101 /* The following characters are reserved */
102 case '?':
103 case '*':
104 case '<':
105 case '>':
106 case '\"':
107 case '|':
108 case ':':
109 lpComponentEnd = lpCurrent;
110 bValid = false;
111 break;
112 default:
113 /* Characters below ASCII 32 are not allowed */
114 if ( *lpCurrent < ' ' )
116 lpComponentEnd = lpCurrent;
117 bValid = false;
119 break;
121 cLast = *lpCurrent++;
124 /* If we don't reached the end of the component the length of the component was to long
125 ( See condition of while loop ) */
126 if ( !lpComponentEnd )
128 bValid = false;
129 lpComponentEnd = lpCurrent;
132 if ( bValid )
134 // Empty components are not allowed
135 if ( lpComponentEnd - lpComponent < 1 )
136 bValid = false;
138 // If we reached the end of the string nullptr is returned
139 else if ( !*lpComponentEnd )
140 lpComponentEnd = nullptr;
144 if ( lppComponentEnd )
145 *lppComponentEnd = lpComponentEnd;
147 return bValid;
150 static sal_Int32 countInitialSeparators(sal_Unicode const * path) {
151 sal_Unicode const * p = path;
152 while (*p == '\\' || *p == '/') {
153 ++p;
155 return p - path;
158 DWORD IsValidFilePath(rtl_uString *path, DWORD dwFlags, rtl_uString **corrected)
160 sal_Unicode const * lpszPath = path->buffer;
161 sal_Unicode const * lpComponent = lpszPath;
162 bool bValid = true;
163 DWORD dwPathType = PATHTYPE_ERROR;
164 sal_Int32 nLength = rtl_uString_getLength( path );
166 if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
167 dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
169 DWORD dwCandidatPathType = PATHTYPE_ERROR;
171 if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, o3tl::toU(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1 ) )
173 /* This is long path in UNC notation */
174 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1;
175 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
177 else if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, o3tl::toU(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1 ) )
179 /* This is long path */
180 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1;
182 if ( iswalpha( lpComponent[0] ) && ':' == lpComponent[1] )
184 lpComponent += 2;
185 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
188 else if ( 2 == countInitialSeparators( lpszPath ) )
190 /* The UNC path notation */
191 lpComponent = lpszPath + 2;
192 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
194 else if ( iswalpha( lpszPath[0] ) && ':' == lpszPath[1] )
196 /* Local path verification. Must start with <drive>: */
197 lpComponent = lpszPath + 2;
198 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
201 if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
203 bValid = IsValidFilePathComponent( lpComponent, &lpComponent, VALIDATEPATH_ALLOW_ELLIPSE );
205 /* So far we have a valid servername. Now let's see if we also have a network resource */
207 dwPathType = dwCandidatPathType;
209 if ( bValid )
211 if ( lpComponent && !*++lpComponent )
212 lpComponent = nullptr;
214 if ( !lpComponent )
216 dwPathType |= PATHTYPE_IS_SERVER;
218 else
220 /* Now test the network resource */
222 bValid = IsValidFilePathComponent( lpComponent, &lpComponent, 0 );
224 /* If we now reached the end of the path, everything is O.K. */
226 if ( bValid && (!lpComponent || !*++lpComponent ) )
228 lpComponent = nullptr;
229 dwPathType |= PATHTYPE_IS_VOLUME;
234 else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
236 if ( 1 == countInitialSeparators( lpComponent ) )
237 lpComponent++;
238 else if ( *lpComponent )
239 bValid = false;
241 dwPathType = dwCandidatPathType;
243 /* Now we are behind the backslash or it was a simple drive without backslash */
245 if ( bValid && !*lpComponent )
247 lpComponent = nullptr;
248 dwPathType |= PATHTYPE_IS_VOLUME;
251 else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
253 /* Can be a relative path */
254 lpComponent = lpszPath;
256 /* Relative path can start with a backslash */
258 if ( 1 == countInitialSeparators( lpComponent ) )
260 lpComponent++;
261 if ( !*lpComponent )
262 lpComponent = nullptr;
265 dwPathType = PATHTYPE_RELATIVE;
267 else
269 /* Anything else is an error */
270 bValid = false;
271 lpComponent = lpszPath;
274 /* Now validate each component of the path */
275 rtl_uString * lastCorrected = path;
276 while ( bValid && lpComponent )
278 // Correct path by merging consecutive slashes:
279 if (*lpComponent == '\\' && corrected != nullptr) {
280 sal_Int32 i = lpComponent - lpszPath;
281 rtl_uString_newReplaceStrAt(corrected, lastCorrected, i, 1, nullptr);
282 //TODO: handle out-of-memory
283 lastCorrected = *corrected;
284 lpszPath = (*corrected)->buffer;
285 lpComponent = lpszPath + i;
288 bValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
290 if ( bValid && lpComponent )
292 lpComponent++;
294 /* If the string behind the backslash is empty, we've done */
296 if ( !*lpComponent )
297 lpComponent = nullptr;
301 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
302 if ( bValid && !( dwPathType & PATHTYPE_IS_LONGPATH ) && rtl_ustr_getLength( lpszPath ) >= MAX_PATH )
304 bValid = false;
307 return bValid ? dwPathType : PATHTYPE_ERROR;
310 // Expects a proper absolute or relative path
311 static sal_Int32 PathRemoveFileSpec(LPWSTR lpPath, LPWSTR lpFileName, sal_Int32 nFileBufLen )
313 sal_Int32 nRemoved = 0;
315 if (nFileBufLen && wcscmp(lpPath, L"\\\\") != 0) // tdf#98343 do not remove leading UNC backslashes!
317 lpFileName[0] = 0;
318 LPWSTR lpLastBkSlash = wcsrchr( lpPath, '\\' );
319 LPWSTR lpLastSlash = wcsrchr( lpPath, '/' );
320 LPWSTR lpLastDelimiter = std::max(lpLastSlash, lpLastBkSlash);
322 if ( lpLastDelimiter )
324 sal_Int32 nDelLen = wcslen( lpLastDelimiter );
325 if ( 1 == nDelLen )
327 if ( lpLastDelimiter > lpPath && *(lpLastDelimiter - 1) != ':' )
329 *lpLastDelimiter = 0;
330 *lpFileName = 0;
331 nRemoved = nDelLen;
334 else if ( nDelLen && nDelLen - 1 < nFileBufLen )
336 wcscpy( lpFileName, lpLastDelimiter + 1 );
337 *(++lpLastDelimiter) = 0;
338 nRemoved = nDelLen - 1;
343 return nRemoved;
346 // Undocumented in SHELL32.DLL ordinal 32
347 static LPWSTR PathAddBackslash(LPWSTR lpPath, sal_uInt32 nBufLen)
349 LPWSTR lpEndPath = nullptr;
351 if ( lpPath )
353 std::size_t nLen = wcslen(lpPath);
355 if ( !nLen || ( lpPath[nLen-1] != '\\' && lpPath[nLen-1] != '/' && nLen < nBufLen - 1 ) )
357 lpEndPath = lpPath + nLen;
358 *lpEndPath++ = '\\';
359 *lpEndPath = 0;
362 return lpEndPath;
365 // True if the szPath + szFile is just a special prefix, not a path which we may test for existence.
366 // E.g., \\ or \\server or \\server\share or \\? or \\?\UNC or \\?\UNC\server or \\?\UNC\server\share
367 static bool IsPathSpecialPrefix(LPWSTR szPath, LPWSTR szFile)
369 if (szPath[0] == '\\' && szPath[1] == '\\')
371 if (szPath[2] == 0)
372 return true; // "\\" -> now the server name or "." or "?" will append
373 else if (szPath[2] == '?' && szPath[3] == '\\')
375 if (szPath[4] == 0)
376 return wcscmp(szFile, L"UNC") == 0; // "\\?\" -> now "UNC" will append
377 else
379 if (wcsncmp(szPath + 4, L"UNC\\", 4) == 0)
381 if (szPath[8] == 0)
382 return true; // "\\?\UNC\" -> now the server name will append
383 else if (const wchar_t* pBackSlash = wcschr(szPath + 8, '\\'))
384 return *(pBackSlash + 1) == 0; // "\\?\UNC\Server\" -> now share name will append
388 else if (szPath[2] != '.')
390 if (const wchar_t* pBackSlash = wcschr(szPath + 2, '\\'))
391 return *(pBackSlash + 1) == 0; // "\\Server\" -> now share name will append
394 return false;
397 // Expects a proper absolute or relative path. NB: It is different from GetLongPathName WinAPI!
398 static DWORD GetCaseCorrectPathNameEx(
399 LPWSTR lpszPath, // path buffer to convert
400 sal_uInt32 cchBuffer, // size of path buffer
401 DWORD nSkipLevels,
402 bool bCheckExistence )
404 ::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
405 sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
406 sal_Int32 nLastStepRemoved = nRemoved;
407 while ( nLastStepRemoved && szFile[0] == 0 )
409 // remove separators
410 nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
411 nRemoved += nLastStepRemoved;
414 if ( nRemoved )
416 bool bSkipThis = false;
418 if ( 0 == wcscmp( szFile, L".." ) )
420 bSkipThis = true;
421 nSkipLevels += 1;
423 else if ( 0 == wcscmp( szFile, L"." ) )
425 bSkipThis = true;
427 else if ( nSkipLevels )
429 bSkipThis = true;
430 nSkipLevels--;
432 else
433 bSkipThis = false;
435 if ( !GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence ) )
436 return 0;
438 PathAddBackslash( lpszPath, cchBuffer );
440 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
441 if ( !bSkipThis )
443 if ( bCheckExistence )
446 if (IsPathSpecialPrefix(lpszPath, szFile))
448 /* add the segment name back */
449 wcscat(lpszPath, szFile);
451 else
453 osl::LongPathBuffer<WCHAR> aShortPath(MAX_LONG_PATH);
454 wcscpy(aShortPath, lpszPath);
455 wcscat(aShortPath, szFile);
457 WIN32_FIND_DATAW aFindFileData;
458 HANDLE hFind = FindFirstFileW(aShortPath, &aFindFileData);
460 if (IsValidHandle(hFind))
462 wcscat(lpszPath, aFindFileData.cFileName[0]
463 ? aFindFileData.cFileName
464 : aFindFileData.cAlternateFileName);
466 FindClose(hFind);
468 else
469 lpszPath[0] = 0;
472 else
474 /* add the segment name back */
475 wcscat( lpszPath, szFile );
479 else
481 /* File specification can't be removed therefore the short path is either a drive
482 or a network share. If still levels to skip are left, the path specification
483 tries to travel below the file system root */
484 if ( nSkipLevels )
485 lpszPath[0] = 0;
486 else
487 _wcsupr( lpszPath );
490 return wcslen( lpszPath );
493 DWORD GetCaseCorrectPathName(
494 LPCWSTR lpszShortPath, // file name
495 LPWSTR lpszLongPath, // path buffer
496 sal_uInt32 cchBuffer, // size of path buffer
497 bool bCheckExistence
500 /* Special handling for "\\.\" as system root */
501 if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
503 if ( cchBuffer >= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) )
505 wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
506 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
508 else
510 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
513 else if ( lpszShortPath )
515 if ( wcslen( lpszShortPath ) <= cchBuffer )
517 wcscpy( lpszLongPath, lpszShortPath );
518 return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
522 return 0;
525 static bool osl_decodeURL_( rtl_String* strUTF8, rtl_uString** pstrDecodedURL )
527 sal_Char *pBuffer;
528 const sal_Char *pSrcEnd;
529 const sal_Char *pSrc;
530 sal_Char *pDest;
531 sal_Int32 nSrcLen;
532 bool bValidEncoded = true; /* Assume success */
534 /* The resulting decoded string length is shorter or equal to the source length */
536 nSrcLen = rtl_string_getLength(strUTF8);
537 pBuffer = static_cast<sal_Char*>(malloc((nSrcLen + 1) * sizeof(sal_Char)));
539 pDest = pBuffer;
540 pSrc = rtl_string_getStr(strUTF8);
541 pSrcEnd = pSrc + nSrcLen;
543 /* Now decode the URL what should result in an UTF8 string */
544 while ( bValidEncoded && pSrc < pSrcEnd )
546 switch ( *pSrc )
548 case '%':
550 sal_Char aToken[3];
551 sal_Char aChar;
553 pSrc++;
554 aToken[0] = *pSrc++;
555 aToken[1] = *pSrc++;
556 aToken[2] = 0;
558 aChar = static_cast<sal_Char>(strtoul( aToken, nullptr, 16 ));
560 /* The chars are path delimiters and must not be encoded */
562 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
563 bValidEncoded = false;
564 else
565 *pDest++ = aChar;
567 break;
568 case '\0':
569 case '#':
570 case '?':
571 bValidEncoded = false;
572 break;
573 default:
574 *pDest++ = *pSrc++;
575 break;
579 *pDest++ = 0;
581 if ( bValidEncoded )
583 rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
584 OSL_ASSERT(*pstrDecodedURL != nullptr);
587 free( pBuffer );
589 return bValidEncoded;
592 static void osl_encodeURL_( rtl_uString *strURL, rtl_String **pstrEncodedURL )
594 /* Encode non ascii characters within the URL */
596 rtl_String *strUTF8 = nullptr;
597 sal_Char *pszEncodedURL;
598 const sal_Char *pURLScan;
599 sal_Char *pURLDest;
600 sal_Int32 nURLScanLen;
601 sal_Int32 nURLScanCount;
603 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
605 pszEncodedURL = static_cast<sal_Char*>(malloc( (rtl_string_getLength( strUTF8 ) * 3 + 1) * sizeof(sal_Char) ));
606 assert(pszEncodedURL); // Don't handle OOM conditions
607 pURLDest = pszEncodedURL;
608 pURLScan = rtl_string_getStr( strUTF8 );
609 nURLScanLen = rtl_string_getLength( strUTF8 );
610 nURLScanCount = 0;
612 while ( nURLScanCount < nURLScanLen )
614 sal_Char cCurrent = *pURLScan;
615 switch ( cCurrent )
617 default:
618 if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
620 sprintf( pURLDest, "%%%02X", static_cast<unsigned char>(cCurrent) );
621 pURLDest += 3;
622 break;
624 [[fallthrough]];
625 case '!':
626 case '\'':
627 case '(':
628 case ')':
629 case '*':
630 case '-':
631 case '.':
632 case '_':
633 case '~':
634 case '$':
635 case '&':
636 case '+':
637 case ',':
638 case '=':
639 case '@':
640 case ':':
641 case '/':
642 case '\\':
643 case '|':
644 *pURLDest++ = cCurrent;
645 break;
646 case 0:
647 break;
650 pURLScan++;
651 nURLScanCount++;
654 *pURLDest = 0;
656 rtl_string_release( strUTF8 );
657 rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL );
658 free( pszEncodedURL );
661 oslFileError osl_getSystemPathFromFileURL_( rtl_uString *strURL, rtl_uString **pustrPath, bool bAllowRelative )
663 rtl_String *strUTF8 = nullptr;
664 rtl_uString *strDecodedURL = nullptr;
665 rtl_uString *strTempPath = nullptr;
666 sal_uInt32 nDecodedLen;
667 bool bValidEncoded;
668 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
670 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
671 having a mixed encoded URL later */
673 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
675 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
677 SAL_WARN_IF(
678 strUTF8->length != strURL->length &&
679 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 )
680 , "sal.osl"
681 ,"osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not encoded !!!");
683 bValidEncoded = osl_decodeURL_( strUTF8, &strDecodedURL );
685 /* Release the encoded UTF8 string */
686 rtl_string_release( strUTF8 );
688 if ( bValidEncoded )
690 /* Replace backslashes and pipes */
692 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' );
693 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' );
695 const sal_Unicode *pDecodedURL = rtl_uString_getStr( strDecodedURL );
696 nDecodedLen = rtl_uString_getLength( strDecodedURL );
698 /* Must start with "file://" */
699 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
701 sal_uInt32 nSkip;
703 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
704 nSkip = 8;
705 else if (
706 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
707 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
709 nSkip = 17;
710 else
711 nSkip = 5;
713 /* Indicates local root */
714 if ( nDecodedLen == nSkip )
715 rtl_uString_newFromStr_WithLength( &strTempPath, o3tl::toU(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 );
716 else
718 /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
719 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
721 rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
723 else
725 ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
726 sal_uInt32 nNewLen = GetCaseCorrectPathName( o3tl::toW(pDecodedURL) + nSkip,
727 o3tl::toW(aBuf),
728 aBuf.getBufSizeInSymbols(),
729 false );
731 if ( nNewLen <= MAX_PATH - 12
732 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, o3tl::toU(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1, SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 )
733 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, o3tl::toU(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1 ) )
735 rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
737 else if ( pDecodedURL[nSkip] == '\\' && pDecodedURL[nSkip+1] == '\\' )
739 /* it should be an UNC path, use the according prefix */
740 rtl_uString *strSuffix = nullptr;
741 rtl_uString *strPrefix = nullptr;
742 rtl_uString_newFromStr_WithLength( &strPrefix, o3tl::toU(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
743 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
745 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
747 rtl_uString_release( strPrefix );
748 rtl_uString_release( strSuffix );
750 else
752 rtl_uString *strSuffix = nullptr;
753 rtl_uString *strPrefix = nullptr;
754 rtl_uString_newFromStr_WithLength( &strPrefix, o3tl::toU(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1 );
755 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
757 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
759 rtl_uString_release( strPrefix );
760 rtl_uString_release( strSuffix );
765 if ( IsValidFilePath( strTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
766 nError = osl_File_E_None;
768 else if ( bAllowRelative ) /* This maybe a relative file URL */
770 /* In future the relative path could be converted to absolute if it is too long */
771 rtl_uString_assign( &strTempPath, strDecodedURL );
773 if ( IsValidFilePath( strTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
774 nError = osl_File_E_None;
776 else
777 SAL_INFO_IF(nError, "sal.osl",
778 "osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not an absolute FileURL");
782 if ( strDecodedURL )
783 rtl_uString_release( strDecodedURL );
785 if ( osl_File_E_None == nError )
786 rtl_uString_assign( pustrPath, strTempPath );
788 if ( strTempPath )
789 rtl_uString_release( strTempPath );
791 SAL_INFO_IF(nError, "sal.osl",
792 "osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not a FileURL");
794 return nError;
797 oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
799 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
800 rtl_uString *strTempURL = nullptr;
801 DWORD dwPathType = PATHTYPE_ERROR;
803 if (strPath)
804 dwPathType = IsValidFilePath(strPath, VALIDATEPATH_ALLOW_RELATIVE, nullptr);
806 if (dwPathType)
808 rtl_uString *strTempPath = nullptr;
810 if ( dwPathType & PATHTYPE_IS_LONGPATH )
812 rtl_uString *strBuffer = nullptr;
813 sal_uInt32 nIgnore = 0;
814 sal_uInt32 nLength = 0;
816 /* the path has the longpath prefix, lets remove it */
817 switch ( dwPathType & PATHTYPE_MASK_TYPE )
819 case PATHTYPE_ABSOLUTE_UNC:
820 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
821 OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
823 /* generate the normal UNC path */
824 nLength = rtl_uString_getLength( strPath );
825 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
826 strBuffer->buffer[0] = '\\';
828 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
829 rtl_uString_release( strBuffer );
830 break;
832 case PATHTYPE_ABSOLUTE_LOCAL:
833 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1;
834 OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
836 /* generate the normal path */
837 nLength = rtl_uString_getLength( strPath );
838 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
840 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
841 rtl_uString_release( strBuffer );
842 break;
844 default:
845 OSL_FAIL( "Unexpected long path format!" );
846 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
847 break;
850 else
852 /* Replace backslashes */
853 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
856 switch ( dwPathType & PATHTYPE_MASK_TYPE )
858 case PATHTYPE_RELATIVE:
859 rtl_uString_assign( &strTempURL, strTempPath );
860 nError = osl_File_E_None;
861 break;
862 case PATHTYPE_ABSOLUTE_UNC:
863 rtl_uString_newFromAscii( &strTempURL, "file:" );
864 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
865 nError = osl_File_E_None;
866 break;
867 case PATHTYPE_ABSOLUTE_LOCAL:
868 rtl_uString_newFromAscii( &strTempURL, "file:///" );
869 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
870 nError = osl_File_E_None;
871 break;
872 default:
873 break;
876 /* Release temp path */
877 rtl_uString_release( strTempPath );
880 if ( osl_File_E_None == nError )
882 rtl_String *strEncodedURL = nullptr;
884 /* Encode the URL */
885 osl_encodeURL_( strTempURL, &strEncodedURL );
887 /* Provide URL via unicode string */
888 rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
889 OSL_ASSERT(*pstrURL != nullptr);
890 rtl_string_release( strEncodedURL );
893 /* Release temp URL */
894 if ( strTempURL )
895 rtl_uString_release( strTempURL );
897 SAL_INFO_IF(nError, "sal.osl",
898 "osl_getFileURLFromSystemPath: \"" << OUString(strPath) << "\" is not a systemPath");
899 return nError;
902 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
903 rtl_uString *ustrURL, rtl_uString **pustrPath)
905 return osl_getSystemPathFromFileURL_( ustrURL, pustrPath, true );
908 oslFileError SAL_CALL osl_searchFileURL(
909 rtl_uString *ustrFileName,
910 rtl_uString *ustrSystemSearchPath,
911 rtl_uString **pustrPath)
913 rtl_uString *ustrUNCPath = nullptr;
914 rtl_uString *ustrSysPath = nullptr;
915 oslFileError error;
917 /* First try to interpret the file name as a URL even a relative one */
918 error = osl_getSystemPathFromFileURL_( ustrFileName, &ustrUNCPath, true );
920 /* So far we either have an UNC path or something invalid
921 Now create a system path */
922 if ( osl_File_E_None == error )
923 error = osl_getSystemPathFromFileURL_( ustrUNCPath, &ustrSysPath, true );
925 if ( osl_File_E_None == error )
927 DWORD nBufferLength;
928 DWORD dwResult;
929 LPWSTR lpBuffer = nullptr;
930 LPWSTR lpszFilePart;
932 /* Repeat calling SearchPath ...
933 Start with MAX_PATH for the buffer. In most cases this
934 will be enough and does not force the loop to run twice */
935 dwResult = MAX_PATH;
939 /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
940 LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr;
941 LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath->buffer);
943 /* Allocate space for buffer according to previous returned count of required chars */
944 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
945 nBufferLength = dwResult + 1;
946 lpBuffer = lpBuffer ?
947 static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) :
948 static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR)));
950 dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart );
951 } while ( dwResult && dwResult >= nBufferLength );
953 /* ... until an error occurs or buffer is large enough.
954 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
956 if ( dwResult )
958 rtl_uString_newFromStr( &ustrSysPath, o3tl::toU(lpBuffer) );
959 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
961 else
963 WIN32_FIND_DATAW aFindFileData;
964 HANDLE hFind;
966 /* something went wrong, perhaps the path was absolute */
967 error = oslTranslateFileError( GetLastError() );
969 hFind = FindFirstFileW( o3tl::toW(ustrSysPath->buffer), &aFindFileData );
971 if ( IsValidHandle(hFind) )
973 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
974 FindClose( hFind );
978 free( lpBuffer );
981 if ( ustrSysPath )
982 rtl_uString_release( ustrSysPath );
984 if ( ustrUNCPath )
985 rtl_uString_release( ustrUNCPath );
987 return error;
990 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
992 oslFileError eError;
993 rtl_uString *ustrRelSysPath = nullptr;
994 rtl_uString *ustrBaseSysPath = nullptr;
996 if ( ustrBaseURL && ustrBaseURL->length )
998 eError = osl_getSystemPathFromFileURL_( ustrBaseURL, &ustrBaseSysPath, false );
999 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1001 eError = osl_getSystemPathFromFileURL_( ustrRelativeURL, &ustrRelSysPath, true );
1003 else
1005 eError = osl_getSystemPathFromFileURL_( ustrRelativeURL, &ustrRelSysPath, false );
1006 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1009 if ( !eError )
1011 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
1012 ::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
1013 LPWSTR lpFilePart = nullptr;
1014 DWORD dwResult;
1016 /*@@@ToDo
1017 Bad, bad hack, this only works if the base path
1018 really exists which is not necessary according
1019 to RFC2396
1020 The whole FileURL implementation should be merged
1021 with the rtl/uri class.
1023 if ( ustrBaseSysPath )
1025 osl_acquireMutex( g_CurrentDirectoryMutex );
1027 GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), o3tl::toW(aCurrentDir) );
1028 SetCurrentDirectoryW( o3tl::toW(ustrBaseSysPath->buffer) );
1031 dwResult = GetFullPathNameW( o3tl::toW(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer), &lpFilePart );
1033 if ( ustrBaseSysPath )
1035 SetCurrentDirectoryW( o3tl::toW(aCurrentDir) );
1037 osl_releaseMutex( g_CurrentDirectoryMutex );
1040 if ( dwResult )
1042 if ( dwResult >= aBuffer.getBufSizeInSymbols() )
1043 eError = osl_File_E_INVAL;
1044 else
1046 rtl_uString *ustrAbsSysPath = nullptr;
1048 rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
1050 eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
1052 if ( ustrAbsSysPath )
1053 rtl_uString_release( ustrAbsSysPath );
1056 else
1057 eError = oslTranslateFileError( GetLastError() );
1060 if ( ustrBaseSysPath )
1061 rtl_uString_release( ustrBaseSysPath );
1063 if ( ustrRelSysPath )
1064 rtl_uString_release( ustrRelSysPath );
1066 return eError;
1069 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
1071 rtl_uString_newFromString(strValid, strRequested);
1072 return osl_File_E_None;
1075 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */