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