lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / sal / osl / w32 / file_url.cxx
blobf2bfd8da7e0acf51b1a62cf47cf02b44ee575164
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 SAL_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 // Expects a proper absolute or relative path. NB: It is different from GetLongPathName WinAPI!
366 static DWORD GetCaseCorrectPathNameEx(
367 LPWSTR lpszPath, // path buffer to convert
368 sal_uInt32 cchBuffer, // size of path buffer
369 DWORD nSkipLevels,
370 bool bCheckExistence )
372 ::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
373 sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
374 sal_Int32 nLastStepRemoved = nRemoved;
375 while ( nLastStepRemoved && szFile[0] == 0 )
377 // remove separators
378 nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
379 nRemoved += nLastStepRemoved;
382 if ( nRemoved )
384 bool bSkipThis = false;
386 if ( 0 == wcscmp( szFile, L".." ) )
388 bSkipThis = true;
389 nSkipLevels += 1;
391 else if ( 0 == wcscmp( szFile, L"." ) )
393 bSkipThis = true;
395 else if ( nSkipLevels )
397 bSkipThis = true;
398 nSkipLevels--;
400 else
401 bSkipThis = false;
403 if ( !GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence ) )
404 return 0;
406 PathAddBackslash( lpszPath, cchBuffer );
408 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
409 if ( !bSkipThis )
411 if ( bCheckExistence )
413 ::osl::LongPathBuffer< WCHAR > aShortPath( MAX_LONG_PATH );
414 wcscpy( aShortPath, lpszPath );
415 wcscat( aShortPath, szFile );
417 WIN32_FIND_DATAW aFindFileData;
418 HANDLE hFind = FindFirstFileW( aShortPath, &aFindFileData );
420 if ( IsValidHandle(hFind) )
422 wcscat( lpszPath, aFindFileData.cFileName[0] ? aFindFileData.cFileName : aFindFileData.cAlternateFileName );
424 FindClose( hFind );
426 else
427 lpszPath[0] = 0;
429 else
431 /* add the segment name back */
432 wcscat( lpszPath, szFile );
436 else
438 /* File specification can't be removed therefore the short path is either a drive
439 or a network share. If still levels to skip are left, the path specification
440 tries to travel below the file system root */
441 if ( nSkipLevels )
442 lpszPath[0] = 0;
443 else
444 _wcsupr( lpszPath );
447 return wcslen( lpszPath );
450 DWORD GetCaseCorrectPathName(
451 LPCWSTR lpszShortPath, // file name
452 LPWSTR lpszLongPath, // path buffer
453 sal_uInt32 cchBuffer, // size of path buffer
454 bool bCheckExistence
457 /* Special handling for "\\.\" as system root */
458 if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
460 if ( cchBuffer >= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) )
462 wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
463 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
465 else
467 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
470 else if ( lpszShortPath )
472 if ( wcslen( lpszShortPath ) <= cchBuffer )
474 wcscpy( lpszLongPath, lpszShortPath );
475 return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
479 return 0;
482 static bool osl_decodeURL_( rtl_String* strUTF8, rtl_uString** pstrDecodedURL )
484 sal_Char *pBuffer;
485 const sal_Char *pSrcEnd;
486 const sal_Char *pSrc;
487 sal_Char *pDest;
488 sal_Int32 nSrcLen;
489 bool bValidEncoded = true; /* Assume success */
491 /* The resulting decoded string length is shorter or equal to the source length */
493 nSrcLen = rtl_string_getLength(strUTF8);
494 pBuffer = static_cast<sal_Char*>(malloc((nSrcLen + 1) * sizeof(sal_Char)));
496 pDest = pBuffer;
497 pSrc = rtl_string_getStr(strUTF8);
498 pSrcEnd = pSrc + nSrcLen;
500 /* Now decode the URL what should result in an UTF8 string */
501 while ( bValidEncoded && pSrc < pSrcEnd )
503 switch ( *pSrc )
505 case '%':
507 sal_Char aToken[3];
508 sal_Char aChar;
510 pSrc++;
511 aToken[0] = *pSrc++;
512 aToken[1] = *pSrc++;
513 aToken[2] = 0;
515 aChar = static_cast<sal_Char>(strtoul( aToken, nullptr, 16 ));
517 /* The chars are path delimiters and must not be encoded */
519 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
520 bValidEncoded = false;
521 else
522 *pDest++ = aChar;
524 break;
525 case '\0':
526 case '#':
527 case '?':
528 bValidEncoded = false;
529 break;
530 default:
531 *pDest++ = *pSrc++;
532 break;
536 *pDest++ = 0;
538 if ( bValidEncoded )
540 rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OSTRING_TO_OUSTRING_CVTFLAGS );
541 OSL_ASSERT(*pstrDecodedURL != nullptr);
544 free( pBuffer );
546 return bValidEncoded;
549 static void osl_encodeURL_( rtl_uString *strURL, rtl_String **pstrEncodedURL )
551 /* Encode non ascii characters within the URL */
553 rtl_String *strUTF8 = nullptr;
554 sal_Char *pszEncodedURL;
555 const sal_Char *pURLScan;
556 sal_Char *pURLDest;
557 sal_Int32 nURLScanLen;
558 sal_Int32 nURLScanCount;
560 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
562 pszEncodedURL = static_cast<sal_Char*>(malloc( (rtl_string_getLength( strUTF8 ) * 3 + 1) * sizeof(sal_Char) ));
564 pURLDest = pszEncodedURL;
565 pURLScan = rtl_string_getStr( strUTF8 );
566 nURLScanLen = rtl_string_getLength( strUTF8 );
567 nURLScanCount = 0;
569 while ( nURLScanCount < nURLScanLen )
571 sal_Char cCurrent = *pURLScan;
572 switch ( cCurrent )
574 default:
575 if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
577 sprintf( pURLDest, "%%%02X", static_cast<unsigned char>(cCurrent) );
578 pURLDest += 3;
579 break;
581 SAL_FALLTHROUGH;
582 case '!':
583 case '\'':
584 case '(':
585 case ')':
586 case '*':
587 case '-':
588 case '.':
589 case '_':
590 case '~':
591 case '$':
592 case '&':
593 case '+':
594 case ',':
595 case '=':
596 case '@':
597 case ':':
598 case '/':
599 case '\\':
600 case '|':
601 *pURLDest++ = cCurrent;
602 break;
603 case 0:
604 break;
607 pURLScan++;
608 nURLScanCount++;
611 *pURLDest = 0;
613 rtl_string_release( strUTF8 );
614 rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL );
615 free( pszEncodedURL );
618 oslFileError osl_getSystemPathFromFileURL_( rtl_uString *strURL, rtl_uString **pustrPath, bool bAllowRelative )
620 rtl_String *strUTF8 = nullptr;
621 rtl_uString *strDecodedURL = nullptr;
622 rtl_uString *strTempPath = nullptr;
623 sal_uInt32 nDecodedLen;
624 bool bValidEncoded;
625 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
627 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
628 having a mixed encoded URL later */
630 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
632 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
634 SAL_WARN_IF(
635 strUTF8->length != strURL->length &&
636 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 )
637 , "sal.osl"
638 ,"osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not encoded !!!");
640 bValidEncoded = osl_decodeURL_( strUTF8, &strDecodedURL );
642 /* Release the encoded UTF8 string */
643 rtl_string_release( strUTF8 );
645 if ( bValidEncoded )
647 /* Replace backslashes and pipes */
649 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' );
650 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' );
652 const sal_Unicode *pDecodedURL = rtl_uString_getStr( strDecodedURL );
653 nDecodedLen = rtl_uString_getLength( strDecodedURL );
655 /* Must start with "file://" */
656 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
658 sal_uInt32 nSkip;
660 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
661 nSkip = 8;
662 else if (
663 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
664 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
666 nSkip = 17;
667 else
668 nSkip = 5;
670 /* Indicates local root */
671 if ( nDecodedLen == nSkip )
672 rtl_uString_newFromStr_WithLength( &strTempPath, o3tl::toU(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 );
673 else
675 /* do not separate the directory and file case, so the maximal path length without prefix is MAX_PATH-12 */
676 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
678 rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
680 else
682 ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
683 sal_uInt32 nNewLen = GetCaseCorrectPathName( o3tl::toW(pDecodedURL) + nSkip,
684 o3tl::toW(aBuf),
685 aBuf.getBufSizeInSymbols(),
686 false );
688 if ( nNewLen <= MAX_PATH - 12
689 || 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 )
690 || 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 ) )
692 rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
694 else if ( pDecodedURL[nSkip] == '\\' && pDecodedURL[nSkip+1] == '\\' )
696 /* it should be an UNC path, use the according prefix */
697 rtl_uString *strSuffix = nullptr;
698 rtl_uString *strPrefix = nullptr;
699 rtl_uString_newFromStr_WithLength( &strPrefix, o3tl::toU(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
700 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
702 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
704 rtl_uString_release( strPrefix );
705 rtl_uString_release( strSuffix );
707 else
709 rtl_uString *strSuffix = nullptr;
710 rtl_uString *strPrefix = nullptr;
711 rtl_uString_newFromStr_WithLength( &strPrefix, o3tl::toU(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1 );
712 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
714 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
716 rtl_uString_release( strPrefix );
717 rtl_uString_release( strSuffix );
722 if ( IsValidFilePath( strTempPath, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
723 nError = osl_File_E_None;
725 else if ( bAllowRelative ) /* This maybe a relative file URL */
727 /* In future the relative path could be converted to absolute if it is too long */
728 rtl_uString_assign( &strTempPath, strDecodedURL );
730 if ( IsValidFilePath( strTempPath, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
731 nError = osl_File_E_None;
733 else
734 SAL_INFO_IF(nError, "sal.osl",
735 "osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not an absolute FileURL");
739 if ( strDecodedURL )
740 rtl_uString_release( strDecodedURL );
742 if ( osl_File_E_None == nError )
743 rtl_uString_assign( pustrPath, strTempPath );
745 if ( strTempPath )
746 rtl_uString_release( strTempPath );
748 SAL_INFO_IF(nError, "sal.osl",
749 "osl_getSystemPathFromFileURL: \"" << OUString(strURL) << "\" is not a FileURL");
751 return nError;
754 oslFileError osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
756 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
757 rtl_uString *strTempURL = nullptr;
758 DWORD dwPathType = PATHTYPE_ERROR;
760 if (strPath)
761 dwPathType = IsValidFilePath(strPath, VALIDATEPATH_ALLOW_RELATIVE, nullptr);
763 if (dwPathType)
765 rtl_uString *strTempPath = nullptr;
767 if ( dwPathType & PATHTYPE_IS_LONGPATH )
769 rtl_uString *strBuffer = nullptr;
770 sal_uInt32 nIgnore = 0;
771 sal_uInt32 nLength = 0;
773 /* the path has the longpath prefix, lets remove it */
774 switch ( dwPathType & PATHTYPE_MASK_TYPE )
776 case PATHTYPE_ABSOLUTE_UNC:
777 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
778 OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
780 /* generate the normal UNC path */
781 nLength = rtl_uString_getLength( strPath );
782 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
783 strBuffer->buffer[0] = '\\';
785 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
786 rtl_uString_release( strBuffer );
787 break;
789 case PATHTYPE_ABSOLUTE_LOCAL:
790 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1;
791 OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
793 /* generate the normal path */
794 nLength = rtl_uString_getLength( strPath );
795 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
797 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
798 rtl_uString_release( strBuffer );
799 break;
801 default:
802 OSL_FAIL( "Unexpected long path format!" );
803 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
804 break;
807 else
809 /* Replace backslashes */
810 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
813 switch ( dwPathType & PATHTYPE_MASK_TYPE )
815 case PATHTYPE_RELATIVE:
816 rtl_uString_assign( &strTempURL, strTempPath );
817 nError = osl_File_E_None;
818 break;
819 case PATHTYPE_ABSOLUTE_UNC:
820 rtl_uString_newFromAscii( &strTempURL, "file:" );
821 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
822 nError = osl_File_E_None;
823 break;
824 case PATHTYPE_ABSOLUTE_LOCAL:
825 rtl_uString_newFromAscii( &strTempURL, "file:///" );
826 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
827 nError = osl_File_E_None;
828 break;
829 default:
830 break;
833 /* Release temp path */
834 rtl_uString_release( strTempPath );
837 if ( osl_File_E_None == nError )
839 rtl_String *strEncodedURL = nullptr;
841 /* Encode the URL */
842 osl_encodeURL_( strTempURL, &strEncodedURL );
844 /* Provide URL via unicode string */
845 rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
846 OSL_ASSERT(*pstrURL != nullptr);
847 rtl_string_release( strEncodedURL );
850 /* Release temp URL */
851 if ( strTempURL )
852 rtl_uString_release( strTempURL );
854 SAL_INFO_IF(nError, "sal.osl",
855 "osl_getFileURLFromSystemPath: \"" << OUString(strPath) << "\" is not a systemPath");
856 return nError;
859 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
860 rtl_uString *ustrURL, rtl_uString **pustrPath)
862 return osl_getSystemPathFromFileURL_( ustrURL, pustrPath, true );
865 oslFileError SAL_CALL osl_searchFileURL(
866 rtl_uString *ustrFileName,
867 rtl_uString *ustrSystemSearchPath,
868 rtl_uString **pustrPath)
870 rtl_uString *ustrUNCPath = nullptr;
871 rtl_uString *ustrSysPath = nullptr;
872 oslFileError error;
874 /* First try to interpret the file name as an URL even a relative one */
875 error = osl_getSystemPathFromFileURL_( ustrFileName, &ustrUNCPath, true );
877 /* So far we either have an UNC path or something invalid
878 Now create a system path */
879 if ( osl_File_E_None == error )
880 error = osl_getSystemPathFromFileURL_( ustrUNCPath, &ustrSysPath, true );
882 if ( osl_File_E_None == error )
884 DWORD nBufferLength;
885 DWORD dwResult;
886 LPWSTR lpBuffer = nullptr;
887 LPWSTR lpszFilePart;
889 /* Repeat calling SearchPath ...
890 Start with MAX_PATH for the buffer. In most cases this
891 will be enough and does not force the loop to run twice */
892 dwResult = MAX_PATH;
896 /* If search path is empty use a nullptr pointer instead according to MSDN documentation of SearchPath */
897 LPCWSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? o3tl::toW(ustrSystemSearchPath->buffer) : nullptr;
898 LPCWSTR lpszSearchFile = o3tl::toW(ustrSysPath->buffer);
900 /* Allocate space for buffer according to previous returned count of required chars */
901 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
902 nBufferLength = dwResult + 1;
903 lpBuffer = lpBuffer ?
904 static_cast<LPWSTR>(realloc(lpBuffer, nBufferLength * sizeof(WCHAR))) :
905 static_cast<LPWSTR>(malloc(nBufferLength * sizeof(WCHAR)));
907 dwResult = SearchPathW( lpszSearchPath, lpszSearchFile, nullptr, nBufferLength, lpBuffer, &lpszFilePart );
908 } while ( dwResult && dwResult >= nBufferLength );
910 /* ... until an error occurs or buffer is large enough.
911 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
913 if ( dwResult )
915 rtl_uString_newFromStr( &ustrSysPath, o3tl::toU(lpBuffer) );
916 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
918 else
920 WIN32_FIND_DATAW aFindFileData;
921 HANDLE hFind;
923 /* something went wrong, perhaps the path was absolute */
924 error = oslTranslateFileError( GetLastError() );
926 hFind = FindFirstFileW( o3tl::toW(ustrSysPath->buffer), &aFindFileData );
928 if ( IsValidHandle(hFind) )
930 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
931 FindClose( hFind );
935 free( lpBuffer );
938 if ( ustrSysPath )
939 rtl_uString_release( ustrSysPath );
941 if ( ustrUNCPath )
942 rtl_uString_release( ustrUNCPath );
944 return error;
947 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
949 oslFileError eError;
950 rtl_uString *ustrRelSysPath = nullptr;
951 rtl_uString *ustrBaseSysPath = nullptr;
953 if ( ustrBaseURL && ustrBaseURL->length )
955 eError = osl_getSystemPathFromFileURL_( ustrBaseURL, &ustrBaseSysPath, false );
956 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
958 eError = osl_getSystemPathFromFileURL_( ustrRelativeURL, &ustrRelSysPath, true );
960 else
962 eError = osl_getSystemPathFromFileURL_( ustrRelativeURL, &ustrRelSysPath, false );
963 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
966 if ( !eError )
968 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
969 ::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
970 LPWSTR lpFilePart = nullptr;
971 DWORD dwResult;
973 /*@@@ToDo
974 Bad, bad hack, this only works if the base path
975 really exists which is not necessary according
976 to RFC2396
977 The whole FileURL implementation should be merged
978 with the rtl/uri class.
980 if ( ustrBaseSysPath )
982 osl_acquireMutex( g_CurrentDirectoryMutex );
984 GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), o3tl::toW(aCurrentDir) );
985 SetCurrentDirectoryW( o3tl::toW(ustrBaseSysPath->buffer) );
988 dwResult = GetFullPathNameW( o3tl::toW(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer), &lpFilePart );
990 if ( ustrBaseSysPath )
992 SetCurrentDirectoryW( o3tl::toW(aCurrentDir) );
994 osl_releaseMutex( g_CurrentDirectoryMutex );
997 if ( dwResult )
999 if ( dwResult >= aBuffer.getBufSizeInSymbols() )
1000 eError = osl_File_E_INVAL;
1001 else
1003 rtl_uString *ustrAbsSysPath = nullptr;
1005 rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
1007 eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
1009 if ( ustrAbsSysPath )
1010 rtl_uString_release( ustrAbsSysPath );
1013 else
1014 eError = oslTranslateFileError( GetLastError() );
1017 if ( ustrBaseSysPath )
1018 rtl_uString_release( ustrBaseSysPath );
1020 if ( ustrRelSysPath )
1021 rtl_uString_release( ustrRelSysPath );
1023 return eError;
1026 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
1028 rtl_uString_newFromString(strValid, strRequested);
1029 return osl_File_E_None;
1032 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */