Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / sal / osl / w32 / file_url.cxx
blob978b8db29046fff7bc37343ca6cb96ebcc0dc930
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 #include "systools/win32/uwinapi.h"
24 #include "file_url.h"
25 #include <sal/macros.h>
26 #include "file_error.h"
28 #include "rtl/alloc.h"
29 #include "osl/diagnose.h"
30 #include "osl/file.h"
31 #include "osl/mutex.h"
33 #include "path_helper.hxx"
35 #include <stdio.h>
36 #include <tchar.h>
38 #if OSL_DEBUG_LEVEL > 0
39 #define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ? (void)0 : _osl_warnFile( msg, file ) )
40 #else
41 #define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
42 #endif
44 #define WSTR_SYSTEM_ROOT_PATH L"\\\\.\\"
45 #define WSTR_LONG_PATH_PREFIX L"\\\\?\\"
46 #define WSTR_LONG_PATH_PREFIX_UNC L"\\\\?\\UNC\\"
49 //##################################################################
50 // FileURL functions
51 //##################################################################
53 extern "C" oslMutex g_CurrentDirectoryMutex; /* Initialized in dllentry.c */
54 oslMutex g_CurrentDirectoryMutex = 0;
56 //#####################################################
57 static BOOL IsValidFilePathComponent(
58 LPCTSTR lpComponent, LPCTSTR *lppComponentEnd, DWORD dwFlags)
60 LPCTSTR lpComponentEnd = NULL;
61 LPCTSTR lpCurrent = lpComponent;
62 BOOL fValid = TRUE; /* Assume success */
63 TCHAR cLast = 0;
65 /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
67 while ( !lpComponentEnd && lpCurrent && lpCurrent - lpComponent < MAX_PATH )
69 switch ( *lpCurrent )
71 /* Both backslash and slash determine the end of a path component */
72 case '\0':
73 case '/':
74 case '\\':
75 switch ( cLast )
77 /* Component must not end with '.' or blank and can't be empty */
79 case '.':
80 if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
82 if ( (dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD) ||
83 1 == lpCurrent - lpComponent )
85 /* Either do allow periods anywhere, or current directory */
86 lpComponentEnd = lpCurrent;
87 break;
89 else if ( 2 == lpCurrent - lpComponent && '.' == *lpComponent )
91 /* Parent directory is O.K. */
92 lpComponentEnd = lpCurrent;
93 break;
96 case 0:
97 case ' ':
98 if ( dwFlags & VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD )
99 lpComponentEnd = lpCurrent;
100 else
102 lpComponentEnd = lpCurrent - 1;
103 fValid = FALSE;
105 break;
106 default:
107 lpComponentEnd = lpCurrent;
108 break;
110 break;
111 /* '?' and '*' are valid wildcards but not valid file name characters */
112 case '?':
113 case '*':
114 if ( dwFlags & VALIDATEPATH_ALLOW_WILDCARDS )
115 break;
116 /* The following characters are reserved */
117 case '<':
118 case '>':
119 case '\"':
120 case '|':
121 case ':':
122 lpComponentEnd = lpCurrent;
123 fValid = FALSE;
124 break;
125 default:
126 /* Characters below ASCII 32 are not allowed */
127 if ( *lpCurrent < ' ' )
129 lpComponentEnd = lpCurrent;
130 fValid = FALSE;
132 break;
134 cLast = *lpCurrent++;
137 /* If we don't reached the end of the component the length of the component was to long
138 ( See condition of while loop ) */
139 if ( !lpComponentEnd )
141 fValid = FALSE;
142 lpComponentEnd = lpCurrent;
145 /* Test whether the component specifies a device name what is not allowed */
147 // MT: PERFORMANCE:
148 // This is very expensive. A lot of calls to _tcsicmp.
149 // in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp!
150 // Possible optimizations
151 // - Array should be const static
152 // - Sorted array, use binary search
153 // - More intelligent check for com1-9, lpt1-9
154 // Maybe make szComponent upper case, don't search case intensitive
155 // 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.
157 if ( fValid )
159 LPCTSTR alpDeviceNames[] =
161 TEXT("CON"),
162 TEXT("PRN"),
163 TEXT("AUX"),
164 TEXT("CLOCK$"),
165 TEXT("NUL"),
166 TEXT("LPT1"),
167 TEXT("LPT2"),
168 TEXT("LPT3"),
169 TEXT("LPT4"),
170 TEXT("LPT5"),
171 TEXT("LPT6"),
172 TEXT("LPT7"),
173 TEXT("LPT8"),
174 TEXT("LPT9"),
175 TEXT("COM1"),
176 TEXT("COM2"),
177 TEXT("COM3"),
178 TEXT("COM4"),
179 TEXT("COM5"),
180 TEXT("COM6"),
181 TEXT("COM7"),
182 TEXT("COM8"),
183 TEXT("COM9")
186 TCHAR szComponent[MAX_PATH];
187 int nComponentLength;
188 LPCTSTR lpDot;
189 int i;
191 // A device name with an extension is also invalid
192 lpDot = _tcschr( lpComponent, '.' );
194 if ( !lpDot || lpDot > lpComponentEnd )
195 nComponentLength = lpComponentEnd - lpComponent;
196 else
197 nComponentLength = lpDot - lpComponent;
199 _tcsncpy( szComponent, lpComponent, nComponentLength );
200 szComponent[nComponentLength] = 0;
202 for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ )
204 if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) )
206 lpComponentEnd = lpComponent;
207 fValid = FALSE;
208 break;
214 if ( fValid )
216 // Empty components are not allowed
217 if ( lpComponentEnd - lpComponent < 1 )
218 fValid = FALSE;
220 // If we reached the end of the string NULL is returned
221 else if ( !*lpComponentEnd )
222 lpComponentEnd = NULL;
226 if ( lppComponentEnd )
227 *lppComponentEnd = lpComponentEnd;
229 return fValid;
232 //#####################################################
233 #define CHARSET_SEPARATOR TEXT("\\/")
235 DWORD IsValidFilePath(rtl_uString *path, LPCTSTR *lppError, DWORD dwFlags, rtl_uString **corrected)
237 LPCTSTR lpszPath = reinterpret_cast< LPCTSTR >(path->buffer);
238 LPCTSTR lpComponent = lpszPath;
239 BOOL fValid = TRUE;
240 DWORD dwPathType = PATHTYPE_ERROR;
241 sal_Int32 nLength = rtl_uString_getLength( path );
243 if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
244 dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
246 if ( !lpszPath )
247 fValid = FALSE;
249 DWORD dwCandidatPathType = PATHTYPE_ERROR;
251 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 ) )
253 /* This is long path in UNC notation */
254 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX_UNC) - 1;
255 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
257 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 ) )
259 /* This is long path */
260 lpComponent = lpszPath + SAL_N_ELEMENTS(WSTR_LONG_PATH_PREFIX) - 1;
262 if ( _istalpha( lpComponent[0] ) && ':' == lpComponent[1] )
264 lpComponent += 2;
265 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
268 else if ( 2 == _tcsspn( lpszPath, CHARSET_SEPARATOR ) )
270 /* The UNC path notation */
271 lpComponent = lpszPath + 2;
272 dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
274 else if ( _istalpha( lpszPath[0] ) && ':' == lpszPath[1] )
276 /* Local path verification. Must start with <drive>: */
277 lpComponent = lpszPath + 2;
278 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
281 if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
283 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, VALIDATEPATH_ALLOW_ELLIPSE );
285 /* So far we have a valid servername. Now let's see if we also have a network resource */
287 dwPathType = dwCandidatPathType;
289 if ( fValid )
291 if ( lpComponent && !*++lpComponent )
292 lpComponent = NULL;
294 if ( !lpComponent )
296 dwPathType |= PATHTYPE_IS_SERVER;
298 else
300 /* Now test the network resource */
302 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, 0 );
304 /* If we now reached the end of the path, everything is O.K. */
307 if ( fValid && (!lpComponent || !*++lpComponent ) )
309 lpComponent = NULL;
310 dwPathType |= PATHTYPE_IS_VOLUME;
315 else if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
317 if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
318 lpComponent++;
319 else if ( *lpComponent )
320 fValid = FALSE;
322 dwPathType = dwCandidatPathType;
324 /* Now we are behind the backslash or it was a simple drive without backslash */
326 if ( fValid && !*lpComponent )
328 lpComponent = NULL;
329 dwPathType |= PATHTYPE_IS_VOLUME;
332 else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
334 /* Can be a relative path */
335 lpComponent = lpszPath;
337 /* Relative path can start with a backslash */
339 if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
341 lpComponent++;
342 if ( !*lpComponent )
343 lpComponent = NULL;
346 dwPathType = PATHTYPE_RELATIVE;
348 else
350 /* Anything else is an error */
351 fValid = FALSE;
352 lpComponent = lpszPath;
355 /* Now validate each component of the path */
356 while ( fValid && lpComponent )
358 // Correct path by merging consecutive slashes:
359 if (*lpComponent == '\\' && corrected != NULL) {
360 sal_Int32 i = lpComponent - lpszPath;
361 rtl_uString_newReplaceStrAt(corrected, path, i, 1, NULL);
362 //TODO: handle out-of-memory
363 lpszPath = reinterpret_cast< LPCTSTR >((*corrected)->buffer);
364 lpComponent = lpszPath + i;
367 fValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags | VALIDATEPATH_ALLOW_INVALID_SPACE_AND_PERIOD);
369 if ( fValid && lpComponent )
371 lpComponent++;
373 /* If the string behind the backslash is empty, we've done */
375 if ( !*lpComponent )
376 lpComponent = NULL;
380 /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
381 if ( fValid && !( dwPathType & PATHTYPE_IS_LONGPATH ) && _tcslen( lpszPath ) >= MAX_PATH )
383 fValid = FALSE;
384 lpComponent = lpszPath + MAX_PATH;
387 if ( lppError )
388 *lppError = lpComponent;
390 return fValid ? dwPathType : PATHTYPE_ERROR;
393 //#####################################################
394 static sal_Int32 PathRemoveFileSpec(LPTSTR lpPath, LPTSTR lpFileName, sal_Int32 nFileBufLen )
396 sal_Int32 nRemoved = 0;
398 if ( nFileBufLen )
400 lpFileName[0] = 0;
401 LPTSTR lpLastBkSlash = _tcsrchr( lpPath, '\\' );
402 LPTSTR lpLastSlash = _tcsrchr( lpPath, '/' );
403 LPTSTR lpLastDelimiter = lpLastSlash > lpLastBkSlash ? lpLastSlash : lpLastBkSlash;
405 if ( lpLastDelimiter )
407 sal_Int32 nDelLen = _tcslen( lpLastDelimiter );
408 if ( 1 == nDelLen )
410 if ( lpLastDelimiter > lpPath && *(lpLastDelimiter - 1) != ':' )
412 *lpLastDelimiter = 0;
413 *lpFileName = 0;
414 nRemoved = nDelLen;
417 else if ( nDelLen && nDelLen - 1 < nFileBufLen )
419 _tcscpy( lpFileName, lpLastDelimiter + 1 );
420 *(++lpLastDelimiter) = 0;
421 nRemoved = nDelLen - 1;
426 return nRemoved;
429 //#####################################################
430 // Undocumented in SHELL32.DLL ordinal 32
431 static LPTSTR PathAddBackslash(LPTSTR lpPath, sal_Int32 nBufLen)
433 LPTSTR lpEndPath = NULL;
435 if ( lpPath )
437 int nLen = _tcslen(lpPath);
439 if ( !nLen || ( lpPath[nLen-1] != '\\' && lpPath[nLen-1] != '/' && nLen < nBufLen - 1 ) )
441 lpEndPath = lpPath + nLen;
442 *lpEndPath++ = '\\';
443 *lpEndPath = 0;
446 return lpEndPath;
449 //#####################################################
450 // Same as GetLongPathName but also 95/NT4
451 static DWORD GetCaseCorrectPathNameEx(
452 LPTSTR lpszPath, // path buffer to convert
453 DWORD cchBuffer, // size of path buffer
454 DWORD nSkipLevels,
455 BOOL bCheckExistence )
457 ::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
458 sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
459 sal_Int32 nLastStepRemoved = nRemoved;
460 while ( nLastStepRemoved && szFile[0] == 0 )
462 // remove separators
463 nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
464 nRemoved += nLastStepRemoved;
467 if ( nRemoved )
469 BOOL bSkipThis = FALSE;
471 if ( 0 == _tcscmp( szFile, TEXT("..") ) )
473 bSkipThis = TRUE;
474 nSkipLevels += 1;
476 else if ( 0 == _tcscmp( szFile, TEXT(".") ) )
478 bSkipThis = TRUE;
480 else if ( nSkipLevels )
482 bSkipThis = TRUE;
483 nSkipLevels--;
485 else
486 bSkipThis = FALSE;
488 if ( !GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence ) )
489 return 0;
491 PathAddBackslash( lpszPath, cchBuffer );
493 /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
494 if ( !bSkipThis )
496 if ( bCheckExistence )
498 ::osl::LongPathBuffer< WCHAR > aShortPath( MAX_LONG_PATH );
499 _tcscpy( aShortPath, lpszPath );
500 _tcscat( aShortPath, szFile );
502 WIN32_FIND_DATA aFindFileData;
503 HANDLE hFind = FindFirstFile( aShortPath, &aFindFileData );
505 if ( IsValidHandle(hFind) )
507 _tcscat( lpszPath, aFindFileData.cFileName[0] ? aFindFileData.cFileName : aFindFileData.cAlternateFileName );
509 FindClose( hFind );
511 else
512 lpszPath[0] = 0;
514 else
516 /* add the segment name back */
517 _tcscat( lpszPath, szFile );
521 else
523 /* File specification can't be removed therefore the short path is either a drive
524 or a network share. If still levels to skip are left, the path specification
525 tries to travel below the file system root */
526 if ( nSkipLevels )
527 lpszPath[0] = 0;
528 else
529 _tcsupr( lpszPath );
532 return _tcslen( lpszPath );
535 //#####################################################
536 DWORD GetCaseCorrectPathName(
537 LPCTSTR lpszShortPath, // file name
538 LPTSTR lpszLongPath, // path buffer
539 DWORD cchBuffer, // size of path buffer
540 BOOL bCheckExistence
543 /* Special handling for "\\.\" as system root */
544 if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
546 if ( cchBuffer >= SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) )
548 wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
549 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
551 else
553 return SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1;
556 else if ( lpszShortPath )
558 if ( _tcslen( lpszShortPath ) <= cchBuffer )
560 _tcscpy( lpszLongPath, lpszShortPath );
561 return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
565 return 0;
569 //#############################################
570 static sal_Bool _osl_decodeURL( rtl_String* strUTF8, rtl_uString** pstrDecodedURL )
572 sal_Char *pBuffer;
573 const sal_Char *pSrcEnd;
574 const sal_Char *pSrc;
575 sal_Char *pDest;
576 sal_Int32 nSrcLen;
577 sal_Bool bValidEncoded = sal_True; /* Assume success */
579 /* The resulting decoded string length is shorter or equal to the source length */
581 nSrcLen = rtl_string_getLength(strUTF8);
582 pBuffer = reinterpret_cast<sal_Char*>(rtl_allocateMemory(nSrcLen + 1));
584 pDest = pBuffer;
585 pSrc = rtl_string_getStr(strUTF8);
586 pSrcEnd = pSrc + nSrcLen;
588 /* Now decode the URL what should result in an UTF8 string */
589 while ( bValidEncoded && pSrc < pSrcEnd )
591 switch ( *pSrc )
593 case '%':
595 sal_Char aToken[3];
596 sal_Char aChar;
598 pSrc++;
599 aToken[0] = *pSrc++;
600 aToken[1] = *pSrc++;
601 aToken[2] = 0;
603 aChar = (sal_Char)strtoul( aToken, NULL, 16 );
605 /* The chars are path delimiters and must not be encoded */
607 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
608 bValidEncoded = sal_False;
609 else
610 *pDest++ = aChar;
612 break;
613 default:
614 *pDest++ = *pSrc++;
615 break;
619 *pDest++ = 0;
621 if ( bValidEncoded )
623 rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
624 OSL_ASSERT(*pstrDecodedURL != 0);
627 rtl_freeMemory( pBuffer );
629 return bValidEncoded;
632 //#############################################
633 static void _osl_encodeURL( rtl_uString *strURL, rtl_String **pstrEncodedURL )
635 /* Encode non ascii characters within the URL */
637 rtl_String *strUTF8 = NULL;
638 sal_Char *pszEncodedURL;
639 const sal_Char *pURLScan;
640 sal_Char *pURLDest;
641 sal_Int32 nURLScanLen;
642 sal_Int32 nURLScanCount;
644 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
646 pszEncodedURL = (sal_Char*) rtl_allocateMemory( (rtl_string_getLength( strUTF8 ) * 3 + 1) * sizeof(sal_Char) );
648 pURLDest = pszEncodedURL;
649 pURLScan = rtl_string_getStr( strUTF8 );
650 nURLScanLen = rtl_string_getLength( strUTF8 );
651 nURLScanCount = 0;
653 while ( nURLScanCount < nURLScanLen )
655 sal_Char cCurrent = *pURLScan;
656 switch ( cCurrent )
658 default:
659 if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
661 sprintf( pURLDest, "%%%02X", (unsigned char)cCurrent );
662 pURLDest += 3;
663 break;
665 case '!':
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 *pURLDest++ = cCurrent;
685 break;
686 case 0:
687 break;
690 pURLScan++;
691 nURLScanCount++;
694 *pURLDest = 0;
696 rtl_string_release( strUTF8 );
697 rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL );
698 rtl_freeMemory( pszEncodedURL );
701 //#############################################
703 oslFileError _osl_getSystemPathFromFileURL( rtl_uString *strURL, rtl_uString **pustrPath, sal_Bool bAllowRelative )
705 rtl_String *strUTF8 = NULL;
706 rtl_uString *strDecodedURL = NULL;
707 rtl_uString *strTempPath = NULL;
708 const sal_Unicode *pDecodedURL;
709 sal_uInt32 nDecodedLen;
710 sal_Bool bValidEncoded;
711 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
713 /* If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
714 having a mixed encoded URL later */
716 rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
718 /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
720 OSL_ENSURE_FILE(
721 strUTF8->length == strURL->length ||
722 0 != rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 )
723 ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL );
725 bValidEncoded = _osl_decodeURL( strUTF8, &strDecodedURL );
727 /* Release the encoded UTF8 string */
728 rtl_string_release( strUTF8 );
730 if ( bValidEncoded )
732 /* Replace backslashes and pipes */
734 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' );
735 rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' );
737 pDecodedURL = rtl_uString_getStr( strDecodedURL );
738 nDecodedLen = rtl_uString_getLength( strDecodedURL );
740 /* Must start with "file://" */
741 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
743 sal_uInt32 nSkip;
745 if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
746 nSkip = 8;
747 else if (
748 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
749 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
751 nSkip = 17;
752 else
753 nSkip = 5;
755 /* Indicates local root */
756 if ( nDecodedLen == nSkip )
757 rtl_uString_newFromStr_WithLength( &strTempPath, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), SAL_N_ELEMENTS(WSTR_SYSTEM_ROOT_PATH) - 1 );
758 else
760 /* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
761 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
763 rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
765 else
767 ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
768 sal_uInt32 nNewLen = GetCaseCorrectPathName( reinterpret_cast<LPCTSTR>(pDecodedURL + nSkip),
769 ::osl::mingw_reinterpret_cast<LPTSTR>(aBuf),
770 aBuf.getBufSizeInSymbols(),
771 sal_False );
773 if ( nNewLen <= MAX_PATH - 12
774 || 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 )
775 || 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 ) )
777 rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
779 else if ( pDecodedURL[nSkip] == (sal_Unicode)'\\' && pDecodedURL[nSkip+1] == (sal_Unicode)'\\' )
781 /* it should be an UNC path, use the according prefix */
782 rtl_uString *strSuffix = NULL;
783 rtl_uString *strPrefix = NULL;
784 rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX_UNC), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
785 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
787 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
789 rtl_uString_release( strPrefix );
790 rtl_uString_release( strSuffix );
792 else
794 rtl_uString *strSuffix = NULL;
795 rtl_uString *strPrefix = NULL;
796 rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1 );
797 rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
799 rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
801 rtl_uString_release( strPrefix );
802 rtl_uString_release( strSuffix );
807 if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
808 nError = osl_File_E_None;
810 else if ( bAllowRelative ) /* This maybe a relative file URL */
812 /* In future the relative path could be converted to absolute if it is too long */
813 rtl_uString_assign( &strTempPath, strDecodedURL );
815 if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
816 nError = osl_File_E_None;
819 else
820 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL );
825 if ( strDecodedURL )
826 rtl_uString_release( strDecodedURL );
828 if ( osl_File_E_None == nError )
829 rtl_uString_assign( pustrPath, strTempPath );
831 if ( strTempPath )
832 rtl_uString_release( strTempPath );
835 OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL );
838 return nError;
841 //#############################################
842 oslFileError _osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
844 oslFileError nError = osl_File_E_INVAL; /* Assume failure */
845 rtl_uString *strTempURL = NULL;
846 DWORD dwPathType = PATHTYPE_ERROR;
848 if (strPath)
849 dwPathType = IsValidFilePath(strPath, NULL, VALIDATEPATH_ALLOW_RELATIVE, NULL);
851 if (dwPathType)
853 rtl_uString *strTempPath = NULL;
855 if ( dwPathType & PATHTYPE_IS_LONGPATH )
857 rtl_uString *strBuffer = NULL;
858 sal_uInt32 nIgnore = 0;
859 sal_uInt32 nLength = 0;
861 /* the path has the longpath prefix, lets remove it */
862 switch ( dwPathType & PATHTYPE_MASK_TYPE )
864 case PATHTYPE_ABSOLUTE_UNC:
865 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
866 OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
868 /* generate the normal UNC path */
869 nLength = rtl_uString_getLength( strPath );
870 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
871 strBuffer->buffer[0] = '\\';
873 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
874 rtl_uString_release( strBuffer );
875 break;
877 case PATHTYPE_ABSOLUTE_LOCAL:
878 nIgnore = SAL_N_ELEMENTS( WSTR_LONG_PATH_PREFIX ) - 1;
879 OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
881 /* generate the normal path */
882 nLength = rtl_uString_getLength( strPath );
883 rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
885 rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
886 rtl_uString_release( strBuffer );
887 break;
889 default:
890 OSL_FAIL( "Unexpected long path format!" );
891 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
892 break;
895 else
897 /* Replace backslashes */
898 rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
901 switch ( dwPathType & PATHTYPE_MASK_TYPE )
903 case PATHTYPE_RELATIVE:
904 rtl_uString_assign( &strTempURL, strTempPath );
905 nError = osl_File_E_None;
906 break;
907 case PATHTYPE_ABSOLUTE_UNC:
908 rtl_uString_newFromAscii( &strTempURL, "file:" );
909 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
910 nError = osl_File_E_None;
911 break;
912 case PATHTYPE_ABSOLUTE_LOCAL:
913 rtl_uString_newFromAscii( &strTempURL, "file:///" );
914 rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
915 nError = osl_File_E_None;
916 break;
917 default:
918 break;
921 /* Release temp path */
922 rtl_uString_release( strTempPath );
925 if ( osl_File_E_None == nError )
927 rtl_String *strEncodedURL = NULL;
929 /* Encode the URL */
930 _osl_encodeURL( strTempURL, &strEncodedURL );
932 /* Provide URL via unicode string */
933 rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
934 OSL_ASSERT(*pstrURL != 0);
935 rtl_string_release( strEncodedURL );
938 /* Release temp URL */
939 if ( strTempURL )
940 rtl_uString_release( strTempURL );
943 OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath );
945 return nError;
948 //#####################################################
949 oslFileError SAL_CALL osl_getFileURLFromSystemPath(
950 rtl_uString* ustrPath, rtl_uString** pustrURL )
952 return _osl_getFileURLFromSystemPath( ustrPath, pustrURL );
955 //#####################################################
956 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
957 rtl_uString *ustrURL, rtl_uString **pustrPath)
959 return _osl_getSystemPathFromFileURL( ustrURL, pustrPath, sal_True );
962 //#####################################################
963 oslFileError SAL_CALL osl_searchFileURL(
964 rtl_uString *ustrFileName,
965 rtl_uString *ustrSystemSearchPath,
966 rtl_uString **pustrPath)
968 rtl_uString *ustrUNCPath = NULL;
969 rtl_uString *ustrSysPath = NULL;
970 oslFileError error;
972 /* First try to interpret the file name as an URL even a relative one */
973 error = _osl_getSystemPathFromFileURL( ustrFileName, &ustrUNCPath, sal_True );
975 /* So far we either have an UNC path or something invalid
976 Now create a system path */
977 if ( osl_File_E_None == error )
978 error = _osl_getSystemPathFromFileURL( ustrUNCPath, &ustrSysPath, sal_True );
980 if ( osl_File_E_None == error )
982 DWORD nBufferLength;
983 DWORD dwResult;
984 LPTSTR lpBuffer = NULL;
985 LPTSTR lpszFilePart;
987 /* Repeat calling SearchPath ...
988 Start with MAX_PATH for the buffer. In most cases this
989 will be enough and does not force the loop to runtwice */
990 dwResult = MAX_PATH;
994 /* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
995 LPCTSTR lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? reinterpret_cast<LPCTSTR>(ustrSystemSearchPath->buffer) : NULL;
996 LPCTSTR lpszSearchFile = reinterpret_cast<LPCTSTR>(ustrSysPath->buffer);
998 /* Allocate space for buffer according to previous returned count of required chars */
999 /* +1 is not necessary if we follow MSDN documentation but for robustness we do so */
1000 nBufferLength = dwResult + 1;
1001 lpBuffer = lpBuffer ?
1002 reinterpret_cast<LPTSTR>(rtl_reallocateMemory(lpBuffer, nBufferLength * sizeof(TCHAR))) :
1003 reinterpret_cast<LPTSTR>(rtl_allocateMemory(nBufferLength * sizeof(TCHAR)));
1005 dwResult = SearchPath( lpszSearchPath, lpszSearchFile, NULL, nBufferLength, lpBuffer, &lpszFilePart );
1006 } while ( dwResult && dwResult >= nBufferLength );
1008 /* ... until an error occurs or buffer is large enough.
1009 dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
1011 if ( dwResult )
1013 rtl_uString_newFromStr( &ustrSysPath, reinterpret_cast<const sal_Unicode*>(lpBuffer) );
1014 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1016 else
1018 WIN32_FIND_DATA aFindFileData;
1019 HANDLE hFind;
1021 /* something went wrong, perhaps the path was absolute */
1022 error = oslTranslateFileError( GetLastError() );
1024 hFind = FindFirstFile( reinterpret_cast<LPCTSTR>(ustrSysPath->buffer), &aFindFileData );
1026 if ( IsValidHandle(hFind) )
1028 error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1029 FindClose( hFind );
1033 rtl_freeMemory( lpBuffer );
1036 if ( ustrSysPath )
1037 rtl_uString_release( ustrSysPath );
1039 if ( ustrUNCPath )
1040 rtl_uString_release( ustrUNCPath );
1042 return error;
1045 //#####################################################
1047 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
1049 oslFileError eError;
1050 rtl_uString *ustrRelSysPath = NULL;
1051 rtl_uString *ustrBaseSysPath = NULL;
1053 if ( ustrBaseURL && ustrBaseURL->length )
1055 eError = _osl_getSystemPathFromFileURL( ustrBaseURL, &ustrBaseSysPath, sal_False );
1056 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1058 eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_True );
1060 else
1062 eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_False );
1063 OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1066 if ( !eError )
1068 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
1069 ::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
1070 LPTSTR lpFilePart = NULL;
1071 DWORD dwResult;
1073 /*@@@ToDo
1074 Bad, bad hack, this only works if the base path
1075 really exists which is not necessary according
1076 to RFC2396
1077 The whole FileURL implementation should be merged
1078 with the rtl/uri class.
1080 if ( ustrBaseSysPath )
1082 osl_acquireMutex( g_CurrentDirectoryMutex );
1084 GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aCurrentDir) );
1085 SetCurrentDirectoryW( reinterpret_cast<LPCWSTR>(ustrBaseSysPath->buffer) );
1088 dwResult = GetFullPathNameW( reinterpret_cast<LPCWSTR>(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), &lpFilePart );
1090 if ( ustrBaseSysPath )
1092 SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast<LPCWSTR>(aCurrentDir) );
1094 osl_releaseMutex( g_CurrentDirectoryMutex );
1097 if ( dwResult )
1099 if ( dwResult >= aBuffer.getBufSizeInSymbols() )
1100 eError = osl_File_E_INVAL;
1101 else
1103 rtl_uString *ustrAbsSysPath = NULL;
1105 rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
1107 eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
1109 if ( ustrAbsSysPath )
1110 rtl_uString_release( ustrAbsSysPath );
1113 else
1114 eError = oslTranslateFileError( GetLastError() );
1117 if ( ustrBaseSysPath )
1118 rtl_uString_release( ustrBaseSysPath );
1120 if ( ustrRelSysPath )
1121 rtl_uString_release( ustrRelSysPath );
1123 return eError;
1126 //#####################################################
1127 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
1129 rtl_uString_newFromString(strValid, strRequested);
1130 return osl_File_E_None;
1133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */