1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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\\"
43 oslMutex g_CurrentDirectoryMutex
= nullptr; /* Initialized in dllentry.c */
45 static bool IsValidFilePathComponent(
46 sal_Unicode
const * lpComponent
, sal_Unicode
const **lppComponentEnd
,
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
)
60 /* Both backslash and slash determine the end of a path component */
66 /* Component must not end with '.' or blank and can't be empty */
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
;
78 else if ( 2 == lpCurrent
- lpComponent
&& '.' == *lpComponent
)
80 /* Parent directory is O.K. */
81 lpComponentEnd
= lpCurrent
;
88 if ( dwFlags
& VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD
)
89 lpComponentEnd
= lpCurrent
;
92 lpComponentEnd
= lpCurrent
- 1;
97 lpComponentEnd
= lpCurrent
;
101 /* The following characters are reserved */
109 lpComponentEnd
= lpCurrent
;
113 /* Characters below ASCII 32 are not allowed */
114 if ( *lpCurrent
< ' ' )
116 lpComponentEnd
= lpCurrent
;
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
)
129 lpComponentEnd
= lpCurrent
;
134 // Empty components are not allowed
135 if ( lpComponentEnd
- lpComponent
< 1 )
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
;
150 static sal_Int32
countInitialSeparators(sal_Unicode
const * path
) {
151 sal_Unicode
const * p
= path
;
152 while (*p
== '\\' || *p
== '/') {
158 DWORD
IsValidFilePath(rtl_uString
*path
, DWORD dwFlags
, rtl_uString
**corrected
)
160 sal_Unicode
const * lpszPath
= path
->buffer
;
161 sal_Unicode
const * lpComponent
= lpszPath
;
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] )
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
;
211 if ( lpComponent
&& !*++lpComponent
)
212 lpComponent
= nullptr;
216 dwPathType
|= PATHTYPE_IS_SERVER
;
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
) )
238 else if ( *lpComponent
)
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
) )
262 lpComponent
= nullptr;
265 dwPathType
= PATHTYPE_RELATIVE
;
269 /* Anything else is an error */
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
)
294 /* If the string behind the backslash is empty, we've done */
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
)
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!
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
);
327 if ( lpLastDelimiter
> lpPath
&& *(lpLastDelimiter
- 1) != ':' )
329 *lpLastDelimiter
= 0;
334 else if ( nDelLen
&& nDelLen
- 1 < nFileBufLen
)
336 wcscpy( lpFileName
, lpLastDelimiter
+ 1 );
337 *(++lpLastDelimiter
) = 0;
338 nRemoved
= nDelLen
- 1;
346 // Undocumented in SHELL32.DLL ordinal 32
347 static LPWSTR
PathAddBackslash(LPWSTR lpPath
, sal_uInt32 nBufLen
)
349 LPWSTR lpEndPath
= nullptr;
353 std::size_t nLen
= wcslen(lpPath
);
355 if ( !nLen
|| ( lpPath
[nLen
-1] != '\\' && lpPath
[nLen
-1] != '/' && nLen
< nBufLen
- 1 ) )
357 lpEndPath
= lpPath
+ nLen
;
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
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 )
378 nLastStepRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
379 nRemoved
+= nLastStepRemoved
;
384 bool bSkipThis
= false;
386 if ( 0 == wcscmp( szFile
, L
".." ) )
391 else if ( 0 == wcscmp( szFile
, L
"." ) )
395 else if ( nSkipLevels
)
403 if ( !GetCaseCorrectPathNameEx( lpszPath
, cchBuffer
, nSkipLevels
, bCheckExistence
) )
406 PathAddBackslash( lpszPath
, cchBuffer
);
408 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
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
);
431 /* add the segment name back */
432 wcscat( lpszPath
, szFile
);
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 */
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
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;
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
);
482 static bool osl_decodeURL_( rtl_String
* strUTF8
, rtl_uString
** pstrDecodedURL
)
485 const sal_Char
*pSrcEnd
;
486 const sal_Char
*pSrc
;
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
)));
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
)
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;
528 bValidEncoded
= false;
540 rtl_string2UString( pstrDecodedURL
, pBuffer
, rtl_str_getLength(pBuffer
), RTL_TEXTENCODING_UTF8
, OSTRING_TO_OUSTRING_CVTFLAGS
);
541 OSL_ASSERT(*pstrDecodedURL
!= nullptr);
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
;
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
);
569 while ( nURLScanCount
< nURLScanLen
)
571 sal_Char cCurrent
= *pURLScan
;
575 if (!( ( cCurrent
>= 'a' && cCurrent
<= 'z' ) || ( cCurrent
>= 'A' && cCurrent
<= 'Z' ) || ( cCurrent
>= '0' && cCurrent
<= '9' ) ) )
577 sprintf( pURLDest
, "%%%02X", static_cast<unsigned char>(cCurrent
) );
601 *pURLDest
++ = cCurrent
;
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
;
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 */
635 strUTF8
->length
!= strURL
->length
&&
636 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL
->buffer
, strURL
->length
, "file:\\\\", 7 )
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
);
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 ) )
660 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\\\", 8 ) )
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 )
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 );
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
);
682 ::osl::LongPathBuffer
< sal_Unicode
> aBuf( MAX_LONG_PATH
);
683 sal_uInt32 nNewLen
= GetCaseCorrectPathName( o3tl::toW(pDecodedURL
) + nSkip
,
685 aBuf
.getBufSizeInSymbols(),
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
);
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
;
734 SAL_INFO_IF(nError
, "sal.osl",
735 "osl_getSystemPathFromFileURL: \"" << OUString(strURL
) << "\" is not an absolute FileURL");
740 rtl_uString_release( strDecodedURL
);
742 if ( osl_File_E_None
== nError
)
743 rtl_uString_assign( pustrPath
, strTempPath
);
746 rtl_uString_release( strTempPath
);
748 SAL_INFO_IF(nError
, "sal.osl",
749 "osl_getSystemPathFromFileURL: \"" << OUString(strURL
) << "\" is not a FileURL");
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
;
761 dwPathType
= IsValidFilePath(strPath
, VALIDATEPATH_ALLOW_RELATIVE
, nullptr);
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
);
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
);
802 OSL_FAIL( "Unexpected long path format!" );
803 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
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
;
819 case PATHTYPE_ABSOLUTE_UNC
:
820 rtl_uString_newFromAscii( &strTempURL
, "file:" );
821 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
822 nError
= osl_File_E_None
;
824 case PATHTYPE_ABSOLUTE_LOCAL
:
825 rtl_uString_newFromAscii( &strTempURL
, "file:///" );
826 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
827 nError
= osl_File_E_None
;
833 /* Release temp path */
834 rtl_uString_release( strTempPath
);
837 if ( osl_File_E_None
== nError
)
839 rtl_String
*strEncodedURL
= nullptr;
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 */
852 rtl_uString_release( strTempURL
);
854 SAL_INFO_IF(nError
, "sal.osl",
855 "osl_getFileURLFromSystemPath: \"" << OUString(strPath
) << "\" is not a systemPath");
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;
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
)
886 LPWSTR lpBuffer
= nullptr;
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 */
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 ;-) */
915 rtl_uString_newFromStr( &ustrSysPath
, o3tl::toU(lpBuffer
) );
916 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
920 WIN32_FIND_DATAW aFindFileData
;
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
);
939 rtl_uString_release( ustrSysPath
);
942 rtl_uString_release( ustrUNCPath
);
947 oslFileError SAL_CALL
osl_getAbsoluteFileURL( rtl_uString
* ustrBaseURL
, rtl_uString
* ustrRelativeURL
, rtl_uString
** pustrAbsoluteURL
)
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 );
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" );
968 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
969 ::osl::LongPathBuffer
< sal_Unicode
> aCurrentDir( MAX_LONG_PATH
);
970 LPWSTR lpFilePart
= nullptr;
974 Bad, bad hack, this only works if the base path
975 really exists which is not necessary according
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
);
999 if ( dwResult
>= aBuffer
.getBufSizeInSymbols() )
1000 eError
= osl_File_E_INVAL
;
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
);
1014 eError
= oslTranslateFileError( GetLastError() );
1017 if ( ustrBaseSysPath
)
1018 rtl_uString_release( ustrBaseSysPath
);
1020 if ( ustrRelSysPath
)
1021 rtl_uString_release( ustrRelSysPath
);
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: */