Bump version to 4.1-6
[LibreOffice.git] / sal / osl / w32 / file_url.cxx
blobe9be8959876819799430e518ef3ef0974e357212
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 #define UNICODE
21 #define _UNICODE
22 #define _WIN32_WINNT 0x0500
23 #include "systools/win32/uwinapi.h"
25 #include "file_url.h"
26 #include <sal/macros.h>
27 #include "file_error.h"
29 #include "rtl/alloc.h"
30 #include "osl/diagnose.h"
31 #include "osl/file.h"
32 #include "osl/mutex.h"
34 #include "path_helper.hxx"
36 #include <stdio.h>
37 #include <tchar.h>
39 #if OSL_DEBUG_LEVEL > 0
40 #define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ? (void)0 : _osl_warnFile( msg, file ) )
41 #else
42 #define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
43 #endif
45 #define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
46 #define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
47 #define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
50 //##################################################################
51 // FileURL functions
52 //##################################################################
54 extern "C" oslMutex g_CurrentDirectoryMutex; /* Initialized in dllentry.c */
55 oslMutex g_CurrentDirectoryMutex = 0;
57 //#####################################################
58 static BOOL IsValidFilePathComponent(
59 LPCTSTR lpComponent, LPCTSTR *lppComponentEnd, DWORD dwFlags)
61 LPCTSTR lpComponentEnd = NULL;
62 LPCTSTR lpCurrent = lpComponent;
63 BOOL fValid = TRUE; /* Assume success */
64 TCHAR cLast = 0;
66 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
68 while ( !lpComponentEnd && lpCurrent && lpCurrent - lpComponent < MAX_PATH )
70 switch ( *lpCurrent )
72 /* Both backslash and slash determine the end of a path component */
73 case '\0':
74 case '/':
75 case '\\':
76 switch ( cLast )
78 /* Component must not end with '.' or blank and can't be empty */
80 case '.':
81 if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
83 if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
84 1 == lpCurrent - lpComponent )
86 /* Either do allow periods anywhere, or current directory */
87 lpComponentEnd = lpCurrent;
88 break;
90 else if ( 2 == lpCurrent - lpComponent && '.' == *lpComponent )
92 /* Parent directory is O.K. */
93 lpComponentEnd = lpCurrent;
94 break;
97 case 0:
98 case ' ':
99 if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
100 lpComponentEnd = lpCurrent;
101 else
103 lpComponentEnd = lpCurrent - 1;
104 fValid = FALSE;
106 break;
107 default:
108 lpComponentEnd = lpCurrent;
109 break;
111 break;
112 /* '?' and '*' are valid wildcards but not valid file name characters */
113 case '?':
114 case '*':
115 if ( dwFlags & VALIDATEPATH_ALLOW_WILDCARDS )
116 break;
117 /* The following characters are reserved */
118 case '<':
119 case '>':
120 case '\"':
121 case '|':
122 case ':':
123 lpComponentEnd = lpCurrent;
124 fValid = FALSE;
125 break;
126 default:
127 /* Characters below ASCII 32 are not allowed */
128 if ( *lpCurrent < ' ' )
130 lpComponentEnd = lpCurrent;
131 fValid = FALSE;
133 break;
135 cLast = *lpCurrent++;
138 /* If we don't reached the end of the component the length of the component was to long
139 ( See condition of while loop ) */
140 if ( !lpComponentEnd )
142 fValid = FALSE;
143 lpComponentEnd = lpCurrent;
146 /* Test whether the component specifies a device name what is not allowed */
148 // MT: PERFORMANCE:
149 // This is very expensive. A lot of calls to _tcsicmp.
150 // in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp!
151 // Possible optimizations
152 // - Array should be const static
153 // - Sorted array, use binary search
154 // - More intelligent check for com1-9, lpt1-9
155 // Maybe make szComponent upper case, don't search case intensitive
156 // Talked to HRO: Could be removed. Shouldn't be used in OOo, and if used for something like a filename, it will lead to an error anyway.
158 if ( fValid )
160 LPCTSTR alpDeviceNames[] =
162 TEXT("CON"),
163 TEXT("PRN"),
164 TEXT("AUX"),
165 TEXT("CLOCK$"),
166 TEXT("NUL"),
167 TEXT("LPT1"),
168 TEXT("LPT2"),
169 TEXT("LPT3"),
170 TEXT("LPT4"),
171 TEXT("LPT5"),
172 TEXT("LPT6"),
173 TEXT("LPT7"),
174 TEXT("LPT8"),
175 TEXT("LPT9"),
176 TEXT("COM1"),
177 TEXT("COM2"),
178 TEXT("COM3"),
179 TEXT("COM4"),
180 TEXT("COM5"),
181 TEXT("COM6"),
182 TEXT("COM7"),
183 TEXT("COM8"),
184 TEXT("COM9")
187 TCHAR szComponent[MAX_PATH];
188 int nComponentLength;
189 LPCTSTR lpDot;
190 int i;
192 // A device name with an extension is also invalid
193 lpDot = _tcschr( lpComponent, '.' );
195 if ( !lpDot || lpDot > lpComponentEnd )
196 nComponentLength = lpComponentEnd - lpComponent;
197 else
198 nComponentLength = lpDot - lpComponent;
200 _tcsncpy( szComponent, lpComponent, nComponentLength );
201 szComponent[nComponentLength] = 0;
203 for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ )
205 if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) )
207 lpComponentEnd = lpComponent;
208 fValid = FALSE;
209 break;
215 if ( fValid )
217 // Empty components are not allowed
218 if ( lpComponentEnd - lpComponent < 1 )
219 fValid = FALSE;
221 // If we reached the end of the string NULL is returned
222 else if ( !*lpComponentEnd )
223 lpComponentEnd = NULL;
227 if ( lppComponentEnd )
228 *lppComponentEnd = lpComponentEnd;
230 return fValid;
233 //#####################################################
234 #define CHARSET_SEPARATOR TEXT("\\/")
236 DWORD IsValidFilePath(rtl_uString *path, LPCTSTR *lppError, DWORD dwFlags, rtl_uString **corrected)
238 LPCTSTR lpszPath = reinterpret_cast< LPCTSTR >(path->buffer);
239 LPCTSTR lpComponent = lpszPath;
240 BOOL fValid = TRUE;
241 DWORD dwPathType = PATHTYPE_ERROR;
242 sal_Int32 nLength = rtl_uString_getLength( path );
244 if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
245 dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
247 if ( !lpszPath )
248 fValid = FALSE;
250 DWORD dwCandidatPathType = PATHTYPE_ERROR;
252 if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1 ) )
254 /* This is long path in UNC notation */
255 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1;
256 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
258 else if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1 ) )
260 /* This is long path */
261 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1;
263 if ( _istalpha( lpComponent[0] ) && ':' == lpComponent[1] )
265 lpComponent += 2;
266 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
269 else if ( 2 == _tcsspn( lpszPath, CHARSET_SEPARATOR ) )
271 /* The UNC path notation */
272 lpComponent = lpszPath + 2;
273 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
275 else if ( _istalpha( lpszPath[0] ) && ':' == lpszPath[1] )
277 /* Local path verification. Must start with <drive>: */
278 lpComponent = lpszPath + 2;
279 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
282 if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
284 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, VALIDATEPATH_ALLOW_ELLIPSE );
286 /* So far we have a valid servername. Now let's see if we also have a network resource */
288 dwPathType = dwCandidatPathType;
290 if ( fValid )
292 if ( lpComponent && !*++lpComponent )
293 lpComponent = NULL;
295 if ( !lpComponent )
297 dwPathType |= PATHTYPE_IS_SERVER;
299 else
301 /* Now test the network resource */
303 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, 0 );
305 /* If we now reached the end of the path, everything is O.K. */
308 if ( fValid && (!lpComponent || !*++lpComponent ) )
310 lpComponent = NULL;
311 dwPathType |= PATHTYPE_IS_VOLUME;
316 else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
318 if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
319 lpComponent++;
320 else if ( *lpComponent )
321 fValid = FALSE;
323 dwPathType = dwCandidatPathType;
325 /* Now we are behind the backslash or it was a simple drive without backslash */
327 if ( fValid && !*lpComponent )
329 lpComponent = NULL;
330 dwPathType |= PATHTYPE_IS_VOLUME;
333 else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
335 /* Can be a relative path */
336 lpComponent = lpszPath;
338 /* Relative path can start with a backslash */
340 if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
342 lpComponent++;
343 if ( !*lpComponent )
344 lpComponent = NULL;
347 dwPathType = PATHTYPE_RELATIVE;
349 else
351 /* Anything else is an error */
352 fValid = FALSE;
353 lpComponent = lpszPath;
356 /* Now validate each component of the path */
357 while ( fValid && lpComponent )
359 // Correct path by merging consecutive slashes:
360 if (*lpComponent == '\\' && corrected != NULL) {
361 sal_Int32 i = lpComponent - lpszPath;
362 rtl_uString_newReplaceStrAt(corrected, path, i, 1, NULL);
363 //TODO: handle out-of-memory
364 lpszPath = reinterpret_cast< LPCTSTR >((*corrected)->buffer);
365 lpComponent = lpszPath + i;
368 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
370 if ( fValid && lpComponent )
372 lpComponent++;
374 /* If the string behind the backslash is empty, we've done */
376 if ( !*lpComponent )
377 lpComponent = NULL;
381 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
382 if ( fValid && !( dwPathType & PATHTYPE_IS_LONGPATH ) && _tcslen( lpszPath ) >= MAX_PATH )
384 fValid = FALSE;
385 lpComponent = lpszPath + MAX_PATH;
388 if ( lppError )
389 *lppError = lpComponent;
391 return fValid ? dwPathType : PATHTYPE_ERROR;
394 //#####################################################
395 static sal_Int32 PathRemoveFileSpec(LPTSTR lpPath, LPTSTR lpFileName, sal_Int32 nFileBufLen )
397 sal_Int32 nRemoved = 0;
399 if ( nFileBufLen )
401 lpFileName[0] = 0;
402 LPTSTR lpLastBkSlash = _tcsrchr( lpPath, '\\' );
403 LPTSTR lpLastSlash = _tcsrchr( lpPath, '/' );
404 LPTSTR lpLastDelimiter = lpLastSlash > lpLastBkSlash ? lpLastSlash : lpLastBkSlash;
406 if ( lpLastDelimiter )
408 sal_Int32 nDelLen = _tcslen( lpLastDelimiter );
409 if ( 1 == nDelLen )
411 if ( lpLastDelimiter > lpPath && *(lpLastDelimiter - 1) != ':' )
413 *lpLastDelimiter = 0;
414 *lpFileName = 0;
415 nRemoved = nDelLen;
418 else if ( nDelLen && nDelLen - 1 < nFileBufLen )
420 _tcscpy( lpFileName, lpLastDelimiter + 1 );
421 *(++lpLastDelimiter) = 0;
422 nRemoved = nDelLen - 1;
427 return nRemoved;
430 //#####################################################
431 // Undocumented in SHELL32.DLL ordinal 32
432 static LPTSTR PathAddBackslash(LPTSTR lpPath, sal_Int32 nBufLen)
434 LPTSTR lpEndPath = NULL;
436 if ( lpPath )
438 int nLen = _tcslen(lpPath);
440 if ( !nLen || ( lpPath[nLen-1] != '\\' && lpPath[nLen-1] != '/' && nLen < nBufLen - 1 ) )
442 lpEndPath = lpPath + nLen;
443 *lpEndPath++ = '\\';
444 *lpEndPath = 0;
447 return lpEndPath;
450 //#####################################################
451 // Same as GetLongPathName but also 95/NT4
452 static DWORD GetCaseCorrectPathNameEx(
453 LPTSTR lpszPath, // path buffer to convert
454 DWORD cchBuffer, // size of path buffer
455 DWORD nSkipLevels,
456 BOOL bCheckExistence )
458 ::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
459 sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
460 sal_Int32 nLastStepRemoved = nRemoved;
461 while ( nLastStepRemoved && szFile[0] == 0 )
463 // remove separators
464 nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
465 nRemoved += nLastStepRemoved;
468 if ( nRemoved )
470 BOOL bSkipThis = FALSE;
472 if ( 0 == _tcscmp( szFile, TEXT("..") ) )
474 bSkipThis = TRUE;
475 nSkipLevels += 1;
477 else if ( 0 == _tcscmp( szFile, TEXT(".") ) )
479 bSkipThis = TRUE;
481 else if ( nSkipLevels )
483 bSkipThis = TRUE;
484 nSkipLevels--;
486 else
487 bSkipThis = FALSE;
489 if ( !GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence ) )
490 return 0;
492 PathAddBackslash( lpszPath, cchBuffer );
494 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
495 if ( !bSkipThis )
497 if ( bCheckExistence )
499 ::osl::LongPathBuffer< WCHAR > aShortPath( MAX_LONG_PATH );
500 _tcscpy( aShortPath, lpszPath );
501 _tcscat( aShortPath, szFile );
503 WIN32_FIND_DATA aFindFileData;
504 HANDLE hFind = FindFirstFile( aShortPath, &aFindFileData );
506 if ( IsValidHandle(hFind) )
508 _tcscat( lpszPath, aFindFileData.cFileName[0] ? aFindFileData.cFileName : aFindFileData.cAlternateFileName );
510 FindClose( hFind );
512 else
513 lpszPath[0] = 0;
515 else
517 /* add the segment name back */
518 _tcscat( lpszPath, szFile );
522 else
524 /* File specification can't be removed therefore the short path is either a drive
525 or a network share. If still levels to skip are left, the path specification
526 tries to travel below the file system root */
527 if ( nSkipLevels )
528 lpszPath[0] = 0;
529 else
530 _tcsupr( lpszPath );
533 return _tcslen( lpszPath );
536 //#####################################################
537 DWORD GetCaseCorrectPathName(
538 LPCTSTR lpszShortPath, // file name
539 LPTSTR lpszLongPath, // path buffer
540 DWORD cchBuffer, // size of path buffer
541 BOOL bCheckExistence
544 /* Special handling for "\\.\" as system root */
545 if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
547 if ( cchBuffer >= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) )
549 wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
550 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
552 else
554 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
557 else if ( lpszShortPath )
559 if ( _tcslen( lpszShortPath ) <= cchBuffer )
561 _tcscpy( lpszLongPath, lpszShortPath );
562 return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
566 return 0;
570 //#############################################
571 static sal_Bool _osl_decodeURL( rtl_String* strUTF8, rtl_uString** pstrDecodedURL )
573 sal_Char *pBuffer;
574 const sal_Char *pSrcEnd;
575 const sal_Char *pSrc;
576 sal_Char *pDest;
577 sal_Int32 nSrcLen;
578 sal_Bool bValidEncoded = sal_True; /* Assume success */
580 /* The resulting decoded string length is shorter or equal to the source length */
582 nSrcLen = rtl_string_getLength(strUTF8);
583 pBuffer = reinterpret_cast<sal_Char*>(rtl_allocateMemory(nSrcLen + 1));
585 pDest = pBuffer;
586 pSrc = rtl_string_getStr(strUTF8);
587 pSrcEnd = pSrc + nSrcLen;
589 /* Now decode the URL what should result in an UTF8 string */
590 while ( bValidEncoded && pSrc < pSrcEnd )
592 switch ( *pSrc )
594 case '%':
596 sal_Char aToken[3];
597 sal_Char aChar;
599 pSrc++;
600 aToken[0] = *pSrc++;
601 aToken[1] = *pSrc++;
602 aToken[2] = 0;
604 aChar = (sal_Char)strtoul( aToken, NULL, 16 );
606 /* The chars are path delimiters and must not be encoded */
608 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
609 bValidEncoded = sal_False;
610 else
611 *pDest++ = aChar;
613 break;
614 default:
615 *pDest++ = *pSrc++;
616 break;
620 *pDest++ = 0;
622 if ( bValidEncoded )
624 rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
625 OSL_ASSERT(*pstrDecodedURL != 0);
628 rtl_freeMemory( pBuffer );
630 return bValidEncoded;
633 //#############################################
634 static void _osl_encodeURL( rtl_uString *strURL, rtl_String **pstrEncodedURL )
636 /* Encode non ascii characters within the URL */
638 rtl_String *strUTF8 = NULL;
639 sal_Char *pszEncodedURL;
640 const sal_Char *pURLScan;
641 sal_Char *pURLDest;
642 sal_Int32 nURLScanLen;
643 sal_Int32 nURLScanCount;
645 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
647 pszEncodedURL = (sal_Char*) rtl_allocateMemory( (rtl_string_getLength( strUTF8 ) * 3 + 1) * sizeof(sal_Char) );
649 pURLDest = pszEncodedURL;
650 pURLScan = rtl_string_getStr( strUTF8 );
651 nURLScanLen = rtl_string_getLength( strUTF8 );
652 nURLScanCount = 0;
654 while ( nURLScanCount < nURLScanLen )
656 sal_Char cCurrent = *pURLScan;
657 switch ( cCurrent )
659 default:
660 if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
662 sprintf( pURLDest, "%%%02X", (unsigned char)cCurrent );
663 pURLDest += 3;
664 break;
666 case '!':
667 case '\'':
668 case '(':
669 case ')':
670 case '*':
671 case '-':
672 case '.':
673 case '_':
674 case '~':
675 case '$':
676 case '&':
677 case '+':
678 case ',':
679 case '=':
680 case '@':
681 case ':':
682 case '/':
683 case '\\':
684 case '|':
685 *pURLDest++ = cCurrent;
686 break;
687 case 0:
688 break;
691 pURLScan++;
692 nURLScanCount++;
695 *pURLDest = 0;
697 rtl_string_release( strUTF8 );
698 rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL );
699 rtl_freeMemory( pszEncodedURL );
702 //#############################################
704 oslFileError _osl_getSystemPathFromFileURL( rtl_uString *strURL, rtl_uString **pustrPath, sal_Bool bAllowRelative )
706 rtl_String *strUTF8 = NULL;
707 rtl_uString *strDecodedURL = NULL;
708 rtl_uString *strTempPath = NULL;
709 const sal_Unicode *pDecodedURL;
710 sal_uInt32 nDecodedLen;
711 sal_Bool bValidEncoded;
712 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
714 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
715 having a mixed encoded URL later */
717 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
719 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
721 OSL_ENSURE_FILE(
722 strUTF8->length == strURL->length ||
723 0 != rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 )
724 ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL );
726 bValidEncoded = _osl_decodeURL( strUTF8, &strDecodedURL );
728 /* Release the encoded UTF8 string */
729 rtl_string_release( strUTF8 );
731 if ( bValidEncoded )
733 /* Replace backslashes and pipes */
735 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' );
736 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' );
738 pDecodedURL = rtl_uString_getStr( strDecodedURL );
739 nDecodedLen = rtl_uString_getLength( strDecodedURL );
741 /* Must start with "file://" */
742 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
744 sal_uInt32 nSkip;
746 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
747 nSkip = 8;
748 else if (
749 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
750 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
752 nSkip = 17;
753 else
754 nSkip = 5;
756 /* Indicates local root */
757 if ( nDecodedLen == nSkip )
758 rtl_uString_newFromStr_WithLength( &strTempPath, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 );
759 else
761 /* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
762 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
764 rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
766 else
768 ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
769 sal_uInt32 nNewLen = GetCaseCorrectPathName( reinterpret_cast<LPCTSTR>(pDecodedURL + nSkip),
770 ::osl::mingw_reinterpret_cast<LPTSTR>(aBuf),
771 aBuf.getBufSizeInSymbols(),
772 sal_False );
774 if ( nNewLen <= MAX_PATH - 12
775 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1, SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 )
776 || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1, SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1 ) )
778 rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
780 else if ( pDecodedURL[nSkip] == (sal_Unicode)'\\' && pDecodedURL[nSkip+1] == (sal_Unicode)'\\' )
782 /* it should be an UNC path, use the according prefix */
783 rtl_uString *strSuffix = NULL;
784 rtl_uString *strPrefix = NULL;
785 rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
786 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
788 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
790 rtl_uString_release( strPrefix );
791 rtl_uString_release( strSuffix );
793 else
795 rtl_uString *strSuffix = NULL;
796 rtl_uString *strPrefix = NULL;
797 rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1 );
798 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
800 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
802 rtl_uString_release( strPrefix );
803 rtl_uString_release( strSuffix );
808 if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
809 nError = osl_File_E_None;
811 else if ( bAllowRelative ) /* This maybe a relative file URL */
813 /* In future the relative path could be converted to absolute if it is too long */
814 rtl_uString_assign( &strTempPath, strDecodedURL );
816 if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
817 nError = osl_File_E_None;
820 else
821 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL );
826 if ( strDecodedURL )
827 rtl_uString_release( strDecodedURL );
829 if ( osl_File_E_None == nError )
830 rtl_uString_assign( pustrPath, strTempPath );
832 if ( strTempPath )
833 rtl_uString_release( strTempPath );
836 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL );
839 return nError;
842 //#############################################
843 oslFileError _osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
845 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
846 rtl_uString *strTempURL = NULL;
847 DWORD dwPathType = PATHTYPE_ERROR;
849 if (strPath)
850 dwPathType = IsValidFilePath(strPath, NULL, VALIDATEPATH_ALLOW_RELATIVE, NULL);
852 if (dwPathType)
854 rtl_uString *strTempPath = NULL;
856 if ( dwPathType & PATHTYPE_IS_LONGPATH )
858 rtl_uString *strBuffer = NULL;
859 sal_uInt32 nIgnore = 0;
860 sal_uInt32 nLength = 0;
862 /* the path has the longpath prefix, lets remove it */
863 switch ( dwPathType & PATHTYPE_MASK_TYPE )
865 case PATHTYPE_ABSOLUTE_UNC:
866 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
867 OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
869 /* generate the normal UNC path */
870 nLength = rtl_uString_getLength( strPath );
871 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
872 strBuffer->buffer[0] = '\\';
874 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
875 rtl_uString_release( strBuffer );
876 break;
878 case PATHTYPE_ABSOLUTE_LOCAL:
879 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1;
880 OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
882 /* generate the normal path */
883 nLength = rtl_uString_getLength( strPath );
884 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
886 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
887 rtl_uString_release( strBuffer );
888 break;
890 default:
891 OSL_FAIL( "Unexpected long path format!" );
892 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
893 break;
896 else
898 /* Replace backslashes */
899 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
902 switch ( dwPathType & PATHTYPE_MASK_TYPE )
904 case PATHTYPE_RELATIVE:
905 rtl_uString_assign( &strTempURL, strTempPath );
906 nError = osl_File_E_None;
907 break;
908 case PATHTYPE_ABSOLUTE_UNC:
909 rtl_uString_newFromAscii( &strTempURL, "file:" );
910 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
911 nError = osl_File_E_None;
912 break;
913 case PATHTYPE_ABSOLUTE_LOCAL:
914 rtl_uString_newFromAscii( &strTempURL, "file:///" );
915 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
916 nError = osl_File_E_None;
917 break;
918 default:
919 break;
922 /* Release temp path */
923 rtl_uString_release( strTempPath );
926 if ( osl_File_E_None == nError )
928 rtl_String *strEncodedURL = NULL;
930 /* Encode the URL */
931 _osl_encodeURL( strTempURL, &strEncodedURL );
933 /* Provide URL via unicode string */
934 rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
935 OSL_ASSERT(*pstrURL != 0);
936 rtl_string_release( strEncodedURL );
939 /* Release temp URL */
940 if ( strTempURL )
941 rtl_uString_release( strTempURL );
944 OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath );
946 return nError;
949 //#####################################################
950 oslFileError SAL_CALL osl_getFileURLFromSystemPath(
951 rtl_uString* ustrPath, rtl_uString** pustrURL )
953 return _osl_getFileURLFromSystemPath( ustrPath, pustrURL );
956 //#####################################################
957 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
958 rtl_uString *ustrURL, rtl_uString **pustrPath)
960 return _osl_getSystemPathFromFileURL( ustrURL, pustrPath, sal_True );
963 //#####################################################
964 oslFileError SAL_CALL osl_searchFileURL(
965 rtl_uString *ustrFileName,
966 rtl_uString *ustrSystemSearchPath,
967 rtl_uString **pustrPath)
969 rtl_uString *ustrUNCPath = NULL;
970 rtl_uString *ustrSysPath = NULL;
971 oslFileError error;
973 /* First try to interpret the file name as an URL even a relative one */
974 error = _osl_getSystemPathFromFileURL( ustrFileName, &ustrUNCPath, sal_True );
976 /* So far we either have an UNC path or something invalid
977 Now create a system path */
978 if ( osl_File_E_None == error )
979 error = _osl_getSystemPathFromFileURL( ustrUNCPath, &ustrSysPath, sal_True );
981 if ( osl_File_E_None == error )
983 DWORD nBufferLength;
984 DWORD dwResult;
985 LPTSTR lpBuffer = NULL;
986 LPTSTR lpszFilePart;
988 /* Repeat calling SearchPath ...
989 Start with MAX_PATH for the buffer. In most cases this
990 will be enough and does not force the loop to runtwice */
991 dwResult = MAX_PATH;
995 /* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
996 LPCTSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? reinterpret_cast<LPCTSTR>(ustrSystemSearchPath->buffer) : NULL;
997 LPCTSTR lpszSearchFile = reinterpret_cast<LPCTSTR>(ustrSysPath->buffer);
999 /* Allocate space for buffer according to previous returned count of required chars */
1000 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
1001 nBufferLength = dwResult + 1;
1002 lpBuffer = lpBuffer ?
1003 reinterpret_cast<LPTSTR>(rtl_reallocateMemory(lpBuffer, nBufferLength * sizeof(TCHAR))) :
1004 reinterpret_cast<LPTSTR>(rtl_allocateMemory(nBufferLength * sizeof(TCHAR)));
1006 dwResult = SearchPath( lpszSearchPath, lpszSearchFile, NULL, nBufferLength, lpBuffer, &lpszFilePart );
1007 } while ( dwResult && dwResult >= nBufferLength );
1009 /* ... until an error occures or buffer is large enough.
1010 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
1012 if ( dwResult )
1014 rtl_uString_newFromStr( &ustrSysPath, reinterpret_cast<const sal_Unicode*>(lpBuffer) );
1015 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1017 else
1019 WIN32_FIND_DATA aFindFileData;
1020 HANDLE hFind;
1022 /* something went wrong, perhaps the path was absolute */
1023 error = oslTranslateFileError( GetLastError() );
1025 hFind = FindFirstFile( reinterpret_cast<LPCTSTR>(ustrSysPath->buffer), &aFindFileData );
1027 if ( IsValidHandle(hFind) )
1029 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1030 FindClose( hFind );
1034 rtl_freeMemory( lpBuffer );
1037 if ( ustrSysPath )
1038 rtl_uString_release( ustrSysPath );
1040 if ( ustrUNCPath )
1041 rtl_uString_release( ustrUNCPath );
1043 return error;
1046 //#####################################################
1048 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
1050 oslFileError eError;
1051 rtl_uString *ustrRelSysPath = NULL;
1052 rtl_uString *ustrBaseSysPath = NULL;
1054 if ( ustrBaseURL && ustrBaseURL->length )
1056 eError = _osl_getSystemPathFromFileURL( ustrBaseURL, &ustrBaseSysPath, sal_False );
1057 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1059 eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_True );
1061 else
1063 eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_False );
1064 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1067 if ( !eError )
1069 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
1070 ::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
1071 LPTSTR lpFilePart = NULL;
1072 DWORD dwResult;
1074 /*@@@ToDo
1075 Bad, bad hack, this only works if the base path
1076 really exists which is not necessary according
1077 to RFC2396
1078 The whole FileURL implementation should be merged
1079 with the rtl/uri class.
1081 if ( ustrBaseSysPath )
1083 osl_acquireMutex( g_CurrentDirectoryMutex );
1085 GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aCurrentDir) );
1086 SetCurrentDirectoryW( reinterpret_cast<LPCWSTR>(ustrBaseSysPath->buffer) );
1089 dwResult = GetFullPathNameW( reinterpret_cast<LPCWSTR>(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), &lpFilePart );
1091 if ( ustrBaseSysPath )
1093 SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast<LPCWSTR>(aCurrentDir) );
1095 osl_releaseMutex( g_CurrentDirectoryMutex );
1098 if ( dwResult )
1100 if ( dwResult >= aBuffer.getBufSizeInSymbols() )
1101 eError = osl_File_E_INVAL;
1102 else
1104 rtl_uString *ustrAbsSysPath = NULL;
1106 rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
1108 eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
1110 if ( ustrAbsSysPath )
1111 rtl_uString_release( ustrAbsSysPath );
1114 else
1115 eError = oslTranslateFileError( GetLastError() );
1118 if ( ustrBaseSysPath )
1119 rtl_uString_release( ustrBaseSysPath );
1121 if ( ustrRelSysPath )
1122 rtl_uString_release( ustrRelSysPath );
1124 return eError;
1127 //#####################################################
1128 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
1130 rtl_uString_newFromString(strValid, strRequested);
1131 return osl_File_E_None;
1134 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */