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