xref: /AOO41X/main/sal/osl/w32/file_url.cxx (revision 87d2adbc9cadf14644c3679b041b9226f7630199)
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 //#####################################################
IsValidFilePathComponent(LPCTSTR lpComponent,LPCTSTR * lppComponentEnd,DWORD dwFlags)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 
IsValidFilePath(rtl_uString * path,LPCTSTR * lppError,DWORD dwFlags,rtl_uString ** corrected)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 //#####################################################
PathRemoveFileSpec(LPTSTR lpPath,LPTSTR lpFileName,sal_Int32 nFileBufLen)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
PathAddBackslash(LPTSTR lpPath,sal_Int32 nBufLen)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
GetCaseCorrectPathNameEx(LPTSTR lpszPath,DWORD cchBuffer,DWORD nSkipLevels,BOOL bCheckExistence)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 
GetCaseCorrectPathName(LPCTSTR lpszShortPath,LPTSTR lpszLongPath,DWORD cchBuffer,BOOL bCheckExistence)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 //#############################################
_osl_decodeURL(rtl_String * strUTF8,rtl_uString ** pstrDecodedURL)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 //#############################################
_osl_encodeURL(rtl_uString * strURL,rtl_String ** pstrEncodedURL)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 
_osl_getSystemPathFromFileURL(rtl_uString * strURL,rtl_uString ** pustrPath,sal_Bool bAllowRelative)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 //#############################################
_osl_getFileURLFromSystemPath(rtl_uString * strPath,rtl_uString ** pstrURL)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 //#####################################################
osl_getFileURLFromSystemPath(rtl_uString * ustrPath,rtl_uString ** pustrURL)957 oslFileError SAL_CALL osl_getFileURLFromSystemPath(
958     rtl_uString* ustrPath, rtl_uString** pustrURL )
959 {
960     return _osl_getFileURLFromSystemPath( ustrPath, pustrURL );
961 }
962 
963 //#####################################################
osl_getSystemPathFromFileURL(rtl_uString * ustrURL,rtl_uString ** pustrPath)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 //#####################################################
osl_searchFileURL(rtl_uString * ustrFileName,rtl_uString * ustrSystemSearchPath,rtl_uString ** pustrPath)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 neccessary 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 occures 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 
osl_getAbsoluteFileURL(rtl_uString * ustrBaseURL,rtl_uString * ustrRelativeURL,rtl_uString ** pustrAbsoluteURL)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 //#####################################################
osl_getCanonicalName(rtl_uString * strRequested,rtl_uString ** strValid)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