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