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 10 #include <sal/config.h> 11 #include <rtl/ustring.hxx> 12 #include <rtl/bootstrap.hxx> 13 #include <sal/log.hxx> 14 #include <osl/file.hxx> 15 #include <comphelper/backupfilehelper.hxx> 16 #include <comphelper/DirectoryHelper.hxx> 17 #include <rtl/crc.h> 18 #include <algorithm> 19 #include <deque> 20 #include <memory> 21 #include <string_view> 22 #include <vector> 23 #include <zlib.h> 24 25 #include <comphelper/processfactory.hxx> 26 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> 27 #include <com/sun/star/ucb/CommandAbortedException.hpp> 28 #include <com/sun/star/ucb/CommandFailedException.hpp> 29 #include <com/sun/star/uno/Sequence.hxx> 30 #include <com/sun/star/uno/Reference.hxx> 31 #include <com/sun/star/deployment/DeploymentException.hpp> 32 #include <com/sun/star/deployment/ExtensionManager.hpp> 33 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp> 34 #include <com/sun/star/xml/dom/DocumentBuilder.hpp> 35 #include <com/sun/star/xml/dom/XElement.hpp> 36 #include <com/sun/star/xml/dom/XNodeList.hpp> 37 #include <com/sun/star/xml/dom/XText.hpp> 38 #include <com/sun/star/xml/sax/XSAXSerializable.hpp> 39 #include <com/sun/star/xml/sax/Writer.hpp> 40 #include <com/sun/star/xml/sax/XWriter.hpp> 41 #include <com/sun/star/io/XStream.hpp> 42 #include <com/sun/star/io/TempFile.hpp> 43 #include <com/sun/star/io/XOutputStream.hpp> 44 #include <com/sun/star/xml/sax/XDocumentHandler.hpp> 45 #include <com/sun/star/beans/XPropertySet.hpp> 46 #include <cppuhelper/exc_hlp.hxx> 47 48 using namespace comphelper; 49 using namespace css; 50 using namespace css::xml::dom; 51 52 const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE = 16384; 53 54 namespace 55 { 56 typedef std::shared_ptr< osl::File > FileSharedPtr; 57 58 sal_uInt32 createCrc32(FileSharedPtr const & rCandidate, sal_uInt32 nOffset) 59 { 60 sal_uInt32 nCrc32(0); 61 62 if (rCandidate && osl::File::E_None == rCandidate->open(osl_File_OpenFlag_Read)) 63 { 64 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; 65 sal_uInt64 nBytesTransfer(0); 66 sal_uInt64 nSize(0); 67 68 rCandidate->getSize(nSize); 69 70 // set offset in source file - should be zero due to crc32 should 71 // only be needed to be created for new entries, gets loaded with old 72 // ones 73 if (osl::File::E_None == rCandidate->setPos(osl_Pos_Absolut, sal_Int64(nOffset))) 74 { 75 while (nSize != 0) 76 { 77 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); 78 79 if (osl::File::E_None == rCandidate->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) && nBytesTransfer == nToTransfer) 80 { 81 // add to crc and reduce size 82 nCrc32 = rtl_crc32(nCrc32, static_cast<void*>(aArray), static_cast<sal_uInt32>(nBytesTransfer)); 83 nSize -= nToTransfer; 84 } 85 else 86 { 87 // error - reset to zero again 88 nSize = nCrc32 = 0; 89 } 90 } 91 } 92 93 rCandidate->close(); 94 } 95 96 return nCrc32; 97 } 98 99 bool read_sal_uInt32(FileSharedPtr const & rFile, sal_uInt32& rTarget) 100 { 101 sal_uInt8 aArray[4]; 102 sal_uInt64 nBaseRead(0); 103 104 // read rTarget 105 if (osl::File::E_None == rFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead) 106 { 107 rTarget = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]); 108 return true; 109 } 110 111 return false; 112 } 113 114 bool write_sal_uInt32(oslFileHandle& rHandle, sal_uInt32 nSource) 115 { 116 sal_uInt8 aArray[4]; 117 sal_uInt64 nBaseWritten(0); 118 119 // write nSource 120 aArray[0] = sal_uInt8((nSource & 0xff000000) >> 24); 121 aArray[1] = sal_uInt8((nSource & 0x00ff0000) >> 16); 122 aArray[2] = sal_uInt8((nSource & 0x0000ff00) >> 8); 123 aArray[3] = sal_uInt8(nSource & 0x000000ff); 124 125 return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten; 126 } 127 128 bool read_OString(FileSharedPtr const & rFile, OString& rTarget) 129 { 130 sal_uInt32 nLength(0); 131 132 if (!read_sal_uInt32(rFile, nLength)) 133 { 134 return false; 135 } 136 137 sal_uInt64 nPos; 138 if (osl::File::E_None != rFile->getPos(nPos)) 139 return false; 140 141 sal_uInt64 nSize; 142 if (osl::File::E_None != rFile->getSize(nSize)) 143 return false; 144 145 const auto nRemainingSize = nSize - nPos; 146 if (nLength > nRemainingSize) 147 return false; 148 149 std::vector<char> aTarget(nLength); 150 sal_uInt64 nBaseRead(0); 151 152 // read rTarget 153 if (osl::File::E_None == rFile->read(static_cast<void*>(aTarget.data()), nLength, nBaseRead) && nLength == nBaseRead) 154 { 155 rTarget = OString(aTarget.data(), static_cast<sal_Int32>(nBaseRead)); 156 return true; 157 } 158 159 return false; 160 } 161 162 bool write_OString(oslFileHandle& rHandle, const OString& rSource) 163 { 164 const sal_uInt32 nLength(rSource.getLength()); 165 166 if (!write_sal_uInt32(rHandle, nLength)) 167 { 168 return false; 169 } 170 171 sal_uInt64 nBaseWritten(0); 172 173 return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(rSource.getStr()), nLength, &nBaseWritten) && nLength == nBaseWritten; 174 } 175 176 OUString createFileURL( 177 std::u16string_view rURL, std::u16string_view rName, std::u16string_view rExt) 178 { 179 OUString aRetval; 180 181 if (!rURL.empty() && !rName.empty()) 182 { 183 aRetval = OUString::Concat(rURL) + "/" + rName; 184 185 if (!rExt.empty()) 186 { 187 aRetval += OUString::Concat(".") + rExt; 188 } 189 } 190 191 return aRetval; 192 } 193 194 OUString createPackURL(std::u16string_view rURL, std::u16string_view rName) 195 { 196 OUString aRetval; 197 198 if (!rURL.empty() && !rName.empty()) 199 { 200 aRetval = OUString::Concat(rURL) + "/" + rName + ".pack"; 201 } 202 203 return aRetval; 204 } 205 } 206 207 namespace 208 { 209 enum PackageRepository { USER, SHARED, BUNDLED }; 210 211 class ExtensionInfoEntry 212 { 213 private: 214 OString maName; // extension name 215 PackageRepository maRepository; // user|shared|bundled 216 bool mbEnabled; // state 217 218 public: 219 ExtensionInfoEntry() 220 : maRepository(USER), 221 mbEnabled(false) 222 { 223 } 224 225 ExtensionInfoEntry(const OString& rName, bool bEnabled) 226 : maName(rName), 227 maRepository(USER), 228 mbEnabled(bEnabled) 229 { 230 } 231 232 ExtensionInfoEntry(const uno::Reference< deployment::XPackage >& rxPackage) 233 : maName(OUStringToOString(rxPackage->getName(), RTL_TEXTENCODING_ASCII_US)), 234 maRepository(USER), 235 mbEnabled(false) 236 { 237 // check maRepository 238 const OString aRepName(OUStringToOString(rxPackage->getRepositoryName(), RTL_TEXTENCODING_ASCII_US)); 239 240 if (aRepName == "shared") 241 { 242 maRepository = SHARED; 243 } 244 else if (aRepName == "bundled") 245 { 246 maRepository = BUNDLED; 247 } 248 249 // check mbEnabled 250 const beans::Optional< beans::Ambiguous< sal_Bool > > option( 251 rxPackage->isRegistered(uno::Reference< task::XAbortChannel >(), 252 uno::Reference< ucb::XCommandEnvironment >())); 253 254 if (option.IsPresent) 255 { 256 ::beans::Ambiguous< sal_Bool > const& reg = option.Value; 257 258 if (!reg.IsAmbiguous) 259 { 260 mbEnabled = reg.Value; 261 } 262 } 263 } 264 265 bool isSameExtension(const ExtensionInfoEntry& rComp) const 266 { 267 return (maRepository == rComp.maRepository && maName == rComp.maName); 268 } 269 270 bool operator<(const ExtensionInfoEntry& rComp) const 271 { 272 if (maRepository == rComp.maRepository) 273 { 274 if (maName == rComp.maName) 275 { 276 return mbEnabled < rComp.mbEnabled; 277 } 278 else 279 { 280 return 0 > maName.compareTo(rComp.maName); 281 } 282 } 283 else 284 { 285 return maRepository < rComp.maRepository; 286 } 287 } 288 289 bool read_entry(FileSharedPtr const & rFile) 290 { 291 // read maName 292 if (!read_OString(rFile, maName)) 293 { 294 return false; 295 } 296 297 // read maRepository 298 sal_uInt32 nState(0); 299 300 if (read_sal_uInt32(rFile, nState)) 301 { 302 maRepository = static_cast< PackageRepository >(nState); 303 } 304 else 305 { 306 return false; 307 } 308 309 // read mbEnabled 310 if (read_sal_uInt32(rFile, nState)) 311 { 312 mbEnabled = static_cast< bool >(nState); 313 } 314 else 315 { 316 return false; 317 } 318 319 return true; 320 } 321 322 bool write_entry(oslFileHandle& rHandle) const 323 { 324 // write maName; 325 if (!write_OString(rHandle, maName)) 326 { 327 return false; 328 } 329 330 // write maRepository 331 sal_uInt32 nState(maRepository); 332 333 if (!write_sal_uInt32(rHandle, nState)) 334 { 335 return false; 336 } 337 338 // write mbEnabled 339 nState = static_cast< sal_uInt32 >(mbEnabled); 340 341 return write_sal_uInt32(rHandle, nState); 342 } 343 344 const OString& getName() const 345 { 346 return maName; 347 } 348 349 bool isEnabled() const 350 { 351 return mbEnabled; 352 } 353 }; 354 355 typedef std::vector< ExtensionInfoEntry > ExtensionInfoEntryVector; 356 357 constexpr OUStringLiteral gaRegPath { u"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml" }; 358 359 class ExtensionInfo 360 { 361 private: 362 ExtensionInfoEntryVector maEntries; 363 364 public: 365 ExtensionInfo() 366 { 367 } 368 369 const ExtensionInfoEntryVector& getExtensionInfoEntryVector() const 370 { 371 return maEntries; 372 } 373 374 void reset() 375 { 376 // clear all data 377 maEntries.clear(); 378 } 379 380 void createUsingXExtensionManager() 381 { 382 // clear all data 383 reset(); 384 385 // create content from current extension configuration 386 uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages; 387 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 388 uno::Reference< deployment::XExtensionManager > m_xExtensionManager = deployment::ExtensionManager::get(xContext); 389 390 try 391 { 392 xAllPackages = m_xExtensionManager->getAllExtensions(uno::Reference< task::XAbortChannel >(), 393 uno::Reference< ucb::XCommandEnvironment >()); 394 } 395 catch (const deployment::DeploymentException &) 396 { 397 return; 398 } 399 catch (const ucb::CommandFailedException &) 400 { 401 return; 402 } 403 catch (const ucb::CommandAbortedException &) 404 { 405 return; 406 } 407 catch (const lang::IllegalArgumentException & e) 408 { 409 css::uno::Any anyEx = cppu::getCaughtException(); 410 throw css::lang::WrappedTargetRuntimeException( e.Message, 411 e.Context, anyEx ); 412 } 413 414 for (const uno::Sequence< uno::Reference< deployment::XPackage > > & xPackageList : std::as_const(xAllPackages)) 415 { 416 for (const uno::Reference< deployment::XPackage > & xPackage : xPackageList) 417 { 418 if (xPackage.is()) 419 { 420 maEntries.emplace_back(xPackage); 421 } 422 } 423 } 424 425 if (!maEntries.empty()) 426 { 427 // sort the list 428 std::sort(maEntries.begin(), maEntries.end()); 429 } 430 } 431 432 private: 433 void visitNodesXMLRead(const uno::Reference< xml::dom::XElement >& rElement) 434 { 435 if (!rElement.is()) 436 return; 437 438 const OUString aTagName(rElement->getTagName()); 439 440 if (aTagName == "extension") 441 { 442 OUString aAttrUrl(rElement->getAttribute("url")); 443 const OUString aAttrRevoked(rElement->getAttribute("revoked")); 444 445 if (!aAttrUrl.isEmpty()) 446 { 447 const sal_Int32 nIndex(aAttrUrl.lastIndexOf('/')); 448 449 if (nIndex > 0 && aAttrUrl.getLength() > nIndex + 1) 450 { 451 aAttrUrl = aAttrUrl.copy(nIndex + 1); 452 } 453 454 const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean()); 455 maEntries.emplace_back( 456 OUStringToOString(aAttrUrl, RTL_TEXTENCODING_ASCII_US), 457 bEnabled); 458 } 459 } 460 else 461 { 462 uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes(); 463 464 if (aList.is()) 465 { 466 const sal_Int32 nLength(aList->getLength()); 467 468 for (sal_Int32 a(0); a < nLength; a++) 469 { 470 const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY); 471 472 if (aChild.is()) 473 { 474 visitNodesXMLRead(aChild); 475 } 476 } 477 } 478 } 479 } 480 481 public: 482 void createUserExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) 483 { 484 const OUString aPath( 485 OUString::Concat(rUserConfigWorkURL) + "/uno_packages/cache" + gaRegPath); 486 createExtensionRegistryEntriesFromXML(aPath); 487 } 488 489 void createSharedExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) 490 { 491 const OUString aPath( 492 OUString::Concat(rUserConfigWorkURL) + "/extensions/shared" + gaRegPath); 493 createExtensionRegistryEntriesFromXML(aPath); 494 } 495 496 void createBundledExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL) 497 { 498 const OUString aPath( 499 OUString::Concat(rUserConfigWorkURL) + "/extensions/bundled" + gaRegPath); 500 createExtensionRegistryEntriesFromXML(aPath); 501 } 502 503 504 void createExtensionRegistryEntriesFromXML(const OUString& aPath) 505 { 506 if (DirectoryHelper::fileExists(aPath)) 507 { 508 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 509 uno::Reference< xml::dom::XDocumentBuilder > xBuilder(xml::dom::DocumentBuilder::create(xContext)); 510 uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(aPath); 511 512 if (aDocument.is()) 513 { 514 visitNodesXMLRead(aDocument->getDocumentElement()); 515 } 516 } 517 518 if (!maEntries.empty()) 519 { 520 // sort the list 521 std::sort(maEntries.begin(), maEntries.end()); 522 } 523 } 524 525 private: 526 static bool visitNodesXMLChange( 527 const OUString& rTagToSearch, 528 const uno::Reference< xml::dom::XElement >& rElement, 529 const ExtensionInfoEntryVector& rToBeEnabled, 530 const ExtensionInfoEntryVector& rToBeDisabled) 531 { 532 bool bChanged(false); 533 534 if (rElement.is()) 535 { 536 const OUString aTagName(rElement->getTagName()); 537 538 if (aTagName == rTagToSearch) 539 { 540 const OString aAttrUrl(OUStringToOString(rElement->getAttribute("url"), RTL_TEXTENCODING_ASCII_US)); 541 const OUString aAttrRevoked(rElement->getAttribute("revoked")); 542 const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean()); 543 544 if (!aAttrUrl.isEmpty()) 545 { 546 for (const auto& enable : rToBeEnabled) 547 { 548 if (-1 != aAttrUrl.indexOf(enable.getName())) 549 { 550 if (!bEnabled) 551 { 552 // needs to be enabled 553 rElement->removeAttribute("revoked"); 554 bChanged = true; 555 } 556 } 557 } 558 559 for (const auto& disable : rToBeDisabled) 560 { 561 if (-1 != aAttrUrl.indexOf(disable.getName())) 562 { 563 if (bEnabled) 564 { 565 // needs to be disabled 566 rElement->setAttribute("revoked", "true"); 567 bChanged = true; 568 } 569 } 570 } 571 } 572 } 573 else 574 { 575 uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes(); 576 577 if (aList.is()) 578 { 579 const sal_Int32 nLength(aList->getLength()); 580 581 for (sal_Int32 a(0); a < nLength; a++) 582 { 583 const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY); 584 585 if (aChild.is()) 586 { 587 bChanged |= visitNodesXMLChange( 588 rTagToSearch, 589 aChild, 590 rToBeEnabled, 591 rToBeDisabled); 592 } 593 } 594 } 595 } 596 } 597 598 return bChanged; 599 } 600 601 static void visitNodesXMLChangeOneCase( 602 const OUString& rUnoPackagReg, 603 const OUString& rTagToSearch, 604 const ExtensionInfoEntryVector& rToBeEnabled, 605 const ExtensionInfoEntryVector& rToBeDisabled) 606 { 607 if (!DirectoryHelper::fileExists(rUnoPackagReg)) 608 return; 609 610 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 611 uno::Reference< xml::dom::XDocumentBuilder > xBuilder = xml::dom::DocumentBuilder::create(xContext); 612 uno::Reference< xml::dom::XDocument > aDocument = xBuilder->parseURI(rUnoPackagReg); 613 614 if (!aDocument.is()) 615 return; 616 617 if (!visitNodesXMLChange( 618 rTagToSearch, 619 aDocument->getDocumentElement(), 620 rToBeEnabled, 621 rToBeDisabled)) 622 return; 623 624 // did change - write back 625 uno::Reference< xml::sax::XSAXSerializable > xSerializer(aDocument, uno::UNO_QUERY); 626 627 if (!xSerializer.is()) 628 return; 629 630 // create a SAXWriter 631 uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext); 632 uno::Reference< io::XStream > xTempFile = io::TempFile::create(xContext); 633 uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream(); 634 635 // set output stream and do the serialization 636 xSaxWriter->setOutputStream(xOutStrm); 637 xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >()); 638 639 // get URL from temp file 640 uno::Reference < beans::XPropertySet > xTempFileProps(xTempFile, uno::UNO_QUERY); 641 uno::Any aUrl = xTempFileProps->getPropertyValue("Uri"); 642 OUString aTempURL; 643 aUrl >>= aTempURL; 644 645 // copy back file 646 if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL)) 647 return; 648 649 if (DirectoryHelper::fileExists(rUnoPackagReg)) 650 { 651 osl::File::remove(rUnoPackagReg); 652 } 653 654 #if OSL_DEBUG_LEVEL > 1 655 SAL_WARN_IF(osl::FileBase::E_None != osl::File::move(aTempURL, rUnoPackagReg), "comphelper.backupfilehelper", "could not copy back modified Extension configuration file"); 656 #else 657 osl::File::move(aTempURL, rUnoPackagReg); 658 #endif 659 } 660 661 public: 662 static void changeEnableDisableStateInXML( 663 std::u16string_view rUserConfigWorkURL, 664 const ExtensionInfoEntryVector& rToBeEnabled, 665 const ExtensionInfoEntryVector& rToBeDisabled) 666 { 667 static const OUStringLiteral aRegPathFront(u"/uno_packages/cache/registry/com.sun.star.comp.deployment."); 668 static const OUStringLiteral aRegPathBack(u".PackageRegistryBackend/backenddb.xml"); 669 // first appearance to check 670 { 671 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "bundle" + aRegPathBack); 672 673 visitNodesXMLChangeOneCase( 674 aUnoPackagReg, 675 "extension", 676 rToBeEnabled, 677 rToBeDisabled); 678 } 679 680 // second appearance to check 681 { 682 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "configuration" + aRegPathBack); 683 684 visitNodesXMLChangeOneCase( 685 aUnoPackagReg, 686 "configuration", 687 rToBeEnabled, 688 rToBeDisabled); 689 } 690 691 // third appearance to check 692 { 693 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "script" + aRegPathBack); 694 695 visitNodesXMLChangeOneCase( 696 aUnoPackagReg, 697 "script", 698 rToBeEnabled, 699 rToBeDisabled); 700 } 701 } 702 703 bool read_entries(FileSharedPtr const & rFile) 704 { 705 // read NumExtensionEntries 706 sal_uInt32 nExtEntries(0); 707 708 if (!read_sal_uInt32(rFile, nExtEntries)) 709 { 710 return false; 711 } 712 713 // coverity#1373663 Untrusted loop bound, check file size 714 // isn't utterly broken 715 sal_uInt64 nFileSize(0); 716 rFile->getSize(nFileSize); 717 if (nFileSize < nExtEntries) 718 return false; 719 720 for (sal_uInt32 a(0); a < nExtEntries; a++) 721 { 722 ExtensionInfoEntry aNewEntry; 723 724 if (aNewEntry.read_entry(rFile)) 725 { 726 maEntries.push_back(aNewEntry); 727 } 728 else 729 { 730 return false; 731 } 732 } 733 734 return true; 735 } 736 737 bool write_entries(oslFileHandle& rHandle) const 738 { 739 const sal_uInt32 nExtEntries(maEntries.size()); 740 741 if (!write_sal_uInt32(rHandle, nExtEntries)) 742 { 743 return false; 744 } 745 746 for (const auto& a : maEntries) 747 { 748 if (!a.write_entry(rHandle)) 749 { 750 return false; 751 } 752 } 753 754 return true; 755 } 756 757 bool createTempFile(OUString& rTempFileName) 758 { 759 oslFileHandle aHandle; 760 bool bRetval(false); 761 762 // create current configuration 763 if (maEntries.empty()) 764 { 765 createUsingXExtensionManager(); 766 } 767 768 // open target temp file and write current configuration to it - it exists until deleted 769 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &rTempFileName)) 770 { 771 bRetval = write_entries(aHandle); 772 773 // close temp file - it exists until deleted 774 osl_closeFile(aHandle); 775 } 776 777 return bRetval; 778 } 779 780 bool areThereEnabledExtensions() const 781 { 782 for (const auto& a : maEntries) 783 { 784 if (a.isEnabled()) 785 { 786 return true; 787 } 788 } 789 790 return false; 791 } 792 }; 793 } 794 795 namespace 796 { 797 class PackedFileEntry 798 { 799 private: 800 sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file 801 sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not) 802 sal_uInt32 mnOffset; // offset in File (zero identifies new file) 803 sal_uInt32 mnCrc32; // checksum 804 FileSharedPtr maFile; // file where to find the data (at offset) 805 bool const mbDoCompress; // flag if this file is scheduled to be compressed when written 806 807 bool copy_content_straight(oslFileHandle& rTargetHandle) 808 { 809 if (maFile && osl::File::E_None == maFile->open(osl_File_OpenFlag_Read)) 810 { 811 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; 812 sal_uInt64 nBytesTransfer(0); 813 sal_uInt64 nSize(getPackFileSize()); 814 815 // set offset in source file - when this is zero, a new file is to be added 816 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) 817 { 818 while (nSize != 0) 819 { 820 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); 821 822 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) 823 { 824 break; 825 } 826 827 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aArray), nToTransfer, &nBytesTransfer) || nBytesTransfer != nToTransfer) 828 { 829 break; 830 } 831 832 nSize -= nToTransfer; 833 } 834 } 835 836 maFile->close(); 837 return (0 == nSize); 838 } 839 840 return false; 841 } 842 843 bool copy_content_compress(oslFileHandle& rTargetHandle) 844 { 845 if (maFile && osl::File::E_None == maFile->open(osl_File_OpenFlag_Read)) 846 { 847 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; 848 sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE]; 849 sal_uInt64 nBytesTransfer(0); 850 sal_uInt64 nSize(getPackFileSize()); 851 z_stream zstream; 852 memset(&zstream, 0, sizeof(zstream)); 853 854 if (Z_OK == deflateInit(&zstream, Z_BEST_COMPRESSION)) 855 { 856 // set offset in source file - when this is zero, a new file is to be added 857 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) 858 { 859 bool bOkay(true); 860 861 while (bOkay && nSize != 0) 862 { 863 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); 864 865 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) 866 { 867 break; 868 } 869 870 zstream.avail_in = nToTransfer; 871 zstream.next_in = reinterpret_cast<unsigned char*>(aArray); 872 873 do { 874 zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE; 875 zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer); 876 #if !defined Z_PREFIX 877 const sal_Int64 nRetval(deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH)); 878 #else 879 const sal_Int64 nRetval(z_deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH)); 880 #endif 881 if (Z_STREAM_ERROR == nRetval) 882 { 883 bOkay = false; 884 } 885 else 886 { 887 const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out); 888 889 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable) 890 { 891 bOkay = false; 892 } 893 } 894 } while (bOkay && 0 == zstream.avail_out); 895 896 if (!bOkay) 897 { 898 break; 899 } 900 901 nSize -= nToTransfer; 902 } 903 904 #if !defined Z_PREFIX 905 deflateEnd(&zstream); 906 #else 907 z_deflateEnd(&zstream); 908 #endif 909 } 910 } 911 912 maFile->close(); 913 914 // get compressed size and add to entry 915 if (mnFullFileSize == mnPackFileSize && mnFullFileSize == zstream.total_in) 916 { 917 mnPackFileSize = zstream.total_out; 918 } 919 920 return (0 == nSize); 921 } 922 923 return false; 924 } 925 926 bool copy_content_uncompress(oslFileHandle& rTargetHandle) 927 { 928 if (maFile && osl::File::E_None == maFile->open(osl_File_OpenFlag_Read)) 929 { 930 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE]; 931 sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE]; 932 sal_uInt64 nBytesTransfer(0); 933 sal_uInt64 nSize(getPackFileSize()); 934 z_stream zstream; 935 memset(&zstream, 0, sizeof(zstream)); 936 937 if (Z_OK == inflateInit(&zstream)) 938 { 939 // set offset in source file - when this is zero, a new file is to be added 940 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset()))) 941 { 942 bool bOkay(true); 943 944 while (bOkay && nSize != 0) 945 { 946 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE))); 947 948 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer) 949 { 950 break; 951 } 952 953 zstream.avail_in = nToTransfer; 954 zstream.next_in = reinterpret_cast<unsigned char*>(aArray); 955 956 do { 957 zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE; 958 zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer); 959 #if !defined Z_PREFIX 960 const sal_Int64 nRetval(inflate(&zstream, Z_NO_FLUSH)); 961 #else 962 const sal_Int64 nRetval(z_inflate(&zstream, Z_NO_FLUSH)); 963 #endif 964 if (Z_STREAM_ERROR == nRetval) 965 { 966 bOkay = false; 967 } 968 else 969 { 970 const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out); 971 972 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable) 973 { 974 bOkay = false; 975 } 976 } 977 } while (bOkay && 0 == zstream.avail_out); 978 979 if (!bOkay) 980 { 981 break; 982 } 983 984 nSize -= nToTransfer; 985 } 986 987 #if !defined Z_PREFIX 988 deflateEnd(&zstream); 989 #else 990 z_deflateEnd(&zstream); 991 #endif 992 } 993 } 994 995 maFile->close(); 996 return (0 == nSize); 997 } 998 999 return false; 1000 } 1001 1002 1003 public: 1004 // create new, uncompressed entry 1005 PackedFileEntry( 1006 sal_uInt32 nFullFileSize, 1007 sal_uInt32 nCrc32, 1008 FileSharedPtr const & rFile, 1009 bool bDoCompress) 1010 : mnFullFileSize(nFullFileSize), 1011 mnPackFileSize(nFullFileSize), 1012 mnOffset(0), 1013 mnCrc32(nCrc32), 1014 maFile(rFile), 1015 mbDoCompress(bDoCompress) 1016 { 1017 } 1018 1019 // create entry to be loaded as header (read_header) 1020 PackedFileEntry() 1021 : mnFullFileSize(0), 1022 mnPackFileSize(0), 1023 mnOffset(0), 1024 mnCrc32(0), 1025 mbDoCompress(false) 1026 { 1027 } 1028 1029 sal_uInt32 getFullFileSize() const 1030 { 1031 return mnFullFileSize; 1032 } 1033 1034 sal_uInt32 getPackFileSize() const 1035 { 1036 return mnPackFileSize; 1037 } 1038 1039 sal_uInt32 getOffset() const 1040 { 1041 return mnOffset; 1042 } 1043 1044 void setOffset(sal_uInt32 nOffset) 1045 { 1046 mnOffset = nOffset; 1047 } 1048 1049 static sal_uInt32 getEntrySize() 1050 { 1051 return 12; 1052 } 1053 1054 sal_uInt32 getCrc32() const 1055 { 1056 return mnCrc32; 1057 } 1058 1059 bool read_header(FileSharedPtr const & rFile) 1060 { 1061 if (!rFile) 1062 { 1063 return false; 1064 } 1065 1066 maFile = rFile; 1067 1068 // read and compute full file size 1069 if (!read_sal_uInt32(rFile, mnFullFileSize)) 1070 { 1071 return false; 1072 } 1073 1074 // read and compute entry crc32 1075 if (!read_sal_uInt32(rFile, mnCrc32)) 1076 { 1077 return false; 1078 } 1079 1080 // read and compute packed size 1081 if (!read_sal_uInt32(rFile, mnPackFileSize)) 1082 { 1083 return false; 1084 } 1085 1086 return true; 1087 } 1088 1089 bool write_header(oslFileHandle& rHandle) const 1090 { 1091 // write full file size 1092 if (!write_sal_uInt32(rHandle, mnFullFileSize)) 1093 { 1094 return false; 1095 } 1096 1097 // write crc32 1098 if (!write_sal_uInt32(rHandle, mnCrc32)) 1099 { 1100 return false; 1101 } 1102 1103 // write packed file size 1104 if (!write_sal_uInt32(rHandle, mnPackFileSize)) 1105 { 1106 return false; 1107 } 1108 1109 return true; 1110 } 1111 1112 bool copy_content(oslFileHandle& rTargetHandle, bool bUncompress) 1113 { 1114 if (bUncompress) 1115 { 1116 if (getFullFileSize() == getPackFileSize()) 1117 { 1118 // not compressed, just copy 1119 return copy_content_straight(rTargetHandle); 1120 } 1121 else 1122 { 1123 // compressed, need to uncompress on copy 1124 return copy_content_uncompress(rTargetHandle); 1125 } 1126 } 1127 else if (0 == getOffset()) 1128 { 1129 if (mbDoCompress) 1130 { 1131 // compressed wanted, need to compress on copy 1132 return copy_content_compress(rTargetHandle); 1133 } 1134 else 1135 { 1136 // not compressed, straight copy 1137 return copy_content_straight(rTargetHandle); 1138 } 1139 } 1140 else 1141 { 1142 return copy_content_straight(rTargetHandle); 1143 } 1144 } 1145 }; 1146 } 1147 1148 namespace 1149 { 1150 class PackedFile 1151 { 1152 private: 1153 const OUString maURL; 1154 std::deque< PackedFileEntry > 1155 maPackedFileEntryVector; 1156 bool mbChanged; 1157 1158 public: 1159 PackedFile(const OUString& rURL) 1160 : maURL(rURL), 1161 mbChanged(false) 1162 { 1163 FileSharedPtr aSourceFile = std::make_shared<osl::File>(rURL); 1164 1165 if (osl::File::E_None == aSourceFile->open(osl_File_OpenFlag_Read)) 1166 { 1167 sal_uInt64 nBaseLen(0); 1168 aSourceFile->getSize(nBaseLen); 1169 1170 // we need at least File_ID and num entries -> 8byte 1171 if (8 < nBaseLen) 1172 { 1173 sal_uInt8 aArray[4]; 1174 sal_uInt64 nBaseRead(0); 1175 1176 // read and check File_ID 1177 if (osl::File::E_None == aSourceFile->read(static_cast< void* >(aArray), 4, nBaseRead) && 4 == nBaseRead) 1178 { 1179 if ('P' == aArray[0] && 'A' == aArray[1] && 'C' == aArray[2] && 'K' == aArray[3]) 1180 { 1181 // read and compute num entries in this file 1182 if (osl::File::E_None == aSourceFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead) 1183 { 1184 sal_uInt32 nEntries((sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3])); 1185 1186 // if there are entries (and less than max), read them 1187 if (nEntries >= 1 && nEntries <= 10) 1188 { 1189 for (sal_uInt32 a(0); a < nEntries; a++) 1190 { 1191 // create new entry, read header (size, crc and PackedSize), 1192 // set offset and source file 1193 PackedFileEntry aEntry; 1194 1195 if (aEntry.read_header(aSourceFile)) 1196 { 1197 // add to local data 1198 maPackedFileEntryVector.push_back(aEntry); 1199 } 1200 else 1201 { 1202 // error 1203 nEntries = 0; 1204 } 1205 } 1206 1207 if (0 == nEntries) 1208 { 1209 // on read error clear local data 1210 maPackedFileEntryVector.clear(); 1211 } 1212 else 1213 { 1214 // calculate and set offsets to file binary content 1215 sal_uInt32 nHeaderSize(8); 1216 1217 nHeaderSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize(); 1218 1219 sal_uInt32 nOffset(nHeaderSize); 1220 1221 for (auto& b : maPackedFileEntryVector) 1222 { 1223 b.setOffset(nOffset); 1224 nOffset += b.getPackFileSize(); 1225 } 1226 } 1227 } 1228 } 1229 } 1230 } 1231 } 1232 1233 aSourceFile->close(); 1234 } 1235 1236 if (maPackedFileEntryVector.empty()) 1237 { 1238 // on error or no data get rid of pack file 1239 osl::File::remove(maURL); 1240 } 1241 } 1242 1243 void flush() 1244 { 1245 bool bRetval(true); 1246 1247 if (maPackedFileEntryVector.empty()) 1248 { 1249 // get rid of (now?) empty pack file 1250 osl::File::remove(maURL); 1251 } 1252 else if (mbChanged) 1253 { 1254 // need to create a new pack file, do this in a temp file to which data 1255 // will be copied from local file (so keep it here until this is done) 1256 oslFileHandle aHandle; 1257 OUString aTempURL; 1258 1259 // open target temp file - it exists until deleted 1260 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) 1261 { 1262 sal_uInt8 aArray[4]; 1263 sal_uInt64 nBaseWritten(0); 1264 1265 aArray[0] = 'P'; 1266 aArray[1] = 'A'; 1267 aArray[2] = 'C'; 1268 aArray[3] = 'K'; 1269 1270 // write File_ID 1271 if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten) 1272 { 1273 const sal_uInt32 nSize(maPackedFileEntryVector.size()); 1274 1275 // write number of entries 1276 if (write_sal_uInt32(aHandle, nSize)) 1277 { 1278 // write placeholder for headers. Due to the fact that 1279 // PackFileSize for newly added files gets set during 1280 // writing the content entry, write headers after content 1281 // is written. To do so, write placeholders here 1282 sal_uInt32 nWriteSize(0); 1283 1284 nWriteSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize(); 1285 1286 aArray[0] = aArray[1] = aArray[2] = aArray[3] = 0; 1287 1288 for (sal_uInt32 a(0); bRetval && a < nWriteSize; a++) 1289 { 1290 if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 1, &nBaseWritten) || 1 != nBaseWritten) 1291 { 1292 bRetval = false; 1293 } 1294 } 1295 1296 if (bRetval) 1297 { 1298 // write contents - this may adapt PackFileSize for new 1299 // files 1300 for (auto& candidate : maPackedFileEntryVector) 1301 { 1302 if (!candidate.copy_content(aHandle, false)) 1303 { 1304 bRetval = false; 1305 break; 1306 } 1307 } 1308 } 1309 1310 if (bRetval) 1311 { 1312 // seek back to header start (at position 8) 1313 if (osl_File_E_None != osl_setFilePos(aHandle, osl_Pos_Absolut, sal_Int64(8))) 1314 { 1315 bRetval = false; 1316 } 1317 } 1318 1319 if (bRetval) 1320 { 1321 // write headers 1322 for (const auto& candidate : maPackedFileEntryVector) 1323 { 1324 if (!candidate.write_header(aHandle)) 1325 { 1326 // error 1327 bRetval = false; 1328 break; 1329 } 1330 } 1331 } 1332 } 1333 } 1334 } 1335 1336 // close temp file (in all cases) - it exists until deleted 1337 osl_closeFile(aHandle); 1338 1339 if (bRetval) 1340 { 1341 // copy over existing file by first deleting original 1342 // and moving the temp file to old original 1343 osl::File::remove(maURL); 1344 osl::File::move(aTempURL, maURL); 1345 } 1346 1347 // delete temp file (in all cases - it may be moved already) 1348 osl::File::remove(aTempURL); 1349 } 1350 } 1351 1352 bool tryPush(FileSharedPtr const & rFileCandidate, bool bCompress) 1353 { 1354 sal_uInt64 nFileSize(0); 1355 1356 if (rFileCandidate && osl::File::E_None == rFileCandidate->open(osl_File_OpenFlag_Read)) 1357 { 1358 rFileCandidate->getSize(nFileSize); 1359 rFileCandidate->close(); 1360 } 1361 1362 if (0 == nFileSize) 1363 { 1364 // empty file offered 1365 return false; 1366 } 1367 1368 bool bNeedToAdd(false); 1369 sal_uInt32 nCrc32(0); 1370 1371 if (maPackedFileEntryVector.empty()) 1372 { 1373 // no backup yet, add as 1st backup 1374 bNeedToAdd = true; 1375 } 1376 else 1377 { 1378 // already backups there, check if different from last entry 1379 const PackedFileEntry& aLastEntry = maPackedFileEntryVector.back(); 1380 1381 // check if file is different 1382 if (aLastEntry.getFullFileSize() != static_cast<sal_uInt32>(nFileSize)) 1383 { 1384 // different size, different file 1385 bNeedToAdd = true; 1386 } 1387 else 1388 { 1389 // same size, check crc32 1390 nCrc32 = createCrc32(rFileCandidate, 0); 1391 1392 if (nCrc32 != aLastEntry.getCrc32()) 1393 { 1394 // different crc, different file 1395 bNeedToAdd = true; 1396 } 1397 } 1398 } 1399 1400 if (bNeedToAdd) 1401 { 1402 // create crc32 if not yet done 1403 if (0 == nCrc32) 1404 { 1405 nCrc32 = createCrc32(rFileCandidate, 0); 1406 } 1407 1408 // create a file entry for a new file. Offset is set automatically 1409 // to 0 to mark the entry as new file entry 1410 maPackedFileEntryVector.emplace_back( 1411 static_cast< sal_uInt32 >(nFileSize), 1412 nCrc32, 1413 rFileCandidate, 1414 bCompress); 1415 1416 mbChanged = true; 1417 } 1418 1419 return bNeedToAdd; 1420 } 1421 1422 bool tryPop(oslFileHandle& rHandle) 1423 { 1424 if (!maPackedFileEntryVector.empty()) 1425 { 1426 // already backups there, check if different from last entry 1427 PackedFileEntry& aLastEntry = maPackedFileEntryVector.back(); 1428 1429 // here the uncompress flag has to be determined, true 1430 // means to add the file compressed, false means to add it 1431 // uncompressed 1432 bool bRetval = aLastEntry.copy_content(rHandle, true); 1433 1434 if (bRetval) 1435 { 1436 maPackedFileEntryVector.pop_back(); 1437 mbChanged = true; 1438 } 1439 1440 return bRetval; 1441 } 1442 1443 return false; 1444 } 1445 1446 void tryReduceToNumBackups(sal_uInt16 nNumBackups) 1447 { 1448 while (maPackedFileEntryVector.size() > nNumBackups) 1449 { 1450 maPackedFileEntryVector.pop_front(); 1451 mbChanged = true; 1452 } 1453 } 1454 1455 bool empty() const 1456 { 1457 return maPackedFileEntryVector.empty(); 1458 } 1459 }; 1460 } 1461 1462 namespace comphelper 1463 { 1464 sal_uInt16 BackupFileHelper::mnMaxAllowedBackups = 10; 1465 bool BackupFileHelper::mbExitWasCalled = false; 1466 bool BackupFileHelper::mbSafeModeDirExists = false; 1467 OUString BackupFileHelper::maInitialBaseURL; 1468 OUString BackupFileHelper::maUserConfigBaseURL; 1469 OUString BackupFileHelper::maUserConfigWorkURL; 1470 OUString BackupFileHelper::maRegModName; 1471 OUString BackupFileHelper::maExt; 1472 1473 const OUString& BackupFileHelper::getInitialBaseURL() 1474 { 1475 if (maInitialBaseURL.isEmpty()) 1476 { 1477 // try to access user layer configuration file URL, the one that 1478 // points to registrymodifications.xcu 1479 OUString conf("${CONFIGURATION_LAYERS}"); 1480 rtl::Bootstrap::expandMacros(conf); 1481 static const OUStringLiteral aTokenUser(u"user:"); 1482 sal_Int32 nStart(conf.indexOf(aTokenUser)); 1483 1484 if (-1 != nStart) 1485 { 1486 nStart += aTokenUser.getLength(); 1487 sal_Int32 nEnd(conf.indexOf(' ', nStart)); 1488 1489 if (-1 == nEnd) 1490 { 1491 nEnd = conf.getLength(); 1492 } 1493 1494 maInitialBaseURL = conf.copy(nStart, nEnd - nStart); 1495 (void)maInitialBaseURL.startsWith("!", &maInitialBaseURL); 1496 } 1497 1498 if (!maInitialBaseURL.isEmpty()) 1499 { 1500 // split URL at extension and at last path separator 1501 maUserConfigBaseURL = DirectoryHelper::splitAtLastToken( 1502 DirectoryHelper::splitAtLastToken(maInitialBaseURL, '.', maExt), '/', 1503 maRegModName); 1504 } 1505 1506 if (!maUserConfigBaseURL.isEmpty()) 1507 { 1508 // check if SafeModeDir exists 1509 mbSafeModeDirExists = DirectoryHelper::dirExists(maUserConfigBaseURL + "/" + getSafeModeName()); 1510 } 1511 1512 maUserConfigWorkURL = maUserConfigBaseURL; 1513 1514 if (mbSafeModeDirExists) 1515 { 1516 // adapt work URL to do all repair op's in the correct directory 1517 maUserConfigWorkURL += "/" + getSafeModeName(); 1518 } 1519 } 1520 1521 return maInitialBaseURL; 1522 } 1523 1524 const OUString& BackupFileHelper::getSafeModeName() 1525 { 1526 static const OUString aSafeMode("SafeMode"); 1527 1528 return aSafeMode; 1529 } 1530 1531 BackupFileHelper::BackupFileHelper() 1532 : mnNumBackups(2), 1533 mnMode(1), 1534 mbActive(false), 1535 mbExtensions(true), 1536 mbCompress(true) 1537 { 1538 OUString sTokenOut; 1539 1540 // read configuration item 'SecureUserConfig' -> bool on/off 1541 if (rtl::Bootstrap::get("SecureUserConfig", sTokenOut)) 1542 { 1543 mbActive = sTokenOut.toBoolean(); 1544 } 1545 1546 if (mbActive) 1547 { 1548 // ensure existence 1549 getInitialBaseURL(); 1550 1551 // if not found, we are out of business (maExt may be empty) 1552 mbActive = !maInitialBaseURL.isEmpty() && !maUserConfigBaseURL.isEmpty() && !maRegModName.isEmpty(); 1553 } 1554 1555 if (mbActive && rtl::Bootstrap::get("SecureUserConfigNumCopies", sTokenOut)) 1556 { 1557 const sal_uInt16 nConfigNumCopies(static_cast<sal_uInt16>(sTokenOut.toUInt32())); 1558 1559 // limit to range [1..mnMaxAllowedBackups] 1560 mnNumBackups = std::clamp(mnNumBackups, nConfigNumCopies, mnMaxAllowedBackups); 1561 } 1562 1563 if (mbActive && rtl::Bootstrap::get("SecureUserConfigMode", sTokenOut)) 1564 { 1565 const sal_uInt16 nMode(static_cast<sal_uInt16>(sTokenOut.toUInt32())); 1566 1567 // limit to range [0..2] 1568 mnMode = std::min(nMode, sal_uInt16(2)); 1569 } 1570 1571 if (mbActive && rtl::Bootstrap::get("SecureUserConfigExtensions", sTokenOut)) 1572 { 1573 mbExtensions = sTokenOut.toBoolean(); 1574 } 1575 1576 if (mbActive && rtl::Bootstrap::get("SecureUserConfigCompress", sTokenOut)) 1577 { 1578 mbCompress = sTokenOut.toBoolean(); 1579 } 1580 } 1581 1582 void BackupFileHelper::setExitWasCalled() 1583 { 1584 mbExitWasCalled = true; 1585 } 1586 1587 bool BackupFileHelper::getExitWasCalled() 1588 { 1589 return mbExitWasCalled; 1590 } 1591 1592 void BackupFileHelper::reactOnSafeMode(bool bSafeMode) 1593 { 1594 // ensure existence of needed paths 1595 getInitialBaseURL(); 1596 1597 if (maUserConfigBaseURL.isEmpty()) 1598 return; 1599 1600 if (bSafeMode) 1601 { 1602 if (!mbSafeModeDirExists) 1603 { 1604 std::set< OUString > aExcludeList; 1605 1606 // do not move SafeMode directory itself 1607 aExcludeList.insert(getSafeModeName()); 1608 1609 // init SafeMode by creating the 'SafeMode' directory and moving 1610 // all stuff there. All repairs will happen there. Both Dirs have to exist. 1611 // extend maUserConfigWorkURL as needed 1612 maUserConfigWorkURL = maUserConfigBaseURL + "/" + getSafeModeName(); 1613 1614 osl::Directory::createPath(maUserConfigWorkURL); 1615 DirectoryHelper::moveDirContent(maUserConfigBaseURL, maUserConfigWorkURL, aExcludeList); 1616 1617 // switch local flag, maUserConfigWorkURL is already reset 1618 mbSafeModeDirExists = true; 1619 } 1620 } 1621 else 1622 { 1623 if (mbSafeModeDirExists) 1624 { 1625 // SafeMode has ended, return to normal mode by moving all content 1626 // from 'SafeMode' directory back to UserDirectory and deleting it. 1627 // Both Dirs have to exist 1628 std::set< OUString > aExcludeList; 1629 1630 DirectoryHelper::moveDirContent(maUserConfigWorkURL, maUserConfigBaseURL, aExcludeList); 1631 osl::Directory::remove(maUserConfigWorkURL); 1632 1633 // switch local flag and reset maUserConfigWorkURL 1634 mbSafeModeDirExists = false; 1635 maUserConfigWorkURL = maUserConfigBaseURL; 1636 } 1637 } 1638 } 1639 1640 void BackupFileHelper::tryPush() 1641 { 1642 // no push when SafeModeDir exists, it may be Office's exit after SafeMode 1643 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not 1644 // done yet (is done at next startup) 1645 if (!mbActive || mbSafeModeDirExists) 1646 return; 1647 1648 const OUString aPackURL(getPackURL()); 1649 1650 // ensure dir and file vectors 1651 fillDirFileInfo(); 1652 1653 // process all files in question recursively 1654 if (!maDirs.empty() || !maFiles.empty()) 1655 { 1656 tryPush_Files( 1657 maDirs, 1658 maFiles, 1659 maUserConfigWorkURL, 1660 aPackURL); 1661 } 1662 } 1663 1664 void BackupFileHelper::tryPushExtensionInfo() 1665 { 1666 // no push when SafeModeDir exists, it may be Office's exit after SafeMode 1667 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not 1668 // done yet (is done at next startup) 1669 if (mbActive && mbExtensions && !mbSafeModeDirExists) 1670 { 1671 const OUString aPackURL(getPackURL()); 1672 1673 tryPush_extensionInfo(aPackURL); 1674 } 1675 } 1676 1677 bool BackupFileHelper::isPopPossible() 1678 { 1679 bool bPopPossible(false); 1680 1681 if (mbActive) 1682 { 1683 const OUString aPackURL(getPackURL()); 1684 1685 // ensure dir and file vectors 1686 fillDirFileInfo(); 1687 1688 // process all files in question recursively 1689 if (!maDirs.empty() || !maFiles.empty()) 1690 { 1691 bPopPossible = isPopPossible_files( 1692 maDirs, 1693 maFiles, 1694 maUserConfigWorkURL, 1695 aPackURL); 1696 } 1697 } 1698 1699 return bPopPossible; 1700 } 1701 1702 void BackupFileHelper::tryPop() 1703 { 1704 if (!mbActive) 1705 return; 1706 1707 bool bDidPop(false); 1708 const OUString aPackURL(getPackURL()); 1709 1710 // ensure dir and file vectors 1711 fillDirFileInfo(); 1712 1713 // process all files in question recursively 1714 if (!maDirs.empty() || !maFiles.empty()) 1715 { 1716 bDidPop = tryPop_files( 1717 maDirs, 1718 maFiles, 1719 maUserConfigWorkURL, 1720 aPackURL); 1721 } 1722 1723 if (bDidPop) 1724 { 1725 // try removal of evtl. empty directory 1726 osl::Directory::remove(aPackURL); 1727 } 1728 } 1729 1730 bool BackupFileHelper::isPopPossibleExtensionInfo() const 1731 { 1732 bool bPopPossible(false); 1733 1734 if (mbActive && mbExtensions) 1735 { 1736 const OUString aPackURL(getPackURL()); 1737 1738 bPopPossible = isPopPossible_extensionInfo(aPackURL); 1739 } 1740 1741 return bPopPossible; 1742 } 1743 1744 void BackupFileHelper::tryPopExtensionInfo() 1745 { 1746 if (!(mbActive && mbExtensions)) 1747 return; 1748 1749 bool bDidPop(false); 1750 const OUString aPackURL(getPackURL()); 1751 1752 bDidPop = tryPop_extensionInfo(aPackURL); 1753 1754 if (bDidPop) 1755 { 1756 // try removal of evtl. empty directory 1757 osl::Directory::remove(aPackURL); 1758 } 1759 } 1760 1761 bool BackupFileHelper::isTryDisableAllExtensionsPossible() 1762 { 1763 // check if there are still enabled extension which can be disabled, 1764 // but as we are now in SafeMode, use XML infos for this since the 1765 // extensions are not loaded from XExtensionManager 1766 class ExtensionInfo aExtensionInfo; 1767 1768 aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 1769 1770 return aExtensionInfo.areThereEnabledExtensions(); 1771 } 1772 1773 void BackupFileHelper::tryDisableAllExtensions() 1774 { 1775 // disable all still enabled extensions, 1776 // but as we are now in SafeMode, use XML infos for this since the 1777 // extensions are not loaded from XExtensionManager 1778 ExtensionInfo aCurrentExtensionInfo; 1779 const ExtensionInfoEntryVector aToBeEnabled{}; 1780 ExtensionInfoEntryVector aToBeDisabled; 1781 1782 aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 1783 1784 const ExtensionInfoEntryVector& rCurrentVector = aCurrentExtensionInfo.getExtensionInfoEntryVector(); 1785 1786 for (const auto& rCurrentInfo : rCurrentVector) 1787 { 1788 if (rCurrentInfo.isEnabled()) 1789 { 1790 aToBeDisabled.push_back(rCurrentInfo); 1791 } 1792 } 1793 1794 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled); 1795 } 1796 1797 bool BackupFileHelper::isTryDeinstallUserExtensionsPossible() 1798 { 1799 // check if there are User Extensions installed. 1800 class ExtensionInfo aExtensionInfo; 1801 1802 aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 1803 1804 return !aExtensionInfo.getExtensionInfoEntryVector().empty(); 1805 } 1806 1807 void BackupFileHelper::tryDeinstallUserExtensions() 1808 { 1809 // delete User Extension installs 1810 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/uno_packages"); 1811 } 1812 1813 bool BackupFileHelper::isTryResetSharedExtensionsPossible() 1814 { 1815 // check if there are shared Extensions installed 1816 class ExtensionInfo aExtensionInfo; 1817 1818 aExtensionInfo.createSharedExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 1819 1820 return !aExtensionInfo.getExtensionInfoEntryVector().empty(); 1821 } 1822 1823 void BackupFileHelper::tryResetSharedExtensions() 1824 { 1825 // reset shared extension info 1826 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/shared"); 1827 } 1828 1829 bool BackupFileHelper::isTryResetBundledExtensionsPossible() 1830 { 1831 // check if there are shared Extensions installed 1832 class ExtensionInfo aExtensionInfo; 1833 1834 aExtensionInfo.createBundledExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 1835 1836 return !aExtensionInfo.getExtensionInfoEntryVector().empty(); 1837 } 1838 1839 void BackupFileHelper::tryResetBundledExtensions() 1840 { 1841 // reset shared extension info 1842 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/bundled"); 1843 } 1844 1845 const std::vector< OUString >& BackupFileHelper::getCustomizationDirNames() 1846 { 1847 static std::vector< OUString > aDirNames = 1848 { 1849 "config", // UI config stuff 1850 "registry", // most of the registry stuff 1851 "psprint", // not really needed, can be abandoned 1852 "store", // not really needed, can be abandoned 1853 "temp", // not really needed, can be abandoned 1854 "pack" // own backup dir 1855 }; 1856 1857 return aDirNames; 1858 } 1859 1860 const std::vector< OUString >& BackupFileHelper::getCustomizationFileNames() 1861 { 1862 static std::vector< OUString > aFileNames = 1863 { 1864 "registrymodifications.xcu" // personal registry stuff 1865 }; 1866 1867 return aFileNames; 1868 } 1869 1870 namespace { 1871 uno::Reference<XElement> lcl_getConfigElement(const uno::Reference<XDocument>& xDocument, const OUString& rPath, 1872 const OUString& rKey, const OUString& rValue) 1873 { 1874 uno::Reference< XElement > itemElement = xDocument->createElement("item"); 1875 itemElement->setAttribute("oor:path", rPath); 1876 1877 uno::Reference< XElement > propElement = xDocument->createElement("prop"); 1878 propElement->setAttribute("oor:name", rKey); 1879 propElement->setAttribute("oor:op", "replace"); // Replace any other options 1880 1881 uno::Reference< XElement > valueElement = xDocument->createElement("value"); 1882 uno::Reference< XText > textElement = xDocument->createTextNode(rValue); 1883 1884 valueElement->appendChild(textElement); 1885 propElement->appendChild(valueElement); 1886 itemElement->appendChild(propElement); 1887 1888 return itemElement; 1889 } 1890 } 1891 1892 void BackupFileHelper::tryDisableHWAcceleration() 1893 { 1894 const OUString aRegistryModifications(maUserConfigWorkURL + "/registrymodifications.xcu"); 1895 if (!DirectoryHelper::fileExists(aRegistryModifications)) 1896 return; 1897 1898 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); 1899 uno::Reference< XDocumentBuilder > xBuilder = DocumentBuilder::create(xContext); 1900 uno::Reference< XDocument > xDocument = xBuilder->parseURI(aRegistryModifications); 1901 uno::Reference< XElement > xRootElement = xDocument->getDocumentElement(); 1902 1903 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", 1904 "DisableOpenGL", "true")); 1905 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/Misc", 1906 "UseOpenCL", "false")); 1907 // Do not disable Skia entirely, just force its CPU-based raster mode. 1908 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", 1909 "ForceSkia", "false")); 1910 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL", 1911 "ForceSkiaRaster", "true")); 1912 1913 // write back 1914 uno::Reference< xml::sax::XSAXSerializable > xSerializer(xDocument, uno::UNO_QUERY); 1915 1916 if (!xSerializer.is()) 1917 return; 1918 1919 // create a SAXWriter 1920 uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext); 1921 uno::Reference< io::XStream > xTempFile = io::TempFile::create(xContext); 1922 uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream(); 1923 1924 // set output stream and do the serialization 1925 xSaxWriter->setOutputStream(xOutStrm); 1926 xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >()); 1927 1928 // get URL from temp file 1929 uno::Reference < beans::XPropertySet > xTempFileProps(xTempFile, uno::UNO_QUERY); 1930 uno::Any aUrl = xTempFileProps->getPropertyValue("Uri"); 1931 OUString aTempURL; 1932 aUrl >>= aTempURL; 1933 1934 // copy back file 1935 if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL)) 1936 return; 1937 1938 if (DirectoryHelper::fileExists(aRegistryModifications)) 1939 { 1940 osl::File::remove(aRegistryModifications); 1941 } 1942 1943 int result = osl::File::move(aTempURL, aRegistryModifications); 1944 SAL_WARN_IF(result != osl::FileBase::E_None, "comphelper.backupfilehelper", "could not copy back modified Extension configuration file"); 1945 } 1946 1947 bool BackupFileHelper::isTryResetCustomizationsPossible() 1948 { 1949 // return true if not all of the customization selection dirs or files are deleted 1950 const std::vector< OUString >& rDirs = getCustomizationDirNames(); 1951 1952 for (const auto& a : rDirs) 1953 { 1954 if (DirectoryHelper::dirExists(maUserConfigWorkURL + "/" + a)) 1955 { 1956 return true; 1957 } 1958 } 1959 1960 const std::vector< OUString >& rFiles = getCustomizationFileNames(); 1961 1962 for (const auto& b : rFiles) 1963 { 1964 if (DirectoryHelper::fileExists(maUserConfigWorkURL + "/" + b)) 1965 { 1966 return true; 1967 } 1968 } 1969 1970 return false; 1971 } 1972 1973 void BackupFileHelper::tryResetCustomizations() 1974 { 1975 // delete all of the customization selection dirs 1976 const std::vector< OUString >& rDirs = getCustomizationDirNames(); 1977 1978 for (const auto& a : rDirs) 1979 { 1980 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/" + a); 1981 } 1982 1983 const std::vector< OUString >& rFiles = getCustomizationFileNames(); 1984 1985 for (const auto& b : rFiles) 1986 { 1987 osl::File::remove(maUserConfigWorkURL + "/" + b); 1988 } 1989 } 1990 1991 void BackupFileHelper::tryResetUserProfile() 1992 { 1993 // completely delete the current UserProfile 1994 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL); 1995 } 1996 1997 const OUString& BackupFileHelper::getUserProfileURL() 1998 { 1999 return maUserConfigBaseURL; 2000 } 2001 2002 const OUString& BackupFileHelper::getUserProfileWorkURL() 2003 { 2004 return maUserConfigWorkURL; 2005 } 2006 2007 /////////////////// helpers /////////////////////// 2008 2009 OUString BackupFileHelper::getPackURL() 2010 { 2011 return OUString(maUserConfigWorkURL + "/pack"); 2012 } 2013 2014 /////////////////// file push helpers /////////////////////// 2015 2016 bool BackupFileHelper::tryPush_Files( 2017 const std::set< OUString >& rDirs, 2018 const std::set< std::pair< OUString, OUString > >& rFiles, 2019 std::u16string_view rSourceURL, // source dir without trailing '/' 2020 const OUString& rTargetURL // target dir without trailing '/' 2021 ) 2022 { 2023 bool bDidPush(false); 2024 osl::Directory::createPath(rTargetURL); 2025 2026 // process files 2027 for (const auto& file : rFiles) 2028 { 2029 bDidPush |= tryPush_file( 2030 rSourceURL, 2031 rTargetURL, 2032 file.first, 2033 file.second); 2034 } 2035 2036 // process dirs 2037 for (const auto& dir : rDirs) 2038 { 2039 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); 2040 OUString aNewTargetURL(rTargetURL + "/" + dir); 2041 std::set< OUString > aNewDirs; 2042 std::set< std::pair< OUString, OUString > > aNewFiles; 2043 2044 DirectoryHelper::scanDirsAndFiles( 2045 aNewSourceURL, 2046 aNewDirs, 2047 aNewFiles); 2048 2049 if (!aNewDirs.empty() || !aNewFiles.empty()) 2050 { 2051 bDidPush |= tryPush_Files( 2052 aNewDirs, 2053 aNewFiles, 2054 aNewSourceURL, 2055 aNewTargetURL); 2056 } 2057 } 2058 2059 if (!bDidPush) 2060 { 2061 // try removal of evtl. empty directory 2062 osl::Directory::remove(rTargetURL); 2063 } 2064 2065 return bDidPush; 2066 } 2067 2068 bool BackupFileHelper::tryPush_file( 2069 std::u16string_view rSourceURL, // source dir without trailing '/' 2070 std::u16string_view rTargetURL, // target dir without trailing '/' 2071 std::u16string_view rName, // filename 2072 std::u16string_view rExt // extension (or empty) 2073 ) 2074 { 2075 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); 2076 2077 if (DirectoryHelper::fileExists(aFileURL)) 2078 { 2079 const OUString aPackURL(createPackURL(rTargetURL, rName)); 2080 PackedFile aPackedFile(aPackURL); 2081 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aFileURL); 2082 2083 if (aPackedFile.tryPush(aBaseFile, mbCompress)) 2084 { 2085 // reduce to allowed number and flush 2086 aPackedFile.tryReduceToNumBackups(mnNumBackups); 2087 aPackedFile.flush(); 2088 2089 return true; 2090 } 2091 } 2092 2093 return false; 2094 } 2095 2096 /////////////////// file pop possibilities helper /////////////////////// 2097 2098 bool BackupFileHelper::isPopPossible_files( 2099 const std::set< OUString >& rDirs, 2100 const std::set< std::pair< OUString, OUString > >& rFiles, 2101 std::u16string_view rSourceURL, // source dir without trailing '/' 2102 std::u16string_view rTargetURL // target dir without trailing '/' 2103 ) 2104 { 2105 bool bPopPossible(false); 2106 2107 // process files 2108 for (const auto& file : rFiles) 2109 { 2110 bPopPossible |= isPopPossible_file( 2111 rSourceURL, 2112 rTargetURL, 2113 file.first, 2114 file.second); 2115 } 2116 2117 // process dirs 2118 for (const auto& dir : rDirs) 2119 { 2120 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); 2121 OUString aNewTargetURL(OUString::Concat(rTargetURL) + "/" + dir); 2122 std::set< OUString > aNewDirs; 2123 std::set< std::pair< OUString, OUString > > aNewFiles; 2124 2125 DirectoryHelper::scanDirsAndFiles( 2126 aNewSourceURL, 2127 aNewDirs, 2128 aNewFiles); 2129 2130 if (!aNewDirs.empty() || !aNewFiles.empty()) 2131 { 2132 bPopPossible |= isPopPossible_files( 2133 aNewDirs, 2134 aNewFiles, 2135 aNewSourceURL, 2136 aNewTargetURL); 2137 } 2138 } 2139 2140 return bPopPossible; 2141 } 2142 2143 bool BackupFileHelper::isPopPossible_file( 2144 std::u16string_view rSourceURL, // source dir without trailing '/' 2145 std::u16string_view rTargetURL, // target dir without trailing '/' 2146 std::u16string_view rName, // filename 2147 std::u16string_view rExt // extension (or empty) 2148 ) 2149 { 2150 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); 2151 2152 if (DirectoryHelper::fileExists(aFileURL)) 2153 { 2154 const OUString aPackURL(createPackURL(rTargetURL, rName)); 2155 PackedFile aPackedFile(aPackURL); 2156 2157 return !aPackedFile.empty(); 2158 } 2159 2160 return false; 2161 } 2162 2163 /////////////////// file pop helpers /////////////////////// 2164 2165 bool BackupFileHelper::tryPop_files( 2166 const std::set< OUString >& rDirs, 2167 const std::set< std::pair< OUString, OUString > >& rFiles, 2168 std::u16string_view rSourceURL, // source dir without trailing '/' 2169 const OUString& rTargetURL // target dir without trailing '/' 2170 ) 2171 { 2172 bool bDidPop(false); 2173 2174 // process files 2175 for (const auto& file : rFiles) 2176 { 2177 bDidPop |= tryPop_file( 2178 rSourceURL, 2179 rTargetURL, 2180 file.first, 2181 file.second); 2182 } 2183 2184 // process dirs 2185 for (const auto& dir : rDirs) 2186 { 2187 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir); 2188 OUString aNewTargetURL(rTargetURL + "/" + dir); 2189 std::set< OUString > aNewDirs; 2190 std::set< std::pair< OUString, OUString > > aNewFiles; 2191 2192 DirectoryHelper::scanDirsAndFiles( 2193 aNewSourceURL, 2194 aNewDirs, 2195 aNewFiles); 2196 2197 if (!aNewDirs.empty() || !aNewFiles.empty()) 2198 { 2199 bDidPop |= tryPop_files( 2200 aNewDirs, 2201 aNewFiles, 2202 aNewSourceURL, 2203 aNewTargetURL); 2204 } 2205 } 2206 2207 if (bDidPop) 2208 { 2209 // try removal of evtl. empty directory 2210 osl::Directory::remove(rTargetURL); 2211 } 2212 2213 return bDidPop; 2214 } 2215 2216 bool BackupFileHelper::tryPop_file( 2217 std::u16string_view rSourceURL, // source dir without trailing '/' 2218 std::u16string_view rTargetURL, // target dir without trailing '/' 2219 std::u16string_view rName, // filename 2220 std::u16string_view rExt // extension (or empty) 2221 ) 2222 { 2223 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt)); 2224 2225 if (DirectoryHelper::fileExists(aFileURL)) 2226 { 2227 // try Pop for base file 2228 const OUString aPackURL(createPackURL(rTargetURL, rName)); 2229 PackedFile aPackedFile(aPackURL); 2230 2231 if (!aPackedFile.empty()) 2232 { 2233 oslFileHandle aHandle; 2234 OUString aTempURL; 2235 2236 // open target temp file - it exists until deleted 2237 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) 2238 { 2239 bool bRetval(aPackedFile.tryPop(aHandle)); 2240 2241 // close temp file (in all cases) - it exists until deleted 2242 osl_closeFile(aHandle); 2243 2244 if (bRetval) 2245 { 2246 // copy over existing file by first deleting original 2247 // and moving the temp file to old original 2248 osl::File::remove(aFileURL); 2249 osl::File::move(aTempURL, aFileURL); 2250 2251 // reduce to allowed number and flush 2252 aPackedFile.tryReduceToNumBackups(mnNumBackups); 2253 aPackedFile.flush(); 2254 } 2255 2256 // delete temp file (in all cases - it may be moved already) 2257 osl::File::remove(aTempURL); 2258 2259 return bRetval; 2260 } 2261 } 2262 } 2263 2264 return false; 2265 } 2266 2267 /////////////////// ExtensionInfo helpers /////////////////////// 2268 2269 bool BackupFileHelper::tryPush_extensionInfo( 2270 std::u16string_view rTargetURL // target dir without trailing '/' 2271 ) 2272 { 2273 ExtensionInfo aExtensionInfo; 2274 OUString aTempURL; 2275 bool bRetval(false); 2276 2277 // create current configuration and write to temp file - it exists until deleted 2278 if (aExtensionInfo.createTempFile(aTempURL)) 2279 { 2280 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); 2281 PackedFile aPackedFile(aPackURL); 2282 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL); 2283 2284 if (aPackedFile.tryPush(aBaseFile, mbCompress)) 2285 { 2286 // reduce to allowed number and flush 2287 aPackedFile.tryReduceToNumBackups(mnNumBackups); 2288 aPackedFile.flush(); 2289 bRetval = true; 2290 } 2291 } 2292 2293 // delete temp file (in all cases) 2294 osl::File::remove(aTempURL); 2295 return bRetval; 2296 } 2297 2298 bool BackupFileHelper::isPopPossible_extensionInfo( 2299 std::u16string_view rTargetURL // target dir without trailing '/' 2300 ) 2301 { 2302 // extensionInfo always exists internally, no test needed 2303 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); 2304 PackedFile aPackedFile(aPackURL); 2305 2306 return !aPackedFile.empty(); 2307 } 2308 2309 bool BackupFileHelper::tryPop_extensionInfo( 2310 std::u16string_view rTargetURL // target dir without trailing '/' 2311 ) 2312 { 2313 // extensionInfo always exists internally, no test needed 2314 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo")); 2315 PackedFile aPackedFile(aPackURL); 2316 2317 if (!aPackedFile.empty()) 2318 { 2319 oslFileHandle aHandle; 2320 OUString aTempURL; 2321 2322 // open target temp file - it exists until deleted 2323 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL)) 2324 { 2325 bool bRetval(aPackedFile.tryPop(aHandle)); 2326 2327 // close temp file (in all cases) - it exists until deleted 2328 osl_closeFile(aHandle); 2329 2330 if (bRetval) 2331 { 2332 // last config is in temp file, load it to ExtensionInfo 2333 ExtensionInfo aLoadedExtensionInfo; 2334 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL); 2335 2336 if (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read)) 2337 { 2338 if (aLoadedExtensionInfo.read_entries(aBaseFile)) 2339 { 2340 // get current extension info, but from XML config files 2341 ExtensionInfo aCurrentExtensionInfo; 2342 2343 aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL); 2344 2345 // now we have loaded last_working (aLoadedExtensionInfo) and 2346 // current (aCurrentExtensionInfo) ExtensionInfo and may react on 2347 // differences by de/activating these as needed 2348 const ExtensionInfoEntryVector& aUserEntries = aCurrentExtensionInfo.getExtensionInfoEntryVector(); 2349 const ExtensionInfoEntryVector& rLoadedVector = aLoadedExtensionInfo.getExtensionInfoEntryVector(); 2350 ExtensionInfoEntryVector aToBeDisabled; 2351 ExtensionInfoEntryVector aToBeEnabled; 2352 2353 for (const auto& rCurrentInfo : aUserEntries) 2354 { 2355 const ExtensionInfoEntry* pLoadedInfo = nullptr; 2356 2357 for (const auto& rLoadedInfo : rLoadedVector) 2358 { 2359 if (rCurrentInfo.isSameExtension(rLoadedInfo)) 2360 { 2361 pLoadedInfo = &rLoadedInfo; 2362 break; 2363 } 2364 } 2365 2366 if (nullptr != pLoadedInfo) 2367 { 2368 // loaded info contains information about the Extension rCurrentInfo 2369 const bool bCurrentEnabled(rCurrentInfo.isEnabled()); 2370 const bool bLoadedEnabled(pLoadedInfo->isEnabled()); 2371 2372 if (bCurrentEnabled && !bLoadedEnabled) 2373 { 2374 aToBeDisabled.push_back(rCurrentInfo); 2375 } 2376 else if (!bCurrentEnabled && bLoadedEnabled) 2377 { 2378 aToBeEnabled.push_back(rCurrentInfo); 2379 } 2380 } 2381 else 2382 { 2383 // There is no loaded info about the Extension rCurrentInfo. 2384 // It needs to be disabled 2385 if (rCurrentInfo.isEnabled()) 2386 { 2387 aToBeDisabled.push_back(rCurrentInfo); 2388 } 2389 } 2390 } 2391 2392 if (!aToBeDisabled.empty() || !aToBeEnabled.empty()) 2393 { 2394 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled); 2395 } 2396 2397 bRetval = true; 2398 } 2399 } 2400 2401 // reduce to allowed number and flush 2402 aPackedFile.tryReduceToNumBackups(mnNumBackups); 2403 aPackedFile.flush(); 2404 } 2405 2406 // delete temp file (in all cases - it may be moved already) 2407 osl::File::remove(aTempURL); 2408 2409 return bRetval; 2410 } 2411 } 2412 2413 return false; 2414 } 2415 2416 /////////////////// FileDirInfo helpers /////////////////////// 2417 2418 void BackupFileHelper::fillDirFileInfo() 2419 { 2420 if (!maDirs.empty() || !maFiles.empty()) 2421 { 2422 // already done 2423 return; 2424 } 2425 2426 // Information about the configuration and the role/purpose of directories in 2427 // the UserConfiguration is taken from: https://wiki.documentfoundation.org/UserProfile 2428 2429 // fill dir and file info list to work with dependent on work mode 2430 switch (mnMode) 2431 { 2432 case 0: 2433 { 2434 // simple mode: add just registrymodifications 2435 // (the orig file in maInitialBaseURL) 2436 maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt)); 2437 break; 2438 } 2439 case 1: 2440 { 2441 // defined mode: Add a selection of dirs containing User-Defined and thus 2442 // valuable configuration information. 2443 // This is clearly discussable in every single point and may be adapted/corrected 2444 // over time. Main focus is to secure User-Defined/adapted values 2445 2446 // add registrymodifications (the orig file in maInitialBaseURL) 2447 maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt)); 2448 2449 // User-defined substitution table (Tools/AutoCorrect) 2450 maDirs.insert("autocorr"); 2451 2452 // User-Defined AutoText (Edit/AutoText) 2453 maDirs.insert("autotext"); 2454 2455 // User-defined Macros 2456 maDirs.insert("basic"); 2457 2458 // User-adapted toolbars for modules 2459 maDirs.insert("config"); 2460 2461 // Initial and User-defined Databases 2462 maDirs.insert("database"); 2463 2464 // most part of registry files 2465 maDirs.insert("registry"); 2466 2467 // User-Defined Scripts 2468 maDirs.insert("Scripts"); 2469 2470 // Template files 2471 maDirs.insert("template"); 2472 2473 // Custom Dictionaries 2474 maDirs.insert("wordbook"); 2475 2476 // Questionable - where and how is Extension stuff held and how 2477 // does this interact with enabled/disabled states which are extra handled? 2478 // Keep out of business until deeper evaluated 2479 // 2480 // maDirs.insert("extensions"); 2481 // maDirs.insert("uno-packages"); 2482 break; 2483 } 2484 case 2: 2485 { 2486 // whole directory. To do so, scan directory and exclude some dirs 2487 // from which we know they do not need to be secured explicitly. This 2488 // should already include registrymodifications, too. 2489 DirectoryHelper::scanDirsAndFiles( 2490 maUserConfigWorkURL, 2491 maDirs, 2492 maFiles); 2493 2494 // should not exist, but for the case an error occurred and it got 2495 // copied somehow, avoid further recursive copying/saving 2496 maDirs.erase("SafeMode"); 2497 2498 // not really needed, can be abandoned 2499 maDirs.erase("psprint"); 2500 2501 // not really needed, can be abandoned 2502 maDirs.erase("store"); 2503 2504 // not really needed, can be abandoned 2505 maDirs.erase("temp"); 2506 2507 // exclude own backup dir to avoid recursion 2508 maDirs.erase("pack"); 2509 2510 break; 2511 } 2512 } 2513 } 2514 } 2515 2516 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2517
