xref: /aoo42x/main/sal/osl/w32/file_url.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #define UNICODE
29 #define _UNICODE
30 #define _WIN32_WINNT_0x0500
31 #include "systools/win32/uwinapi.h"
32 
33 #include "file_url.h"
34 #include "file_error.h"
35 
36 #include "rtl/alloc.h"
37 #include "osl/diagnose.h"
38 #include "osl/file.h"
39 #include "osl/mutex.h"
40 
41 #include "path_helper.hxx"
42 
43 #include <stdio.h>
44 #include <tchar.h>
45 
46 #if OSL_DEBUG_LEVEL > 0
47 #define OSL_ENSURE_FILE( cond, msg, file ) ( (cond) ?  (void)0 : _osl_warnFile( msg, file ) )
48 #else
49 #define OSL_ENSURE_FILE( cond, msg, file ) ((void)0)
50 #endif
51 
52 #define ELEMENTS_OF_ARRAY(arr) (sizeof(arr)/(sizeof((arr)[0])))
53 
54 #define WSTR_SYSTEM_ROOT_PATH				L"\\\\.\\"
55 #define WSTR_LONG_PATH_PREFIX				L"\\\\?\\"
56 #define WSTR_LONG_PATH_PREFIX_UNC			L"\\\\?\\UNC\\"
57 
58 
59 //##################################################################
60 // FileURL functions
61 //##################################################################
62 
63 extern "C" oslMutex g_CurrentDirectoryMutex; /* Initialized in dllentry.c */
64 oslMutex g_CurrentDirectoryMutex = 0;
65 
66 //#####################################################
67 static BOOL IsValidFilePathComponent(
68     LPCTSTR lpComponent, LPCTSTR *lppComponentEnd, DWORD dwFlags)
69 {
70 	    LPCTSTR	lpComponentEnd = NULL;
71 	    LPCTSTR	lpCurrent = lpComponent;
72 	    BOOL	fValid = TRUE;	/* Assume success */
73 	    TCHAR	cLast = 0;
74 
75 	    /* Path component length must not exceed MAX_PATH even if long path with "\\?\" prefix is used */
76 
77 	    while ( !lpComponentEnd && lpCurrent && lpCurrent - lpComponent < MAX_PATH )
78 	    {
79 		    switch ( *lpCurrent )
80 		    {
81 			    /* Both backslash and slash determine the end of a path component */
82 		    case '\0':
83 		    case '/':
84 		    case '\\':
85 			    switch ( cLast )
86 			    {
87 				    /* Component must not end with '.' or blank and can't be empty */
88 
89 			    case '.':
90 				    if ( dwFlags & VALIDATEPATH_ALLOW_ELLIPSE )
91 				    {
92 					    if ( 1 == lpCurrent - lpComponent )
93 					    {
94 						    /* Current directory is O.K. */
95 						    lpComponentEnd = lpCurrent;
96 						    break;
97 					    }
98 					    else if ( 2 == lpCurrent - lpComponent && '.' == *lpComponent )
99 					    {
100 						    /* Parent directory is O.K. */
101 						    lpComponentEnd = lpCurrent;
102 						    break;
103 					    }
104 				    }
105 			    case 0:
106 			    case ' ':
107 				    lpComponentEnd = lpCurrent - 1;
108 				    fValid = FALSE;
109 				    break;
110 			    default:
111 				    lpComponentEnd = lpCurrent;
112 				    break;
113 			    }
114 			    break;
115 			    /* '?' and '*' are valid wildcards but not valid file name characters */
116 		    case '?':
117 		    case '*':
118 			    if ( dwFlags & VALIDATEPATH_ALLOW_WILDCARDS )
119 				    break;
120 			    /* The following characters are reserved */
121 		    case '<':
122 		    case '>':
123 		    case '\"':
124 		    case '|':
125 		    case ':':
126 			    lpComponentEnd = lpCurrent;
127 			    fValid = FALSE;
128 			    break;
129 		    default:
130 			    /* Characters below ASCII 32 are not allowed */
131 			    if ( *lpCurrent < ' ' )
132 			    {
133 				    lpComponentEnd = lpCurrent;
134 				    fValid = FALSE;
135 			    }
136 			    break;
137 		    }
138 		    cLast = *lpCurrent++;
139 	    }
140 
141 	    /*	If we don't reached the end of the component the length of the component was to long
142 		    ( See condition of while loop ) */
143 	    if ( !lpComponentEnd )
144 	    {
145 		    fValid = FALSE;
146 		    lpComponentEnd = lpCurrent;
147 	    }
148 
149 		/* Test wether the component specifies a device name what is not allowed */
150 
151 		// MT: PERFORMANCE:
152 		// This is very expensive. A lot of calls to _tcsicmp.
153 		// in SRC6870m71 67.000 calls of this method while empty office start result into more than 1.500.00 calls of _tcsicmp!
154 		// Possible optimizations
155 		// - Array should be const static
156 		// - Sorted array, use binary search
157 		// - More intelligent check for com1-9, lpt1-9
158 		// Maybe make szComponent upper case, don't search case intensitive
159 		// 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.
160 		/*
161 	    if ( fValid )
162 	    {
163 		    LPCTSTR	alpDeviceNames[] =
164 		    {
165 			    TEXT("CON"),
166 			    TEXT("PRN"),
167 			    TEXT("AUX"),
168 			    TEXT("CLOCK$"),
169 			    TEXT("NUL"),
170 			    TEXT("LPT1"),
171 			    TEXT("LPT2"),
172 			    TEXT("LPT3"),
173 			    TEXT("LPT4"),
174 			    TEXT("LPT5"),
175 			    TEXT("LPT6"),
176 			    TEXT("LPT7"),
177 			    TEXT("LPT8"),
178 			    TEXT("LPT9"),
179 			    TEXT("COM1"),
180 			    TEXT("COM2"),
181 			    TEXT("COM3"),
182 			    TEXT("COM4"),
183 			    TEXT("COM5"),
184 			    TEXT("COM6"),
185 			    TEXT("COM7"),
186 			    TEXT("COM8"),
187 			    TEXT("COM9")
188 		    };
189 
190 		    TCHAR	szComponent[MAX_PATH];
191 		    int		nComponentLength;
192 		    LPCTSTR	lpDot;
193 		    int		i;
194 
195 		    // A device name with an extension is also invalid
196 		    lpDot = _tcschr( lpComponent, '.' );
197 
198 		    if ( !lpDot || lpDot > lpComponentEnd )
199 			    nComponentLength = lpComponentEnd - lpComponent;
200 		    else
201 			    nComponentLength = lpDot - lpComponent;
202 
203 		    _tcsncpy( szComponent, lpComponent, nComponentLength );
204 		    szComponent[nComponentLength] = 0;
205 
206 		    for ( i = 0; i < sizeof( alpDeviceNames ) / sizeof(LPCTSTR); i++ )
207 		    {
208 			    if ( 0 == _tcsicmp( szComponent, alpDeviceNames[i] ) )
209 			    {
210 				    lpComponentEnd = lpComponent;
211 				    fValid = FALSE;
212 				    break;
213 			    }
214 		    }
215 	    }
216 	    */
217 
218 		if ( fValid )
219 		{
220 			// Empty components are not allowed
221 			if ( lpComponentEnd - lpComponent < 1 )
222 				fValid = FALSE;
223 
224 			// If we reached the end of the string NULL is returned
225 			else if ( !*lpComponentEnd )
226 				lpComponentEnd = NULL;
227 
228 		}
229 
230 	    if ( lppComponentEnd )
231 		    *lppComponentEnd = lpComponentEnd;
232 
233 	    return fValid;
234 }
235 
236 //#####################################################
237 #define	CHARSET_SEPARATOR TEXT("\\/")
238 
239 DWORD IsValidFilePath(rtl_uString *path, LPCTSTR *lppError, DWORD dwFlags, rtl_uString **corrected)
240 {
241         LPCTSTR lpszPath = reinterpret_cast< LPCTSTR >(path->buffer);
242 	    LPCTSTR	lpComponent = lpszPath;
243 	    BOOL	fValid = TRUE;
244 	    DWORD	dwPathType = PATHTYPE_ERROR;
245         sal_Int32 nLength = rtl_uString_getLength( path );
246 
247 	    if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
248 		    dwFlags |= VALIDATEPATH_ALLOW_ELLIPSE;
249 
250 	    if ( !lpszPath )
251 		    fValid = FALSE;
252 
253 	    DWORD	dwCandidatPathType = PATHTYPE_ERROR;
254 
255         if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX_UNC), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1 ) )
256         {
257             /* This is long path in UNC notation */
258             lpComponent = lpszPath + ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX_UNC) - 1;
259             dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC | PATHTYPE_IS_LONGPATH;
260         }
261         else if ( 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( path->buffer, nLength, reinterpret_cast<const sal_Unicode *>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1 ) )
262         {
263             /* This is long path */
264             lpComponent = lpszPath + ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1;
265 
266             if ( _istalpha( lpComponent[0] ) && ':' == lpComponent[1] )
267             {
268                 lpComponent += 2;
269                 dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL | PATHTYPE_IS_LONGPATH;
270             }
271         }
272 	    else if ( 2 == _tcsspn( lpszPath, CHARSET_SEPARATOR ) )
273         {
274             /* The UNC path notation */
275             lpComponent = lpszPath + 2;
276             dwCandidatPathType = PATHTYPE_ABSOLUTE_UNC;
277         }
278 	    else if ( _istalpha( lpszPath[0] ) && ':' == lpszPath[1] )
279         {
280             /* Local path verification. Must start with <drive>: */
281 		    lpComponent = lpszPath + 2;
282             dwCandidatPathType = PATHTYPE_ABSOLUTE_LOCAL;
283         }
284 
285 	    if ( ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_UNC )
286 	    {
287 		    fValid = IsValidFilePathComponent( lpComponent, &lpComponent, VALIDATEPATH_ALLOW_ELLIPSE );
288 
289 		    /* So far we have a valid servername. Now let's see if we also have a network resource */
290 
291 		    dwPathType = dwCandidatPathType;
292 
293 		    if ( fValid )
294 		    {
295 			    if ( lpComponent &&	 !*++lpComponent )
296 				    lpComponent = NULL;
297 
298 			    if ( !lpComponent )
299 			    {
300     #if 0
301 				    /* We only have a Server specification what is invalid */
302 
303 				    lpComponent = lpszPath;
304 				    fValid = FALSE;
305     #else
306 				    dwPathType |= PATHTYPE_IS_SERVER;
307     #endif
308 			    }
309 			    else
310 			    {
311 				    /* Now test the network resource */
312 
313 				    fValid = IsValidFilePathComponent( lpComponent, &lpComponent, 0 );
314 
315 				    /* If we now reached the end of the path, everything is O.K. */
316 
317 
318 				    if ( fValid && (!lpComponent || lpComponent && !*++lpComponent ) )
319 				    {
320 					    lpComponent = NULL;
321 					    dwPathType |= PATHTYPE_IS_VOLUME;
322 				    }
323 			    }
324 		    }
325 	    }
326 	    else if (  ( dwCandidatPathType & PATHTYPE_MASK_TYPE ) == PATHTYPE_ABSOLUTE_LOCAL )
327 	    {
328 		    if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
329 			    lpComponent++;
330 		    else if ( *lpComponent )
331 			    fValid = FALSE;
332 
333 		    dwPathType = dwCandidatPathType;
334 
335 		    /* Now we are behind the backslash or it was a simple drive without backslash */
336 
337 		    if ( fValid && !*lpComponent )
338 		    {
339 			    lpComponent = NULL;
340 			    dwPathType |= PATHTYPE_IS_VOLUME;
341 		    }
342 	    }
343 	    else if ( dwFlags & VALIDATEPATH_ALLOW_RELATIVE )
344 	    {
345             /* Can be a relative path */
346 		    lpComponent = lpszPath;
347 
348 		    /* Relative path can start with a backslash */
349 
350 		    if ( 1 == _tcsspn( lpComponent, CHARSET_SEPARATOR ) )
351 		    {
352 			    lpComponent++;
353 			    if ( !*lpComponent )
354 				    lpComponent = NULL;
355 		    }
356 
357 		    dwPathType = PATHTYPE_RELATIVE;
358 	    }
359 	    else
360 	    {
361             /* Anything else is an error */
362 		    fValid = FALSE;
363 		    lpComponent = lpszPath;
364 	    }
365 
366 	    /* Now validate each component of the path */
367 	    while ( fValid && lpComponent )
368 	    {
369             // Correct path by merging consecutive slashes:
370             if (*lpComponent == '\\' && corrected != NULL) {
371                 sal_Int32 i = lpComponent - lpszPath;
372                 rtl_uString_newReplaceStrAt(corrected, path, i, 1, NULL);
373                     //TODO: handle out-of-memory
374                 lpszPath = reinterpret_cast< LPCTSTR >((*corrected)->buffer);
375                 lpComponent = lpszPath + i;
376             }
377 
378 		    fValid = IsValidFilePathComponent( lpComponent, &lpComponent, dwFlags );
379 
380 		    if ( fValid && lpComponent )
381 		    {
382 			    lpComponent++;
383 
384 			    /* If the string behind the backslash is empty, we've done */
385 
386 			    if ( !*lpComponent )
387 				    lpComponent = NULL;
388 		    }
389 	    }
390 
391         /* The path can be longer than MAX_PATH only in case it has the longpath prefix */
392 	    if ( fValid && !( dwPathType &  PATHTYPE_IS_LONGPATH ) && _tcslen( lpszPath ) >= MAX_PATH )
393 	    {
394 		    fValid = FALSE;
395 		    lpComponent = lpszPath + MAX_PATH;
396 	    }
397 
398 	    if ( lppError )
399 		    *lppError = lpComponent;
400 
401 	    return fValid ? dwPathType : PATHTYPE_ERROR;
402 }
403 
404 //#####################################################
405 static sal_Int32 PathRemoveFileSpec(LPTSTR lpPath, LPTSTR lpFileName, sal_Int32 nFileBufLen )
406 {
407     sal_Int32 nRemoved = 0;
408 
409     if ( nFileBufLen )
410     {
411         lpFileName[0] = 0;
412         LPTSTR	lpLastBkSlash = _tcsrchr( lpPath, '\\' );
413         LPTSTR	lpLastSlash = _tcsrchr( lpPath, '/' );
414         LPTSTR	lpLastDelimiter = lpLastSlash > lpLastBkSlash ? lpLastSlash : lpLastBkSlash;
415 
416         if ( lpLastDelimiter )
417         {
418                 sal_Int32 nDelLen = _tcslen( lpLastDelimiter );
419                 if ( 1 == nDelLen )
420                 {
421                     if ( lpLastDelimiter > lpPath && *(lpLastDelimiter - 1) != ':' )
422                     {
423                         *lpLastDelimiter = 0;
424                         *lpFileName = 0;
425                         nRemoved = nDelLen;
426                     }
427                 }
428                 else if ( nDelLen && nDelLen - 1 < nFileBufLen )
429                 {
430                     _tcscpy( lpFileName, lpLastDelimiter + 1 );
431                     *(++lpLastDelimiter) = 0;
432                     nRemoved = nDelLen - 1;
433                 }
434         }
435     }
436 
437     return nRemoved;
438 }
439 
440 //#####################################################
441 // Undocumented in SHELL32.DLL ordinal 32
442 static LPTSTR PathAddBackslash(LPTSTR lpPath, sal_Int32 nBufLen)
443 {
444     LPTSTR	lpEndPath = NULL;
445 
446     if ( lpPath )
447     {
448 		    int		nLen = _tcslen(lpPath);
449 
450 		    if ( !nLen || lpPath[nLen-1] != '\\' && lpPath[nLen-1] != '/' && nLen < nBufLen - 1 )
451 		    {
452 			    lpEndPath = lpPath + nLen;
453 			    *lpEndPath++ = '\\';
454 			    *lpEndPath = 0;
455 		    }
456     }
457     return lpEndPath;
458 }
459 
460 //#####################################################
461 // Same as GetLongPathName but also 95/NT4
462 static DWORD GetCaseCorrectPathNameEx(
463     LPTSTR	lpszPath,	// path buffer to convert
464     DWORD	cchBuffer,		// size of path buffer
465     DWORD	nSkipLevels,
466     BOOL bCheckExistence )
467 {
468         ::osl::LongPathBuffer< WCHAR > szFile( MAX_PATH + 1 );
469 	    sal_Int32 nRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
470         sal_Int32 nLastStepRemoved = nRemoved;
471         while ( nLastStepRemoved && szFile[0] == 0 )
472         {
473             // remove separators
474 	        nLastStepRemoved = PathRemoveFileSpec( lpszPath, szFile, MAX_PATH + 1 );
475             nRemoved += nLastStepRemoved;
476         }
477 
478 	    if ( nRemoved )
479 	    {
480             BOOL bSkipThis = FALSE;
481 
482 		    if ( 0 == _tcscmp( szFile, TEXT("..") ) )
483 		    {
484 			    bSkipThis = TRUE;
485 			    nSkipLevels += 1;
486 		    }
487             else if ( 0 == _tcscmp( szFile, TEXT(".") ) )
488             {
489 			    bSkipThis = TRUE;
490             }
491 		    else if ( nSkipLevels )
492 		    {
493 			    bSkipThis = TRUE;
494 			    nSkipLevels--;
495 		    }
496 		    else
497 			    bSkipThis = FALSE;
498 
499 		    GetCaseCorrectPathNameEx( lpszPath, cchBuffer, nSkipLevels, bCheckExistence );
500 
501 		    PathAddBackslash( lpszPath, cchBuffer );
502 
503 		    /* Analyze parent if not only a trailing backslash was cutted but a real file spec */
504 		    if ( !bSkipThis )
505 		    {
506                 if ( bCheckExistence )
507                 {
508                     ::osl::LongPathBuffer< WCHAR > aShortPath( MAX_LONG_PATH );
509                     _tcscpy( aShortPath, lpszPath );
510                     _tcscat( aShortPath, szFile );
511 
512                     WIN32_FIND_DATA	aFindFileData;
513                     HANDLE	hFind = FindFirstFile( aShortPath, &aFindFileData );
514 
515                     if ( IsValidHandle(hFind) )
516                     {
517                         _tcscat( lpszPath, aFindFileData.cFileName[0] ? aFindFileData.cFileName : aFindFileData.cAlternateFileName );
518 
519                         FindClose( hFind );
520                     }
521                     else
522                         lpszPath[0] = 0;
523                 }
524                 else
525                 {
526                     /* add the segment name back */
527                     _tcscat( lpszPath, szFile );
528                 }
529 		    }
530 	    }
531 	    else
532 	    {
533 		    /* File specification can't be removed therefore the short path is either a drive
534 			   or a network share. If still levels to skip are left, the path specification
535 			   tries to travel below the file system root */
536 		    if ( nSkipLevels )
537                     lpszPath[0] = 0;
538             else
539                 _tcsupr( lpszPath );
540 	    }
541 
542 	    return _tcslen( lpszPath );
543 }
544 
545 //#####################################################
546 #define WSTR_SYSTEM_ROOT_PATH				L"\\\\.\\"
547 
548 DWORD GetCaseCorrectPathName(
549     LPCTSTR	lpszShortPath,	// file name
550     LPTSTR	lpszLongPath,	// path buffer
551     DWORD	cchBuffer,		// size of path buffer
552     BOOL bCheckExistence
553 )
554 {
555     /* Special handling for "\\.\" as system root */
556     if ( lpszShortPath && 0 == wcscmp( lpszShortPath, WSTR_SYSTEM_ROOT_PATH ) )
557     {
558         if ( cchBuffer >= ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) )
559         {
560             wcscpy( lpszLongPath, WSTR_SYSTEM_ROOT_PATH );
561             return ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1;
562         }
563         else
564         {
565             return ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1;
566         }
567     }
568     else if ( lpszShortPath )
569     {
570         if ( _tcslen( lpszShortPath ) <= cchBuffer )
571         {
572             _tcscpy( lpszLongPath, lpszShortPath );
573             return GetCaseCorrectPathNameEx( lpszLongPath, cchBuffer, 0, bCheckExistence );
574         }
575     }
576 
577     return 0;
578 }
579 
580 
581 //#############################################
582 static sal_Bool _osl_decodeURL( rtl_String* strUTF8, rtl_uString** pstrDecodedURL )
583 {
584     sal_Char		*pBuffer;
585     const sal_Char	*pSrcEnd;
586     const sal_Char	*pSrc;
587     sal_Char		*pDest;
588     sal_Int32		nSrcLen;
589     sal_Bool		bValidEncoded = sal_True;	/* Assume success */
590 
591     /* The resulting decoded string length is shorter or equal to the source length */
592 
593     nSrcLen = rtl_string_getLength(strUTF8);
594     pBuffer = reinterpret_cast<sal_Char*>(rtl_allocateMemory(nSrcLen + 1));
595 
596     pDest = pBuffer;
597     pSrc = rtl_string_getStr(strUTF8);
598     pSrcEnd = pSrc + nSrcLen;
599 
600     /* Now decode the URL what should result in an UTF8 string */
601     while ( bValidEncoded && pSrc < pSrcEnd )
602     {
603         switch ( *pSrc )
604         {
605         case '%':
606             {
607                 sal_Char	aToken[3];
608                 sal_Char	aChar;
609 
610                 pSrc++;
611                 aToken[0] = *pSrc++;
612                 aToken[1] = *pSrc++;
613                 aToken[2] = 0;
614 
615                 aChar = (sal_Char)strtoul( aToken, NULL, 16 );
616 
617                 /* The chars are path delimiters and must not be encoded */
618 
619                 if ( 0 == aChar || '\\' == aChar || '/' == aChar || ':' == aChar )
620                     bValidEncoded = sal_False;
621                 else
622                     *pDest++ = aChar;
623             }
624             break;
625         default:
626             *pDest++ = *pSrc++;
627             break;
628         }
629     }
630 
631     *pDest++ = 0;
632 
633     if ( bValidEncoded )
634     {
635         rtl_string2UString( pstrDecodedURL, pBuffer, rtl_str_getLength(pBuffer), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
636         OSL_ASSERT(*pstrDecodedURL != 0);
637     }
638 
639     rtl_freeMemory( pBuffer );
640 
641     return bValidEncoded;
642 }
643 
644 //#############################################
645 static void _osl_encodeURL( rtl_uString *strURL, rtl_String **pstrEncodedURL )
646 {
647     /* Encode non ascii characters within the URL */
648 
649     rtl_String		*strUTF8 = NULL;
650     sal_Char		*pszEncodedURL;
651     const sal_Char	*pURLScan;
652     sal_Char		*pURLDest;
653     sal_Int32		nURLScanLen;
654     sal_Int32		nURLScanCount;
655 
656     rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
657 
658     pszEncodedURL = (sal_Char*) rtl_allocateMemory( (rtl_string_getLength( strUTF8 ) * 3 + 1)  * sizeof(sal_Char) );
659 
660     pURLDest = pszEncodedURL;
661     pURLScan = rtl_string_getStr( strUTF8 );
662     nURLScanLen = rtl_string_getLength( strUTF8 );
663     nURLScanCount = 0;
664 
665     while ( nURLScanCount < nURLScanLen )
666     {
667         sal_Char cCurrent = *pURLScan;
668         switch ( cCurrent )
669         {
670         default:
671             if (!( ( cCurrent >= 'a' && cCurrent <= 'z' ) || ( cCurrent >= 'A' && cCurrent <= 'Z' ) || ( cCurrent >= '0' && cCurrent <= '9' ) ) )
672             {
673                 sprintf( pURLDest, "%%%02X", (unsigned char)cCurrent );
674                 pURLDest += 3;
675                 break;
676             }
677         case '!':
678         case '\'':
679         case '(':
680         case ')':
681         case '*':
682         case '-':
683         case '.':
684         case '_':
685         case '~':
686         case '$':
687         case '&':
688         case '+':
689         case ',':
690         case '=':
691         case '@':
692         case ':':
693         case '/':
694         case '\\':
695         case '|':
696             *pURLDest++ = cCurrent;
697             break;
698         case 0:
699             break;
700         }
701 
702         pURLScan++;
703         nURLScanCount++;
704     }
705 
706     *pURLDest = 0;
707 
708     rtl_string_release( strUTF8 );
709     rtl_string_newFromStr( pstrEncodedURL, pszEncodedURL );
710     rtl_freeMemory( pszEncodedURL );
711 }
712 
713 //#############################################
714 
715 oslFileError _osl_getSystemPathFromFileURL( rtl_uString *strURL, rtl_uString **pustrPath, sal_Bool bAllowRelative )
716 {
717     rtl_String			*strUTF8 = NULL;
718     rtl_uString			*strDecodedURL = NULL;
719     rtl_uString			*strTempPath = NULL;
720     const sal_Unicode	*pDecodedURL;
721     sal_uInt32			nDecodedLen;
722     sal_Bool			bValidEncoded;
723     oslFileError		nError = osl_File_E_INVAL;	/* Assume failure */
724 
725     /*  If someone hasn't encoded the complete URL we convert it to UTF8 now to prevent from
726         having a mixed encoded URL later */
727 
728     rtl_uString2String( &strUTF8, rtl_uString_getStr( strURL ), rtl_uString_getLength( strURL ), RTL_TEXTENCODING_UTF8, OUSTRING_TO_OSTRING_CVTFLAGS );
729 
730     /* If the length of strUTF8 and strURL differs it indicates that the URL was not correct encoded */
731 
732     OSL_ENSURE_FILE(
733         strUTF8->length == strURL->length ||
734         0 != rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( strURL->buffer, strURL->length, "file:\\\\", 7 )
735         ,"osl_getSystemPathFromFileURL: \"%s\" is not encoded !!!", strURL );
736 
737     bValidEncoded = _osl_decodeURL( strUTF8, &strDecodedURL );
738 
739     /* Release the encoded UTF8 string */
740     rtl_string_release( strUTF8 );
741 
742     if ( bValidEncoded )
743     {
744         /* Replace backslashes and pipes */
745 
746         rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '/', '\\' );
747         rtl_uString_newReplace( &strDecodedURL, strDecodedURL, '|', ':' );
748 
749         pDecodedURL = rtl_uString_getStr( strDecodedURL );
750         nDecodedLen = rtl_uString_getLength( strDecodedURL );
751 
752         /* Must start with "file://" */
753         if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\", 7 ) )
754         {
755             sal_uInt32	nSkip;
756 
757             if ( 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\\\", 8 ) )
758                 nSkip = 8;
759             else if (
760                 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\localhost\\", 17 ) ||
761                 0 == rtl_ustr_ascii_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL, nDecodedLen, "file:\\\\127.0.0.1\\", 17 )
762                       )
763                 nSkip = 17;
764             else
765                 nSkip = 5;
766 
767             /* Indicates local root */
768             if ( nDecodedLen == nSkip )
769                 rtl_uString_newFromStr_WithLength( &strTempPath, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1 );
770             else
771             {
772                 /* do not separate the directory and file case, so the maximal path lengs without prefix is MAX_PATH-12 */
773                 if ( nDecodedLen - nSkip <= MAX_PATH - 12 )
774                 {
775                     rtl_uString_newFromStr_WithLength( &strTempPath, pDecodedURL + nSkip, nDecodedLen - nSkip );
776                 }
777                 else
778                 {
779                     ::osl::LongPathBuffer< sal_Unicode > aBuf( MAX_LONG_PATH );
780                     sal_uInt32 nNewLen = GetCaseCorrectPathName( reinterpret_cast<LPCTSTR>(pDecodedURL + nSkip),
781                                                                  ::osl::mingw_reinterpret_cast<LPTSTR>(aBuf),
782                                                                  aBuf.getBufSizeInSymbols(),
783                                                                  sal_False );
784 
785                     if ( nNewLen <= MAX_PATH - 12
786                       || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_SYSTEM_ROOT_PATH), ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1, ELEMENTS_OF_ARRAY(WSTR_SYSTEM_ROOT_PATH) - 1 )
787                       || 0 == rtl_ustr_shortenedCompareIgnoreAsciiCase_WithLength( pDecodedURL + nSkip, nDecodedLen - nSkip, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1, ELEMENTS_OF_ARRAY(WSTR_LONG_PATH_PREFIX) - 1 ) )
788                     {
789                         rtl_uString_newFromStr_WithLength( &strTempPath, aBuf, nNewLen );
790                     }
791                     else if ( pDecodedURL[nSkip] == (sal_Unicode)'\\' && pDecodedURL[nSkip+1] == (sal_Unicode)'\\' )
792                     {
793                         /* it should be an UNC path, use the according prefix */
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_UNC), ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX_UNC ) - 1 );
797                         rtl_uString_newFromStr_WithLength( &strSuffix, aBuf + 2, nNewLen - 2 );
798 
799                         rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
800 
801                         rtl_uString_release( strPrefix );
802                         rtl_uString_release( strSuffix );
803                     }
804                     else
805                     {
806                         rtl_uString *strSuffix = NULL;
807                         rtl_uString *strPrefix = NULL;
808                         rtl_uString_newFromStr_WithLength( &strPrefix, reinterpret_cast<const sal_Unicode*>(WSTR_LONG_PATH_PREFIX), ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX ) - 1 );
809                         rtl_uString_newFromStr_WithLength( &strSuffix, aBuf, nNewLen );
810 
811                         rtl_uString_newConcat( &strTempPath, strPrefix, strSuffix );
812 
813                         rtl_uString_release( strPrefix );
814                         rtl_uString_release( strSuffix );
815                     }
816                 }
817             }
818 
819             if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
820                 nError = osl_File_E_None;
821         }
822         else if ( bAllowRelative )	/* This maybe a relative file URL */
823         {
824             /* In future the relative path could be converted to absolute if it is too long */
825             rtl_uString_assign( &strTempPath, strDecodedURL );
826 
827             if ( IsValidFilePath( strTempPath, NULL, VALIDATEPATH_ALLOW_RELATIVE | VALIDATEPATH_ALLOW_ELLIPSE, &strTempPath ) )
828                 nError = osl_File_E_None;
829         }
830         /*
831           else
832           OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not an absolute FileURL !!!", strURL );
833         */
834 
835     }
836 
837     if ( strDecodedURL )
838         rtl_uString_release( strDecodedURL );
839 
840     if ( osl_File_E_None == nError )
841         rtl_uString_assign( pustrPath, strTempPath );
842 
843     if ( strTempPath )
844         rtl_uString_release( strTempPath );
845 
846     /*
847       OSL_ENSURE_FILE( !nError, "osl_getSystemPathFromFileURL: \"%s\" is not a FileURL !!!", strURL );
848     */
849 
850     return nError;
851 }
852 
853 //#############################################
854 oslFileError _osl_getFileURLFromSystemPath( rtl_uString* strPath, rtl_uString** pstrURL )
855 {
856     oslFileError nError = osl_File_E_INVAL; /* Assume failure */
857     rtl_uString	*strTempURL = NULL;
858     DWORD dwPathType = PATHTYPE_ERROR;
859 
860     if (strPath)
861         dwPathType = IsValidFilePath(strPath, NULL, VALIDATEPATH_ALLOW_RELATIVE, NULL);
862 
863     if (dwPathType)
864     {
865         rtl_uString	*strTempPath = NULL;
866 
867         if ( dwPathType & PATHTYPE_IS_LONGPATH )
868         {
869             rtl_uString *strBuffer = NULL;
870             sal_uInt32 nIgnore = 0;
871             sal_uInt32 nLength = 0;
872 
873             /* the path has the longpath prefix, lets remove it */
874             switch ( dwPathType & PATHTYPE_MASK_TYPE )
875             {
876                 case PATHTYPE_ABSOLUTE_UNC:
877                     nIgnore = ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX_UNC ) - 1;
878                     OSL_ENSURE( nIgnore == 8, "Unexpected long path UNC prefix!" );
879 
880                     /* generate the normal UNC path */
881                     nLength = rtl_uString_getLength( strPath );
882                     rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore - 2, nLength - nIgnore + 2 );
883                     strBuffer->buffer[0] = '\\';
884 
885                     rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
886                     rtl_uString_release( strBuffer );
887                     break;
888 
889                 case PATHTYPE_ABSOLUTE_LOCAL:
890                     nIgnore = ELEMENTS_OF_ARRAY( WSTR_LONG_PATH_PREFIX ) - 1;
891                     OSL_ENSURE( nIgnore == 4, "Unexpected long path prefix!" );
892 
893                     /* generate the normal path */
894                     nLength = rtl_uString_getLength( strPath );
895                     rtl_uString_newFromStr_WithLength( &strBuffer, strPath->buffer + nIgnore, nLength - nIgnore );
896 
897                     rtl_uString_newReplace( &strTempPath, strBuffer, '\\', '/' );
898                     rtl_uString_release( strBuffer );
899                     break;
900 
901                 default:
902                     OSL_ASSERT( "Unexpected long path format!" );
903                     rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
904                     break;
905             }
906         }
907         else
908         {
909             /* Replace backslashes */
910             rtl_uString_newReplace( &strTempPath, strPath, '\\', '/' );
911         }
912 
913         switch ( dwPathType & PATHTYPE_MASK_TYPE )
914         {
915         case PATHTYPE_RELATIVE:
916             rtl_uString_assign( &strTempURL, strTempPath );
917             nError = osl_File_E_None;
918             break;
919         case PATHTYPE_ABSOLUTE_UNC:
920             rtl_uString_newFromAscii( &strTempURL, "file:" );
921             rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
922             nError = osl_File_E_None;
923             break;
924         case PATHTYPE_ABSOLUTE_LOCAL:
925             rtl_uString_newFromAscii( &strTempURL, "file:///" );
926             rtl_uString_newConcat( &strTempURL, strTempURL, strTempPath );
927             nError = osl_File_E_None;
928             break;
929         default:
930             break;
931         }
932 
933         /* Release temp path */
934         rtl_uString_release( strTempPath );
935     }
936 
937     if ( osl_File_E_None == nError )
938     {
939         rtl_String	*strEncodedURL = NULL;
940 
941         /* Encode the URL */
942         _osl_encodeURL( strTempURL, &strEncodedURL );
943 
944         /* Provide URL via unicode string */
945         rtl_string2UString( pstrURL, rtl_string_getStr(strEncodedURL), rtl_string_getLength(strEncodedURL), RTL_TEXTENCODING_ASCII_US, OUSTRING_TO_OSTRING_CVTFLAGS );
946         OSL_ASSERT(*pstrURL != 0);
947         rtl_string_release( strEncodedURL );
948     }
949 
950     /* Release temp URL */
951     if ( strTempURL )
952         rtl_uString_release( strTempURL );
953 
954     /*
955       OSL_ENSURE_FILE( !nError, "osl_getFileURLFromSystemPath: \"%s\" is not a systemPath !!!", strPath );
956     */
957     return nError;
958 }
959 
960 //#####################################################
961 oslFileError SAL_CALL osl_getFileURLFromSystemPath(
962     rtl_uString* ustrPath, rtl_uString** pustrURL )
963 {
964 	return _osl_getFileURLFromSystemPath( ustrPath, pustrURL );
965 }
966 
967 //#####################################################
968 oslFileError SAL_CALL osl_getSystemPathFromFileURL(
969     rtl_uString *ustrURL, rtl_uString **pustrPath)
970 {
971 	return _osl_getSystemPathFromFileURL( ustrURL, pustrPath, sal_True );
972 }
973 
974 //#####################################################
975 oslFileError SAL_CALL osl_searchFileURL(
976     rtl_uString *ustrFileName,
977     rtl_uString *ustrSystemSearchPath,
978     rtl_uString **pustrPath)
979 {
980 	rtl_uString		*ustrUNCPath = NULL;
981 	rtl_uString		*ustrSysPath = NULL;
982 	oslFileError	error;
983 
984 	/* First try to interpret the file name as an URL even a relative one */
985 	error = _osl_getSystemPathFromFileURL( ustrFileName, &ustrUNCPath, sal_True );
986 
987 	/* So far we either have an UNC path or something invalid
988 	   Now create a system path */
989 	if ( osl_File_E_None == error )
990 		error = _osl_getSystemPathFromFileURL( ustrUNCPath, &ustrSysPath, sal_True );
991 
992 	if ( osl_File_E_None == error )
993 	{
994 		DWORD	nBufferLength;
995 		DWORD	dwResult;
996 		LPTSTR	lpBuffer = NULL;
997 		LPTSTR	lpszFilePart;
998 
999 		/* Repeat calling SearchPath ...
1000 		   Start with MAX_PATH for the buffer. In most cases this
1001 		   will be enough and does not force the loop to runtwice */
1002 		dwResult = MAX_PATH;
1003 
1004 		do
1005 		{
1006 			/* If search path is empty use a NULL pointer instead according to MSDN documentation of SearchPath */
1007 			LPCTSTR	lpszSearchPath = ustrSystemSearchPath && ustrSystemSearchPath->length ? reinterpret_cast<LPCTSTR>(ustrSystemSearchPath->buffer) : NULL;
1008 			LPCTSTR	lpszSearchFile = reinterpret_cast<LPCTSTR>(ustrSysPath->buffer);
1009 
1010 			/* Allocate space for buffer according to previous returned count of required chars */
1011 			/* +1 is not neccessary if we follow MSDN documentation but for robustness we do so */
1012 			nBufferLength = dwResult + 1;
1013 			lpBuffer = lpBuffer ?
1014 			    reinterpret_cast<LPTSTR>(rtl_reallocateMemory(lpBuffer, nBufferLength * sizeof(TCHAR))) :
1015 			    reinterpret_cast<LPTSTR>(rtl_allocateMemory(nBufferLength * sizeof(TCHAR)));
1016 
1017 			dwResult = SearchPath( lpszSearchPath, lpszSearchFile, NULL, nBufferLength, lpBuffer, &lpszFilePart );
1018 		} while ( dwResult && dwResult >= nBufferLength );
1019 
1020 		/*	... until an error occures or buffer is large enough.
1021 			dwResult == nBufferLength can not happen according to documentation but lets be robust ;-) */
1022 
1023 		if ( dwResult )
1024 		{
1025 			rtl_uString_newFromStr( &ustrSysPath, reinterpret_cast<const sal_Unicode*>(lpBuffer) );
1026 			error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1027 		}
1028 		else
1029 		{
1030 			WIN32_FIND_DATA	aFindFileData;
1031 			HANDLE	hFind;
1032 
1033 			/* Somthing went wrong, perhaps the path was absolute */
1034 			error = oslTranslateFileError( GetLastError() );
1035 
1036 			hFind = FindFirstFile( reinterpret_cast<LPCTSTR>(ustrSysPath->buffer), &aFindFileData );
1037 
1038 			if ( IsValidHandle(hFind) )
1039 			{
1040 				error = osl_getFileURLFromSystemPath( ustrSysPath, pustrPath );
1041 				FindClose( hFind );
1042 			}
1043 		}
1044 
1045 		rtl_freeMemory( lpBuffer );
1046 	}
1047 
1048 	if ( ustrSysPath )
1049 		rtl_uString_release( ustrSysPath );
1050 
1051 	if ( ustrUNCPath )
1052 		rtl_uString_release( ustrUNCPath );
1053 
1054 	return error;
1055 }
1056 
1057 //#####################################################
1058 
1059 oslFileError SAL_CALL osl_getAbsoluteFileURL( rtl_uString* ustrBaseURL, rtl_uString* ustrRelativeURL, rtl_uString** pustrAbsoluteURL )
1060 {
1061 	oslFileError	eError;
1062 	rtl_uString		*ustrRelSysPath = NULL;
1063 	rtl_uString		*ustrBaseSysPath = NULL;
1064 
1065 	if ( ustrBaseURL && ustrBaseURL->length )
1066 	{
1067 		eError = _osl_getSystemPathFromFileURL( ustrBaseURL, &ustrBaseSysPath, sal_False );
1068 		OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with relative or invalid base URL" );
1069 
1070 		eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_True );
1071 	}
1072 	else
1073 	{
1074 		eError = _osl_getSystemPathFromFileURL( ustrRelativeURL, &ustrRelSysPath, sal_False );
1075 		OSL_ENSURE( osl_File_E_None == eError, "osl_getAbsoluteFileURL called with empty base URL and/or invalid relative URL" );
1076 	}
1077 
1078 	if ( !eError )
1079 	{
1080         ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
1081         ::osl::LongPathBuffer< sal_Unicode > aCurrentDir( MAX_LONG_PATH );
1082 		LPTSTR	lpFilePart = NULL;
1083 		DWORD	dwResult;
1084 
1085 /*@@@ToDo
1086   Bad, bad hack, this only works if the base path
1087   really exists which is not necessary according
1088   to RFC2396
1089   The whole FileURL implementation should be merged
1090   with the rtl/uri class.
1091 */
1092 		if ( ustrBaseSysPath )
1093 		{
1094 			osl_acquireMutex( g_CurrentDirectoryMutex );
1095 
1096 			GetCurrentDirectoryW( aCurrentDir.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aCurrentDir) );
1097 			SetCurrentDirectoryW( reinterpret_cast<LPCWSTR>(ustrBaseSysPath->buffer) );
1098 		}
1099 
1100 		dwResult = GetFullPathNameW( reinterpret_cast<LPCWSTR>(ustrRelSysPath->buffer), aBuffer.getBufSizeInSymbols(), ::osl::mingw_reinterpret_cast<LPWSTR>(aBuffer), &lpFilePart );
1101 
1102 		if ( ustrBaseSysPath )
1103 		{
1104 			SetCurrentDirectoryW( ::osl::mingw_reinterpret_cast<LPCWSTR>(aCurrentDir) );
1105 
1106 			osl_releaseMutex( g_CurrentDirectoryMutex );
1107 		}
1108 
1109 		if ( dwResult )
1110 		{
1111 			if ( dwResult >= aBuffer.getBufSizeInSymbols() )
1112 				eError = osl_File_E_INVAL;
1113 			else
1114 			{
1115 				rtl_uString	*ustrAbsSysPath = NULL;
1116 
1117 				rtl_uString_newFromStr( &ustrAbsSysPath, aBuffer );
1118 
1119 				eError = osl_getFileURLFromSystemPath( ustrAbsSysPath, pustrAbsoluteURL );
1120 
1121 				if ( ustrAbsSysPath )
1122 					rtl_uString_release( ustrAbsSysPath );
1123 			}
1124 		}
1125 		else
1126 			eError = oslTranslateFileError( GetLastError() );
1127 	}
1128 
1129 	if ( ustrBaseSysPath )
1130 		rtl_uString_release( ustrBaseSysPath );
1131 
1132 	if ( ustrRelSysPath )
1133 		rtl_uString_release( ustrRelSysPath );
1134 
1135 	return	eError;
1136 }
1137 
1138 //#####################################################
1139 oslFileError SAL_CALL osl_getCanonicalName( rtl_uString *strRequested, rtl_uString **strValid )
1140 {
1141 	rtl_uString_newFromString(strValid, strRequested);
1142 	return osl_File_E_None;
1143 }
1144