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