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 // 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] == '\\')
372 return true; // "\\" -> now the server name or "." or "?" will append
373 else if (szPath
[2] == '?' && szPath
[3] == '\\')
376 return wcscmp(szFile
, L
"UNC") == 0; // "\\?\" -> now "UNC" will append
379 if (wcsncmp(szPath
+ 4, L
"UNC\\", 4) == 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
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
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 )
410 nLastStepRemoved
= PathRemoveFileSpec( lpszPath
, szFile
, MAX_PATH
+ 1 );
411 nRemoved
+= nLastStepRemoved
;
416 bool bSkipThis
= false;
418 if ( 0 == wcscmp( szFile
, L
".." ) )
423 else if ( 0 == wcscmp( szFile
, L
"." ) )
427 else if ( nSkipLevels
)
435 if ( !GetCaseCorrectPathNameEx( lpszPath
, cchBuffer
, nSkipLevels
, bCheckExistence
) )
438 PathAddBackslash( lpszPath
, cchBuffer
);
440 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
443 if ( bCheckExistence
)
446 if (IsPathSpecialPrefix(lpszPath
, szFile
))
448 /* add the segment name back */
449 wcscat(lpszPath
, szFile
);
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
);
474 /* add the segment name back */
475 wcscat( lpszPath
, szFile
);
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 */
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
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;
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
);
525 static bool osl_decodeURL_( rtl_String
* strUTF8
, rtl_uString
** pstrDecodedURL
)
528 const sal_Char
*pSrcEnd
;
529 const sal_Char
*pSrc
;
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
)));
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
)
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;
571 bValidEncoded
= false;
583 rtl_string2UString( pstrDecodedURL
, pBuffer
, rtl_str_getLength(pBuffer
), RTL_TEXTENCODING_UTF8
, OSTRING_TO_OUSTRING_CVTFLAGS
);
584 OSL_ASSERT(*pstrDecodedURL
!= nullptr);
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
;
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
);
612 while ( nURLScanCount
< nURLScanLen
)
614 sal_Char cCurrent
= *pURLScan
;
618 if (!( ( cCurrent
>= 'a' && cCurrent
<= 'z' ) || ( cCurrent
>= 'A' && cCurrent
<= 'Z' ) || ( cCurrent
>= '0' && cCurrent
<= '9' ) ) )
620 sprintf( pURLDest
, "%%%02X", static_cast<unsigned char>(cCurrent
) );
644 *pURLDest
++ = cCurrent
;
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
;
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 */
678 strUTF8
->length
!= strURL
->length
&&
679 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL
->buffer
, strURL
->length
, "file:\\\\", 7 )
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
);
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 ) )
703 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL
, nDecodedLen
, "file:\\\\\\", 8 ) )
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 )
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 );
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
);
725 ::osl::LongPathBuffer
< sal_Unicode
> aBuf( MAX_LONG_PATH
);
726 sal_uInt32 nNewLen
= GetCaseCorrectPathName( o3tl::toW(pDecodedURL
) + nSkip
,
728 aBuf
.getBufSizeInSymbols(),
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
);
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
;
777 SAL_INFO_IF(nError
, "sal.osl",
778 "osl_getSystemPathFromFileURL: \"" << OUString(strURL
) << "\" is not an absolute FileURL");
783 rtl_uString_release( strDecodedURL
);
785 if ( osl_File_E_None
== nError
)
786 rtl_uString_assign( pustrPath
, strTempPath
);
789 rtl_uString_release( strTempPath
);
791 SAL_INFO_IF(nError
, "sal.osl",
792 "osl_getSystemPathFromFileURL: \"" << OUString(strURL
) << "\" is not a FileURL");
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
;
804 dwPathType
= IsValidFilePath(strPath
, VALIDATEPATH_ALLOW_RELATIVE
, nullptr);
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
);
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
);
845 OSL_FAIL( "Unexpected long path format!" );
846 rtl_uString_newReplace( &strTempPath
, strPath
, '\\', '/' );
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
;
862 case PATHTYPE_ABSOLUTE_UNC
:
863 rtl_uString_newFromAscii( &strTempURL
, "file:" );
864 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
865 nError
= osl_File_E_None
;
867 case PATHTYPE_ABSOLUTE_LOCAL
:
868 rtl_uString_newFromAscii( &strTempURL
, "file:///" );
869 rtl_uString_newConcat( &strTempURL
, strTempURL
, strTempPath
);
870 nError
= osl_File_E_None
;
876 /* Release temp path */
877 rtl_uString_release( strTempPath
);
880 if ( osl_File_E_None
== nError
)
882 rtl_String
*strEncodedURL
= nullptr;
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 */
895 rtl_uString_release( strTempURL
);
897 SAL_INFO_IF(nError
, "sal.osl",
898 "osl_getFileURLFromSystemPath: \"" << OUString(strPath
) << "\" is not a systemPath");
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;
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
)
929 LPWSTR lpBuffer
= nullptr;
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 */
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 ;-) */
958 rtl_uString_newFromStr( &ustrSysPath
, o3tl::toU(lpBuffer
) );
959 error
= osl_getFileURLFromSystemPath( ustrSysPath
, pustrPath
);
963 WIN32_FIND_DATAW aFindFileData
;
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
);
982 rtl_uString_release( ustrSysPath
);
985 rtl_uString_release( ustrUNCPath
);
990 oslFileError SAL_CALL
osl_getAbsoluteFileURL( rtl_uString
* ustrBaseURL
, rtl_uString
* ustrRelativeURL
, rtl_uString
** pustrAbsoluteURL
)
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 );
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" );
1011 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
1012 ::osl::LongPathBuffer
< sal_Unicode
> aCurrentDir( MAX_LONG_PATH
);
1013 LPWSTR lpFilePart
= nullptr;
1017 Bad, bad hack, this only works if the base path
1018 really exists which is not necessary according
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
);
1042 if ( dwResult
>= aBuffer
.getBufSizeInSymbols() )
1043 eError
= osl_File_E_INVAL
;
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
);
1057 eError
= oslTranslateFileError( GetLastError() );
1060 if ( ustrBaseSysPath
)
1061 rtl_uString_release( ustrBaseSysPath
);
1063 if ( ustrRelSysPath
)
1064 rtl_uString_release( ustrRelSysPath
);
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: */