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 whether 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 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
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