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