Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / comphelper / source / misc / backupfilehelper.cxx
blobe9b173a47369707a73e1cd1fe8f27cf218cbcf6b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
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 <utility>
23 #include <vector>
24 #include <zlib.h>
26 #include <comphelper/processfactory.hxx>
27 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
28 #include <com/sun/star/ucb/CommandAbortedException.hpp>
29 #include <com/sun/star/ucb/CommandFailedException.hpp>
30 #include <com/sun/star/uno/Sequence.hxx>
31 #include <com/sun/star/uno/Reference.hxx>
32 #include <com/sun/star/deployment/DeploymentException.hpp>
33 #include <com/sun/star/deployment/ExtensionManager.hpp>
34 #include <com/sun/star/xml/dom/XDocumentBuilder.hpp>
35 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
36 #include <com/sun/star/xml/dom/XElement.hpp>
37 #include <com/sun/star/xml/dom/XNodeList.hpp>
38 #include <com/sun/star/xml/dom/XText.hpp>
39 #include <com/sun/star/xml/sax/XSAXSerializable.hpp>
40 #include <com/sun/star/xml/sax/Writer.hpp>
41 #include <com/sun/star/xml/sax/XWriter.hpp>
42 #include <com/sun/star/io/XStream.hpp>
43 #include <com/sun/star/io/TempFile.hpp>
44 #include <com/sun/star/io/XOutputStream.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <cppuhelper/exc_hlp.hxx>
48 using namespace comphelper;
49 using namespace css;
50 using namespace css::xml::dom;
52 const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE = 16384;
54 namespace
56 typedef std::shared_ptr< osl::File > FileSharedPtr;
58 sal_uInt32 createCrc32(FileSharedPtr const & rCandidate, sal_uInt32 nOffset)
60 sal_uInt32 nCrc32(0);
62 if (rCandidate && osl::File::E_None == rCandidate->open(osl_File_OpenFlag_Read))
64 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
65 sal_uInt64 nBytesTransfer(0);
66 sal_uInt64 nSize(0);
68 rCandidate->getSize(nSize);
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)))
75 while (nSize != 0)
77 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
79 if (osl::File::E_None == rCandidate->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) && nBytesTransfer == nToTransfer)
81 // add to crc and reduce size
82 nCrc32 = rtl_crc32(nCrc32, static_cast<void*>(aArray), static_cast<sal_uInt32>(nBytesTransfer));
83 nSize -= nToTransfer;
85 else
87 // error - reset to zero again
88 nSize = nCrc32 = 0;
93 rCandidate->close();
96 return nCrc32;
99 bool read_sal_uInt32(FileSharedPtr const & rFile, sal_uInt32& rTarget)
101 sal_uInt8 aArray[4];
102 sal_uInt64 nBaseRead(0);
104 // read rTarget
105 if (osl::File::E_None == rFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
107 rTarget = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
108 return true;
111 return false;
114 bool write_sal_uInt32(oslFileHandle& rHandle, sal_uInt32 nSource)
116 sal_uInt8 aArray[4];
117 sal_uInt64 nBaseWritten(0);
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);
125 return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten;
128 bool read_OString(FileSharedPtr const & rFile, OString& rTarget)
130 sal_uInt32 nLength(0);
132 if (!read_sal_uInt32(rFile, nLength))
134 return false;
137 sal_uInt64 nPos;
138 if (osl::File::E_None != rFile->getPos(nPos))
139 return false;
141 sal_uInt64 nSize;
142 if (osl::File::E_None != rFile->getSize(nSize))
143 return false;
145 const auto nRemainingSize = nSize - nPos;
146 if (nLength > nRemainingSize)
147 return false;
149 std::vector<char> aTarget(nLength);
150 sal_uInt64 nBaseRead(0);
152 // read rTarget
153 if (osl::File::E_None == rFile->read(static_cast<void*>(aTarget.data()), nLength, nBaseRead) && nLength == nBaseRead)
155 rTarget = OString(aTarget.data(), static_cast<sal_Int32>(nBaseRead));
156 return true;
159 return false;
162 bool write_OString(oslFileHandle& rHandle, const OString& rSource)
164 const sal_uInt32 nLength(rSource.getLength());
166 if (!write_sal_uInt32(rHandle, nLength))
168 return false;
171 sal_uInt64 nBaseWritten(0);
173 return osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(rSource.getStr()), nLength, &nBaseWritten) && nLength == nBaseWritten;
176 OUString createFileURL(
177 std::u16string_view rURL, std::u16string_view rName, std::u16string_view rExt)
179 OUString aRetval;
181 if (!rURL.empty() && !rName.empty())
183 aRetval = OUString::Concat(rURL) + "/" + rName;
185 if (!rExt.empty())
187 aRetval += OUString::Concat(".") + rExt;
191 return aRetval;
194 OUString createPackURL(std::u16string_view rURL, std::u16string_view rName)
196 OUString aRetval;
198 if (!rURL.empty() && !rName.empty())
200 aRetval = OUString::Concat(rURL) + "/" + rName + ".pack";
203 return aRetval;
207 namespace
209 enum PackageRepository { USER, SHARED, BUNDLED };
211 class ExtensionInfoEntry
213 private:
214 OString maName; // extension name
215 PackageRepository maRepository; // user|shared|bundled
216 bool mbEnabled; // state
218 public:
219 ExtensionInfoEntry()
220 : maRepository(USER),
221 mbEnabled(false)
225 ExtensionInfoEntry(OString aName, bool bEnabled)
226 : maName(std::move(aName)),
227 maRepository(USER),
228 mbEnabled(bEnabled)
232 ExtensionInfoEntry(const uno::Reference< deployment::XPackage >& rxPackage)
233 : maName(OUStringToOString(rxPackage->getName(), RTL_TEXTENCODING_ASCII_US)),
234 maRepository(USER),
235 mbEnabled(false)
237 // check maRepository
238 const OString aRepName(OUStringToOString(rxPackage->getRepositoryName(), RTL_TEXTENCODING_ASCII_US));
240 if (aRepName == "shared")
242 maRepository = SHARED;
244 else if (aRepName == "bundled")
246 maRepository = BUNDLED;
249 // check mbEnabled
250 const beans::Optional< beans::Ambiguous< sal_Bool > > option(
251 rxPackage->isRegistered(uno::Reference< task::XAbortChannel >(),
252 uno::Reference< ucb::XCommandEnvironment >()));
254 if (option.IsPresent)
256 ::beans::Ambiguous< sal_Bool > const& reg = option.Value;
258 if (!reg.IsAmbiguous)
260 mbEnabled = reg.Value;
265 bool isSameExtension(const ExtensionInfoEntry& rComp) const
267 return (maRepository == rComp.maRepository && maName == rComp.maName);
270 bool operator<(const ExtensionInfoEntry& rComp) const
272 if (maRepository == rComp.maRepository)
274 if (maName == rComp.maName)
276 return mbEnabled < rComp.mbEnabled;
278 else
280 return 0 > maName.compareTo(rComp.maName);
283 else
285 return maRepository < rComp.maRepository;
289 bool read_entry(FileSharedPtr const & rFile)
291 // read maName
292 if (!read_OString(rFile, maName))
294 return false;
297 // read maRepository
298 sal_uInt32 nState(0);
300 if (read_sal_uInt32(rFile, nState))
302 maRepository = static_cast< PackageRepository >(nState);
304 else
306 return false;
309 // read mbEnabled
310 if (read_sal_uInt32(rFile, nState))
312 mbEnabled = static_cast< bool >(nState);
314 else
316 return false;
319 return true;
322 bool write_entry(oslFileHandle& rHandle) const
324 // write maName;
325 if (!write_OString(rHandle, maName))
327 return false;
330 // write maRepository
331 sal_uInt32 nState(maRepository);
333 if (!write_sal_uInt32(rHandle, nState))
335 return false;
338 // write mbEnabled
339 nState = static_cast< sal_uInt32 >(mbEnabled);
341 return write_sal_uInt32(rHandle, nState);
344 const OString& getName() const
346 return maName;
349 bool isEnabled() const
351 return mbEnabled;
355 typedef std::vector< ExtensionInfoEntry > ExtensionInfoEntryVector;
357 constexpr OUStringLiteral gaRegPath { u"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml" };
359 class ExtensionInfo
361 private:
362 ExtensionInfoEntryVector maEntries;
364 public:
365 ExtensionInfo()
369 const ExtensionInfoEntryVector& getExtensionInfoEntryVector() const
371 return maEntries;
374 void reset()
376 // clear all data
377 maEntries.clear();
380 void createUsingXExtensionManager()
382 // clear all data
383 reset();
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);
392 xAllPackages = m_xExtensionManager->getAllExtensions(uno::Reference< task::XAbortChannel >(),
393 uno::Reference< ucb::XCommandEnvironment >());
395 catch (const deployment::DeploymentException &)
397 return;
399 catch (const ucb::CommandFailedException &)
401 return;
403 catch (const ucb::CommandAbortedException &)
405 return;
407 catch (const lang::IllegalArgumentException & e)
409 css::uno::Any anyEx = cppu::getCaughtException();
410 throw css::lang::WrappedTargetRuntimeException( e.Message,
411 e.Context, anyEx );
414 for (const uno::Sequence< uno::Reference< deployment::XPackage > > & xPackageList : std::as_const(xAllPackages))
416 for (const uno::Reference< deployment::XPackage > & xPackage : xPackageList)
418 if (xPackage.is())
420 maEntries.emplace_back(xPackage);
425 if (!maEntries.empty())
427 // sort the list
428 std::sort(maEntries.begin(), maEntries.end());
432 private:
433 void visitNodesXMLRead(const uno::Reference< xml::dom::XElement >& rElement)
435 if (!rElement.is())
436 return;
438 const OUString aTagName(rElement->getTagName());
440 if (aTagName == "extension")
442 OUString aAttrUrl(rElement->getAttribute("url"));
443 const OUString aAttrRevoked(rElement->getAttribute("revoked"));
445 if (!aAttrUrl.isEmpty())
447 const sal_Int32 nIndex(aAttrUrl.lastIndexOf('/'));
449 if (nIndex > 0 && aAttrUrl.getLength() > nIndex + 1)
451 aAttrUrl = aAttrUrl.copy(nIndex + 1);
454 const bool bEnabled(aAttrRevoked.isEmpty() || !aAttrRevoked.toBoolean());
455 maEntries.emplace_back(
456 OUStringToOString(aAttrUrl, RTL_TEXTENCODING_ASCII_US),
457 bEnabled);
460 else
462 uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes();
464 if (aList.is())
466 const sal_Int32 nLength(aList->getLength());
468 for (sal_Int32 a(0); a < nLength; a++)
470 const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY);
472 if (aChild.is())
474 visitNodesXMLRead(aChild);
481 public:
482 void createUserExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
484 const OUString aPath(
485 OUString::Concat(rUserConfigWorkURL) + "/uno_packages/cache" + gaRegPath);
486 createExtensionRegistryEntriesFromXML(aPath);
489 void createSharedExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
491 const OUString aPath(
492 OUString::Concat(rUserConfigWorkURL) + "/extensions/shared" + gaRegPath);
493 createExtensionRegistryEntriesFromXML(aPath);
496 void createBundledExtensionRegistryEntriesFromXML(std::u16string_view rUserConfigWorkURL)
498 const OUString aPath(
499 OUString::Concat(rUserConfigWorkURL) + "/extensions/bundled" + gaRegPath);
500 createExtensionRegistryEntriesFromXML(aPath);
504 void createExtensionRegistryEntriesFromXML(const OUString& aPath)
506 if (DirectoryHelper::fileExists(aPath))
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);
512 if (aDocument.is())
514 visitNodesXMLRead(aDocument->getDocumentElement());
518 if (!maEntries.empty())
520 // sort the list
521 std::sort(maEntries.begin(), maEntries.end());
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)
532 bool bChanged(false);
534 if (rElement.is())
536 const OUString aTagName(rElement->getTagName());
538 if (aTagName == rTagToSearch)
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());
544 if (!aAttrUrl.isEmpty())
546 for (const auto& enable : rToBeEnabled)
548 if (-1 != aAttrUrl.indexOf(enable.getName()))
550 if (!bEnabled)
552 // needs to be enabled
553 rElement->removeAttribute("revoked");
554 bChanged = true;
559 for (const auto& disable : rToBeDisabled)
561 if (-1 != aAttrUrl.indexOf(disable.getName()))
563 if (bEnabled)
565 // needs to be disabled
566 rElement->setAttribute("revoked", "true");
567 bChanged = true;
573 else
575 uno::Reference< xml::dom::XNodeList > aList = rElement->getChildNodes();
577 if (aList.is())
579 const sal_Int32 nLength(aList->getLength());
581 for (sal_Int32 a(0); a < nLength; a++)
583 const uno::Reference< xml::dom::XElement > aChild(aList->item(a), uno::UNO_QUERY);
585 if (aChild.is())
587 bChanged |= visitNodesXMLChange(
588 rTagToSearch,
589 aChild,
590 rToBeEnabled,
591 rToBeDisabled);
598 return bChanged;
601 static void visitNodesXMLChangeOneCase(
602 const OUString& rUnoPackagReg,
603 const OUString& rTagToSearch,
604 const ExtensionInfoEntryVector& rToBeEnabled,
605 const ExtensionInfoEntryVector& rToBeDisabled)
607 if (!DirectoryHelper::fileExists(rUnoPackagReg))
608 return;
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);
614 if (!aDocument.is())
615 return;
617 if (!visitNodesXMLChange(
618 rTagToSearch,
619 aDocument->getDocumentElement(),
620 rToBeEnabled,
621 rToBeDisabled))
622 return;
624 // did change - write back
625 uno::Reference< xml::sax::XSAXSerializable > xSerializer(aDocument, uno::UNO_QUERY);
627 if (!xSerializer.is())
628 return;
630 // create a SAXWriter
631 uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext);
632 uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext);
633 uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream();
635 // set output stream and do the serialization
636 xSaxWriter->setOutputStream(xOutStrm);
637 xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >());
639 // get URL from temp file
640 OUString aTempURL = xTempFile->getUri();
642 // copy back file
643 if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL))
644 return;
646 if (DirectoryHelper::fileExists(rUnoPackagReg))
648 osl::File::remove(rUnoPackagReg);
651 #if OSL_DEBUG_LEVEL > 1
652 SAL_WARN_IF(osl::FileBase::E_None != osl::File::move(aTempURL, rUnoPackagReg), "comphelper.backupfilehelper", "could not copy back modified Extension configuration file");
653 #else
654 osl::File::move(aTempURL, rUnoPackagReg);
655 #endif
658 public:
659 static void changeEnableDisableStateInXML(
660 std::u16string_view rUserConfigWorkURL,
661 const ExtensionInfoEntryVector& rToBeEnabled,
662 const ExtensionInfoEntryVector& rToBeDisabled)
664 static const OUStringLiteral aRegPathFront(u"/uno_packages/cache/registry/com.sun.star.comp.deployment.");
665 static const OUStringLiteral aRegPathBack(u".PackageRegistryBackend/backenddb.xml");
666 // first appearance to check
668 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "bundle" + aRegPathBack);
670 visitNodesXMLChangeOneCase(
671 aUnoPackagReg,
672 "extension",
673 rToBeEnabled,
674 rToBeDisabled);
677 // second appearance to check
679 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "configuration" + aRegPathBack);
681 visitNodesXMLChangeOneCase(
682 aUnoPackagReg,
683 "configuration",
684 rToBeEnabled,
685 rToBeDisabled);
688 // third appearance to check
690 const OUString aUnoPackagReg(OUString::Concat(rUserConfigWorkURL) + aRegPathFront + "script" + aRegPathBack);
692 visitNodesXMLChangeOneCase(
693 aUnoPackagReg,
694 "script",
695 rToBeEnabled,
696 rToBeDisabled);
700 bool read_entries(FileSharedPtr const & rFile)
702 // read NumExtensionEntries
703 sal_uInt32 nExtEntries(0);
705 if (!read_sal_uInt32(rFile, nExtEntries))
707 return false;
710 // coverity#1373663 Untrusted loop bound, check file size
711 // isn't utterly broken
712 sal_uInt64 nFileSize(0);
713 rFile->getSize(nFileSize);
714 if (nFileSize < nExtEntries)
715 return false;
717 for (sal_uInt32 a(0); a < nExtEntries; a++)
719 ExtensionInfoEntry aNewEntry;
721 if (aNewEntry.read_entry(rFile))
723 maEntries.push_back(aNewEntry);
725 else
727 return false;
731 return true;
734 bool write_entries(oslFileHandle& rHandle) const
736 const sal_uInt32 nExtEntries(maEntries.size());
738 if (!write_sal_uInt32(rHandle, nExtEntries))
740 return false;
743 for (const auto& a : maEntries)
745 if (!a.write_entry(rHandle))
747 return false;
751 return true;
754 bool createTempFile(OUString& rTempFileName)
756 oslFileHandle aHandle;
757 bool bRetval(false);
759 // create current configuration
760 if (maEntries.empty())
762 createUsingXExtensionManager();
765 // open target temp file and write current configuration to it - it exists until deleted
766 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &rTempFileName))
768 bRetval = write_entries(aHandle);
770 // close temp file - it exists until deleted
771 osl_closeFile(aHandle);
774 return bRetval;
777 bool areThereEnabledExtensions() const
779 for (const auto& a : maEntries)
781 if (a.isEnabled())
783 return true;
787 return false;
792 namespace
794 class PackedFileEntry
796 private:
797 sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file
798 sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not)
799 sal_uInt32 mnOffset; // offset in File (zero identifies new file)
800 sal_uInt32 mnCrc32; // checksum
801 FileSharedPtr maFile; // file where to find the data (at offset)
802 bool const mbDoCompress; // flag if this file is scheduled to be compressed when written
804 bool copy_content_straight(oslFileHandle& rTargetHandle)
806 if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
807 return false;
809 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
810 sal_uInt64 nBytesTransfer(0);
811 sal_uInt64 nSize(getPackFileSize());
813 // set offset in source file - when this is zero, a new file is to be added
814 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
816 while (nSize != 0)
818 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
820 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
822 break;
825 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aArray), nToTransfer, &nBytesTransfer) || nBytesTransfer != nToTransfer)
827 break;
830 nSize -= nToTransfer;
834 maFile->close();
835 return (0 == nSize);
838 bool copy_content_compress(oslFileHandle& rTargetHandle)
840 if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
841 return false;
843 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
844 sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE];
845 sal_uInt64 nBytesTransfer(0);
846 sal_uInt64 nSize(getPackFileSize());
847 z_stream zstream;
848 memset(&zstream, 0, sizeof(zstream));
850 if (Z_OK == deflateInit(&zstream, Z_BEST_COMPRESSION))
852 // set offset in source file - when this is zero, a new file is to be added
853 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
855 bool bOkay(true);
857 while (bOkay && nSize != 0)
859 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
861 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
863 break;
866 zstream.avail_in = nToTransfer;
867 zstream.next_in = reinterpret_cast<unsigned char*>(aArray);
869 do {
870 zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE;
871 zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer);
872 #if !defined Z_PREFIX
873 const sal_Int64 nRetval(deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH));
874 #else
875 const sal_Int64 nRetval(z_deflate(&zstream, nSize == nToTransfer ? Z_FINISH : Z_NO_FLUSH));
876 #endif
877 if (Z_STREAM_ERROR == nRetval)
879 bOkay = false;
881 else
883 const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out);
885 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable)
887 bOkay = false;
890 } while (bOkay && 0 == zstream.avail_out);
892 if (!bOkay)
894 break;
897 nSize -= nToTransfer;
900 #if !defined Z_PREFIX
901 deflateEnd(&zstream);
902 #else
903 z_deflateEnd(&zstream);
904 #endif
908 maFile->close();
910 // get compressed size and add to entry
911 if (mnFullFileSize == mnPackFileSize && mnFullFileSize == zstream.total_in)
913 mnPackFileSize = zstream.total_out;
916 return (0 == nSize);
919 bool copy_content_uncompress(oslFileHandle& rTargetHandle)
921 if (!maFile || osl::File::E_None != maFile->open(osl_File_OpenFlag_Read))
922 return false;
924 sal_uInt8 aArray[BACKUP_FILE_HELPER_BLOCK_SIZE];
925 sal_uInt8 aBuffer[BACKUP_FILE_HELPER_BLOCK_SIZE];
926 sal_uInt64 nBytesTransfer(0);
927 sal_uInt64 nSize(getPackFileSize());
928 z_stream zstream;
929 memset(&zstream, 0, sizeof(zstream));
931 if (Z_OK == inflateInit(&zstream))
933 // set offset in source file - when this is zero, a new file is to be added
934 if (osl::File::E_None == maFile->setPos(osl_Pos_Absolut, sal_Int64(getOffset())))
936 bool bOkay(true);
938 while (bOkay && nSize != 0)
940 const sal_uInt64 nToTransfer(std::min(nSize, sal_uInt64(BACKUP_FILE_HELPER_BLOCK_SIZE)));
942 if (osl::File::E_None != maFile->read(static_cast<void*>(aArray), nToTransfer, nBytesTransfer) || nBytesTransfer != nToTransfer)
944 break;
947 zstream.avail_in = nToTransfer;
948 zstream.next_in = reinterpret_cast<unsigned char*>(aArray);
950 do {
951 zstream.avail_out = BACKUP_FILE_HELPER_BLOCK_SIZE;
952 zstream.next_out = reinterpret_cast<unsigned char*>(aBuffer);
953 #if !defined Z_PREFIX
954 const sal_Int64 nRetval(inflate(&zstream, Z_NO_FLUSH));
955 #else
956 const sal_Int64 nRetval(z_inflate(&zstream, Z_NO_FLUSH));
957 #endif
958 if (Z_STREAM_ERROR == nRetval)
960 bOkay = false;
962 else
964 const sal_uInt64 nAvailable(BACKUP_FILE_HELPER_BLOCK_SIZE - zstream.avail_out);
966 if (osl_File_E_None != osl_writeFile(rTargetHandle, static_cast<const void*>(aBuffer), nAvailable, &nBytesTransfer) || nBytesTransfer != nAvailable)
968 bOkay = false;
971 } while (bOkay && 0 == zstream.avail_out);
973 if (!bOkay)
975 break;
978 nSize -= nToTransfer;
981 #if !defined Z_PREFIX
982 deflateEnd(&zstream);
983 #else
984 z_deflateEnd(&zstream);
985 #endif
989 maFile->close();
990 return (0 == nSize);
994 public:
995 // create new, uncompressed entry
996 PackedFileEntry(
997 sal_uInt32 nFullFileSize,
998 sal_uInt32 nCrc32,
999 FileSharedPtr xFile,
1000 bool bDoCompress)
1001 : mnFullFileSize(nFullFileSize),
1002 mnPackFileSize(nFullFileSize),
1003 mnOffset(0),
1004 mnCrc32(nCrc32),
1005 maFile(std::move(xFile)),
1006 mbDoCompress(bDoCompress)
1010 // create entry to be loaded as header (read_header)
1011 PackedFileEntry()
1012 : mnFullFileSize(0),
1013 mnPackFileSize(0),
1014 mnOffset(0),
1015 mnCrc32(0),
1016 mbDoCompress(false)
1020 sal_uInt32 getFullFileSize() const
1022 return mnFullFileSize;
1025 sal_uInt32 getPackFileSize() const
1027 return mnPackFileSize;
1030 sal_uInt32 getOffset() const
1032 return mnOffset;
1035 void setOffset(sal_uInt32 nOffset)
1037 mnOffset = nOffset;
1040 static sal_uInt32 getEntrySize()
1042 return 12;
1045 sal_uInt32 getCrc32() const
1047 return mnCrc32;
1050 bool read_header(FileSharedPtr const & rFile)
1052 if (!rFile)
1054 return false;
1057 maFile = rFile;
1059 // read and compute full file size
1060 if (!read_sal_uInt32(rFile, mnFullFileSize))
1062 return false;
1065 // read and compute entry crc32
1066 if (!read_sal_uInt32(rFile, mnCrc32))
1068 return false;
1071 // read and compute packed size
1072 if (!read_sal_uInt32(rFile, mnPackFileSize))
1074 return false;
1077 return true;
1080 bool write_header(oslFileHandle& rHandle) const
1082 // write full file size
1083 if (!write_sal_uInt32(rHandle, mnFullFileSize))
1085 return false;
1088 // write crc32
1089 if (!write_sal_uInt32(rHandle, mnCrc32))
1091 return false;
1094 // write packed file size
1095 if (!write_sal_uInt32(rHandle, mnPackFileSize))
1097 return false;
1100 return true;
1103 bool copy_content(oslFileHandle& rTargetHandle, bool bUncompress)
1105 if (bUncompress)
1107 if (getFullFileSize() == getPackFileSize())
1109 // not compressed, just copy
1110 return copy_content_straight(rTargetHandle);
1112 else
1114 // compressed, need to uncompress on copy
1115 return copy_content_uncompress(rTargetHandle);
1118 else if (0 == getOffset())
1120 if (mbDoCompress)
1122 // compressed wanted, need to compress on copy
1123 return copy_content_compress(rTargetHandle);
1125 else
1127 // not compressed, straight copy
1128 return copy_content_straight(rTargetHandle);
1131 else
1133 return copy_content_straight(rTargetHandle);
1139 namespace
1141 class PackedFile
1143 private:
1144 const OUString maURL;
1145 std::deque< PackedFileEntry >
1146 maPackedFileEntryVector;
1147 bool mbChanged;
1149 public:
1150 PackedFile(const OUString& rURL)
1151 : maURL(rURL),
1152 mbChanged(false)
1154 FileSharedPtr aSourceFile = std::make_shared<osl::File>(rURL);
1156 if (osl::File::E_None == aSourceFile->open(osl_File_OpenFlag_Read))
1158 sal_uInt64 nBaseLen(0);
1159 aSourceFile->getSize(nBaseLen);
1161 // we need at least File_ID and num entries -> 8byte
1162 if (8 < nBaseLen)
1164 sal_uInt8 aArray[4];
1165 sal_uInt64 nBaseRead(0);
1167 // read and check File_ID
1168 if (osl::File::E_None == aSourceFile->read(static_cast< void* >(aArray), 4, nBaseRead) && 4 == nBaseRead)
1170 if ('P' == aArray[0] && 'A' == aArray[1] && 'C' == aArray[2] && 'K' == aArray[3])
1172 // read and compute num entries in this file
1173 if (osl::File::E_None == aSourceFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
1175 sal_uInt32 nEntries((sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]));
1177 // if there are entries (and less than max), read them
1178 if (nEntries >= 1 && nEntries <= 10)
1180 for (sal_uInt32 a(0); a < nEntries; a++)
1182 // create new entry, read header (size, crc and PackedSize),
1183 // set offset and source file
1184 PackedFileEntry aEntry;
1186 if (aEntry.read_header(aSourceFile))
1188 // add to local data
1189 maPackedFileEntryVector.push_back(aEntry);
1191 else
1193 // error
1194 nEntries = 0;
1198 if (0 == nEntries)
1200 // on read error clear local data
1201 maPackedFileEntryVector.clear();
1203 else
1205 // calculate and set offsets to file binary content
1206 sal_uInt32 nHeaderSize(8);
1208 nHeaderSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
1210 sal_uInt32 nOffset(nHeaderSize);
1212 for (auto& b : maPackedFileEntryVector)
1214 b.setOffset(nOffset);
1215 nOffset += b.getPackFileSize();
1224 aSourceFile->close();
1227 if (maPackedFileEntryVector.empty())
1229 // on error or no data get rid of pack file
1230 osl::File::remove(maURL);
1234 void flush()
1236 bool bRetval(true);
1238 if (maPackedFileEntryVector.empty())
1240 // get rid of (now?) empty pack file
1241 osl::File::remove(maURL);
1243 else if (mbChanged)
1245 // need to create a new pack file, do this in a temp file to which data
1246 // will be copied from local file (so keep it here until this is done)
1247 oslFileHandle aHandle = nullptr;
1248 OUString aTempURL;
1250 // open target temp file - it exists until deleted
1251 if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
1253 sal_uInt8 aArray[4];
1254 sal_uInt64 nBaseWritten(0);
1256 aArray[0] = 'P';
1257 aArray[1] = 'A';
1258 aArray[2] = 'C';
1259 aArray[3] = 'K';
1261 // write File_ID
1262 if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten)
1264 const sal_uInt32 nSize(maPackedFileEntryVector.size());
1266 // write number of entries
1267 if (write_sal_uInt32(aHandle, nSize))
1269 // write placeholder for headers. Due to the fact that
1270 // PackFileSize for newly added files gets set during
1271 // writing the content entry, write headers after content
1272 // is written. To do so, write placeholders here
1273 sal_uInt32 nWriteSize(0);
1275 nWriteSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
1277 aArray[0] = aArray[1] = aArray[2] = aArray[3] = 0;
1279 for (sal_uInt32 a(0); bRetval && a < nWriteSize; a++)
1281 if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 1, &nBaseWritten) || 1 != nBaseWritten)
1283 bRetval = false;
1287 if (bRetval)
1289 // write contents - this may adapt PackFileSize for new
1290 // files
1291 for (auto& candidate : maPackedFileEntryVector)
1293 if (!candidate.copy_content(aHandle, false))
1295 bRetval = false;
1296 break;
1301 if (bRetval)
1303 // seek back to header start (at position 8)
1304 if (osl_File_E_None != osl_setFilePos(aHandle, osl_Pos_Absolut, sal_Int64(8)))
1306 bRetval = false;
1310 if (bRetval)
1312 // write headers
1313 for (const auto& candidate : maPackedFileEntryVector)
1315 if (!candidate.write_header(aHandle))
1317 // error
1318 bRetval = false;
1319 break;
1327 // close temp file (in all cases) - it exists until deleted
1328 osl_closeFile(aHandle);
1330 if (bRetval)
1332 // copy over existing file by first deleting original
1333 // and moving the temp file to old original
1334 osl::File::remove(maURL);
1335 osl::File::move(aTempURL, maURL);
1338 // delete temp file (in all cases - it may be moved already)
1339 osl::File::remove(aTempURL);
1343 bool tryPush(FileSharedPtr const & rFileCandidate, bool bCompress)
1345 sal_uInt64 nFileSize(0);
1347 if (rFileCandidate && osl::File::E_None == rFileCandidate->open(osl_File_OpenFlag_Read))
1349 rFileCandidate->getSize(nFileSize);
1350 rFileCandidate->close();
1353 if (0 == nFileSize)
1355 // empty file offered
1356 return false;
1359 bool bNeedToAdd(false);
1360 sal_uInt32 nCrc32(0);
1362 if (maPackedFileEntryVector.empty())
1364 // no backup yet, add as 1st backup
1365 bNeedToAdd = true;
1367 else
1369 // already backups there, check if different from last entry
1370 const PackedFileEntry& aLastEntry = maPackedFileEntryVector.back();
1372 // check if file is different
1373 if (aLastEntry.getFullFileSize() != static_cast<sal_uInt32>(nFileSize))
1375 // different size, different file
1376 bNeedToAdd = true;
1378 else
1380 // same size, check crc32
1381 nCrc32 = createCrc32(rFileCandidate, 0);
1383 if (nCrc32 != aLastEntry.getCrc32())
1385 // different crc, different file
1386 bNeedToAdd = true;
1391 if (bNeedToAdd)
1393 // create crc32 if not yet done
1394 if (0 == nCrc32)
1396 nCrc32 = createCrc32(rFileCandidate, 0);
1399 // create a file entry for a new file. Offset is set automatically
1400 // to 0 to mark the entry as new file entry
1401 maPackedFileEntryVector.emplace_back(
1402 static_cast< sal_uInt32 >(nFileSize),
1403 nCrc32,
1404 rFileCandidate,
1405 bCompress);
1407 mbChanged = true;
1410 return bNeedToAdd;
1413 bool tryPop(oslFileHandle& rHandle)
1415 if (maPackedFileEntryVector.empty())
1416 return false;
1418 // already backups there, check if different from last entry
1419 PackedFileEntry& aLastEntry = maPackedFileEntryVector.back();
1421 // here the uncompress flag has to be determined, true
1422 // means to add the file compressed, false means to add it
1423 // uncompressed
1424 bool bRetval = aLastEntry.copy_content(rHandle, true);
1426 if (bRetval)
1428 maPackedFileEntryVector.pop_back();
1429 mbChanged = true;
1432 return bRetval;
1435 void tryReduceToNumBackups(sal_uInt16 nNumBackups)
1437 while (maPackedFileEntryVector.size() > nNumBackups)
1439 maPackedFileEntryVector.pop_front();
1440 mbChanged = true;
1444 bool empty() const
1446 return maPackedFileEntryVector.empty();
1451 namespace comphelper
1453 sal_uInt16 BackupFileHelper::mnMaxAllowedBackups = 10;
1454 bool BackupFileHelper::mbExitWasCalled = false;
1455 bool BackupFileHelper::mbSafeModeDirExists = false;
1456 OUString BackupFileHelper::maInitialBaseURL;
1457 OUString BackupFileHelper::maUserConfigBaseURL;
1458 OUString BackupFileHelper::maUserConfigWorkURL;
1459 OUString BackupFileHelper::maRegModName;
1460 OUString BackupFileHelper::maExt;
1462 const OUString& BackupFileHelper::getInitialBaseURL()
1464 if (maInitialBaseURL.isEmpty())
1466 // try to access user layer configuration file URL, the one that
1467 // points to registrymodifications.xcu
1468 OUString conf("${CONFIGURATION_LAYERS}");
1469 rtl::Bootstrap::expandMacros(conf);
1470 static const OUStringLiteral aTokenUser(u"user:");
1471 sal_Int32 nStart(conf.indexOf(aTokenUser));
1473 if (-1 != nStart)
1475 nStart += aTokenUser.getLength();
1476 sal_Int32 nEnd(conf.indexOf(' ', nStart));
1478 if (-1 == nEnd)
1480 nEnd = conf.getLength();
1483 maInitialBaseURL = conf.copy(nStart, nEnd - nStart);
1484 (void)maInitialBaseURL.startsWith("!", &maInitialBaseURL);
1487 if (!maInitialBaseURL.isEmpty())
1489 // split URL at extension and at last path separator
1490 maUserConfigBaseURL = DirectoryHelper::splitAtLastToken(
1491 DirectoryHelper::splitAtLastToken(maInitialBaseURL, '.', maExt), '/',
1492 maRegModName);
1495 if (!maUserConfigBaseURL.isEmpty())
1497 // check if SafeModeDir exists
1498 mbSafeModeDirExists = DirectoryHelper::dirExists(maUserConfigBaseURL + "/" + getSafeModeName());
1501 maUserConfigWorkURL = maUserConfigBaseURL;
1503 if (mbSafeModeDirExists)
1505 // adapt work URL to do all repair op's in the correct directory
1506 maUserConfigWorkURL += "/" + getSafeModeName();
1510 return maInitialBaseURL;
1513 const OUString& BackupFileHelper::getSafeModeName()
1515 static const OUString aSafeMode("SafeMode");
1517 return aSafeMode;
1520 BackupFileHelper::BackupFileHelper()
1521 : mnNumBackups(2),
1522 mnMode(1),
1523 mbActive(false),
1524 mbExtensions(true),
1525 mbCompress(true)
1527 OUString sTokenOut;
1529 // read configuration item 'SecureUserConfig' -> bool on/off
1530 if (rtl::Bootstrap::get("SecureUserConfig", sTokenOut))
1532 mbActive = sTokenOut.toBoolean();
1535 if (mbActive)
1537 // ensure existence
1538 getInitialBaseURL();
1540 // if not found, we are out of business (maExt may be empty)
1541 mbActive = !maInitialBaseURL.isEmpty() && !maUserConfigBaseURL.isEmpty() && !maRegModName.isEmpty();
1544 if (mbActive && rtl::Bootstrap::get("SecureUserConfigNumCopies", sTokenOut))
1546 const sal_uInt16 nConfigNumCopies(static_cast<sal_uInt16>(sTokenOut.toUInt32()));
1548 // limit to range [1..mnMaxAllowedBackups]
1549 mnNumBackups = std::clamp(mnNumBackups, nConfigNumCopies, mnMaxAllowedBackups);
1552 if (mbActive && rtl::Bootstrap::get("SecureUserConfigMode", sTokenOut))
1554 const sal_uInt16 nMode(static_cast<sal_uInt16>(sTokenOut.toUInt32()));
1556 // limit to range [0..2]
1557 mnMode = std::min(nMode, sal_uInt16(2));
1560 if (mbActive && rtl::Bootstrap::get("SecureUserConfigExtensions", sTokenOut))
1562 mbExtensions = sTokenOut.toBoolean();
1565 if (mbActive && rtl::Bootstrap::get("SecureUserConfigCompress", sTokenOut))
1567 mbCompress = sTokenOut.toBoolean();
1571 void BackupFileHelper::setExitWasCalled()
1573 mbExitWasCalled = true;
1576 bool BackupFileHelper::getExitWasCalled()
1578 return mbExitWasCalled;
1581 void BackupFileHelper::reactOnSafeMode(bool bSafeMode)
1583 // ensure existence of needed paths
1584 getInitialBaseURL();
1586 if (maUserConfigBaseURL.isEmpty())
1587 return;
1589 if (bSafeMode)
1591 if (!mbSafeModeDirExists)
1593 std::set< OUString > aExcludeList;
1595 // do not move SafeMode directory itself
1596 aExcludeList.insert(getSafeModeName());
1598 // init SafeMode by creating the 'SafeMode' directory and moving
1599 // all stuff there. All repairs will happen there. Both Dirs have to exist.
1600 // extend maUserConfigWorkURL as needed
1601 maUserConfigWorkURL = maUserConfigBaseURL + "/" + getSafeModeName();
1603 osl::Directory::createPath(maUserConfigWorkURL);
1604 DirectoryHelper::moveDirContent(maUserConfigBaseURL, maUserConfigWorkURL, aExcludeList);
1606 // switch local flag, maUserConfigWorkURL is already reset
1607 mbSafeModeDirExists = true;
1610 else
1612 if (mbSafeModeDirExists)
1614 // SafeMode has ended, return to normal mode by moving all content
1615 // from 'SafeMode' directory back to UserDirectory and deleting it.
1616 // Both Dirs have to exist
1617 std::set< OUString > aExcludeList;
1619 DirectoryHelper::moveDirContent(maUserConfigWorkURL, maUserConfigBaseURL, aExcludeList);
1620 osl::Directory::remove(maUserConfigWorkURL);
1622 // switch local flag and reset maUserConfigWorkURL
1623 mbSafeModeDirExists = false;
1624 maUserConfigWorkURL = maUserConfigBaseURL;
1629 void BackupFileHelper::tryPush()
1631 // no push when SafeModeDir exists, it may be Office's exit after SafeMode
1632 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
1633 // done yet (is done at next startup)
1634 if (!mbActive || mbSafeModeDirExists)
1635 return;
1637 const OUString aPackURL(getPackURL());
1639 // ensure dir and file vectors
1640 fillDirFileInfo();
1642 // process all files in question recursively
1643 if (!maDirs.empty() || !maFiles.empty())
1645 tryPush_Files(
1646 maDirs,
1647 maFiles,
1648 maUserConfigWorkURL,
1649 aPackURL);
1653 void BackupFileHelper::tryPushExtensionInfo()
1655 // no push when SafeModeDir exists, it may be Office's exit after SafeMode
1656 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
1657 // done yet (is done at next startup)
1658 if (mbActive && mbExtensions && !mbSafeModeDirExists)
1660 const OUString aPackURL(getPackURL());
1662 tryPush_extensionInfo(aPackURL);
1666 bool BackupFileHelper::isPopPossible()
1668 bool bPopPossible(false);
1670 if (mbActive)
1672 const OUString aPackURL(getPackURL());
1674 // ensure dir and file vectors
1675 fillDirFileInfo();
1677 // process all files in question recursively
1678 if (!maDirs.empty() || !maFiles.empty())
1680 bPopPossible = isPopPossible_files(
1681 maDirs,
1682 maFiles,
1683 maUserConfigWorkURL,
1684 aPackURL);
1688 return bPopPossible;
1691 void BackupFileHelper::tryPop()
1693 if (!mbActive)
1694 return;
1696 bool bDidPop(false);
1697 const OUString aPackURL(getPackURL());
1699 // ensure dir and file vectors
1700 fillDirFileInfo();
1702 // process all files in question recursively
1703 if (!maDirs.empty() || !maFiles.empty())
1705 bDidPop = tryPop_files(
1706 maDirs,
1707 maFiles,
1708 maUserConfigWorkURL,
1709 aPackURL);
1712 if (bDidPop)
1714 // try removal of evtl. empty directory
1715 osl::Directory::remove(aPackURL);
1719 bool BackupFileHelper::isPopPossibleExtensionInfo() const
1721 bool bPopPossible(false);
1723 if (mbActive && mbExtensions)
1725 const OUString aPackURL(getPackURL());
1727 bPopPossible = isPopPossible_extensionInfo(aPackURL);
1730 return bPopPossible;
1733 void BackupFileHelper::tryPopExtensionInfo()
1735 if (!(mbActive && mbExtensions))
1736 return;
1738 bool bDidPop(false);
1739 const OUString aPackURL(getPackURL());
1741 bDidPop = tryPop_extensionInfo(aPackURL);
1743 if (bDidPop)
1745 // try removal of evtl. empty directory
1746 osl::Directory::remove(aPackURL);
1750 bool BackupFileHelper::isTryDisableAllExtensionsPossible()
1752 // check if there are still enabled extension which can be disabled,
1753 // but as we are now in SafeMode, use XML infos for this since the
1754 // extensions are not loaded from XExtensionManager
1755 class ExtensionInfo aExtensionInfo;
1757 aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
1759 return aExtensionInfo.areThereEnabledExtensions();
1762 void BackupFileHelper::tryDisableAllExtensions()
1764 // disable all still enabled extensions,
1765 // but as we are now in SafeMode, use XML infos for this since the
1766 // extensions are not loaded from XExtensionManager
1767 ExtensionInfo aCurrentExtensionInfo;
1768 const ExtensionInfoEntryVector aToBeEnabled{};
1769 ExtensionInfoEntryVector aToBeDisabled;
1771 aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
1773 const ExtensionInfoEntryVector& rCurrentVector = aCurrentExtensionInfo.getExtensionInfoEntryVector();
1775 for (const auto& rCurrentInfo : rCurrentVector)
1777 if (rCurrentInfo.isEnabled())
1779 aToBeDisabled.push_back(rCurrentInfo);
1783 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled);
1786 bool BackupFileHelper::isTryDeinstallUserExtensionsPossible()
1788 // check if there are User Extensions installed.
1789 class ExtensionInfo aExtensionInfo;
1791 aExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
1793 return !aExtensionInfo.getExtensionInfoEntryVector().empty();
1796 void BackupFileHelper::tryDeinstallUserExtensions()
1798 // delete User Extension installs
1799 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/uno_packages");
1802 bool BackupFileHelper::isTryResetSharedExtensionsPossible()
1804 // check if there are shared Extensions installed
1805 class ExtensionInfo aExtensionInfo;
1807 aExtensionInfo.createSharedExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
1809 return !aExtensionInfo.getExtensionInfoEntryVector().empty();
1812 void BackupFileHelper::tryResetSharedExtensions()
1814 // reset shared extension info
1815 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/shared");
1818 bool BackupFileHelper::isTryResetBundledExtensionsPossible()
1820 // check if there are shared Extensions installed
1821 class ExtensionInfo aExtensionInfo;
1823 aExtensionInfo.createBundledExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
1825 return !aExtensionInfo.getExtensionInfoEntryVector().empty();
1828 void BackupFileHelper::tryResetBundledExtensions()
1830 // reset shared extension info
1831 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/extensions/bundled");
1834 const std::vector< OUString >& BackupFileHelper::getCustomizationDirNames()
1836 static std::vector< OUString > aDirNames =
1838 "config", // UI config stuff
1839 "registry", // most of the registry stuff
1840 "psprint", // not really needed, can be abandoned
1841 "store", // not really needed, can be abandoned
1842 "temp", // not really needed, can be abandoned
1843 "pack" // own backup dir
1846 return aDirNames;
1849 const std::vector< OUString >& BackupFileHelper::getCustomizationFileNames()
1851 static std::vector< OUString > aFileNames =
1853 "registrymodifications.xcu" // personal registry stuff
1856 return aFileNames;
1859 namespace {
1860 uno::Reference<XElement> lcl_getConfigElement(const uno::Reference<XDocument>& xDocument, const OUString& rPath,
1861 const OUString& rKey, const OUString& rValue)
1863 uno::Reference< XElement > itemElement = xDocument->createElement("item");
1864 itemElement->setAttribute("oor:path", rPath);
1866 uno::Reference< XElement > propElement = xDocument->createElement("prop");
1867 propElement->setAttribute("oor:name", rKey);
1868 propElement->setAttribute("oor:op", "replace"); // Replace any other options
1870 uno::Reference< XElement > valueElement = xDocument->createElement("value");
1871 uno::Reference< XText > textElement = xDocument->createTextNode(rValue);
1873 valueElement->appendChild(textElement);
1874 propElement->appendChild(valueElement);
1875 itemElement->appendChild(propElement);
1877 return itemElement;
1881 void BackupFileHelper::tryDisableHWAcceleration()
1883 const OUString aRegistryModifications(maUserConfigWorkURL + "/registrymodifications.xcu");
1884 if (!DirectoryHelper::fileExists(aRegistryModifications))
1885 return;
1887 uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1888 uno::Reference< XDocumentBuilder > xBuilder = DocumentBuilder::create(xContext);
1889 uno::Reference< XDocument > xDocument = xBuilder->parseURI(aRegistryModifications);
1890 uno::Reference< XElement > xRootElement = xDocument->getDocumentElement();
1892 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
1893 "DisableOpenGL", "true"));
1894 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/Misc",
1895 "UseOpenCL", "false"));
1896 // Do not disable Skia entirely, just force its CPU-based raster mode.
1897 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
1898 "ForceSkia", "false"));
1899 xRootElement->appendChild(lcl_getConfigElement(xDocument, "/org.openoffice.Office.Common/VCL",
1900 "ForceSkiaRaster", "true"));
1902 OUString aTempURL;
1904 // use the scope to make sure that the temp file gets properly closed before move
1906 // write back
1907 uno::Reference< xml::sax::XSAXSerializable > xSerializer(xDocument, uno::UNO_QUERY);
1909 if (!xSerializer.is())
1910 return;
1912 // create a SAXWriter
1913 uno::Reference< xml::sax::XWriter > const xSaxWriter = xml::sax::Writer::create(xContext);
1914 uno::Reference< io::XTempFile > xTempFile = io::TempFile::create(xContext);
1915 xTempFile->setRemoveFile(false); // avoid removal of tempfile when leaving the scope
1916 uno::Reference< io::XOutputStream > xOutStrm = xTempFile->getOutputStream();
1918 // set output stream and do the serialization
1919 xSaxWriter->setOutputStream(xOutStrm);
1920 xSerializer->serialize(xSaxWriter, uno::Sequence< beans::StringPair >());
1922 // get URL from temp file
1923 aTempURL = xTempFile->getUri();
1926 // copy back file
1927 if (aTempURL.isEmpty() || !DirectoryHelper::fileExists(aTempURL))
1928 return;
1930 if (DirectoryHelper::fileExists(aRegistryModifications))
1932 osl::File::remove(aRegistryModifications);
1935 int result = osl::File::move(aTempURL, aRegistryModifications);
1936 SAL_WARN_IF(result != osl::FileBase::E_None, "comphelper.backupfilehelper", "could not copy back modified Extension configuration file");
1939 bool BackupFileHelper::isTryResetCustomizationsPossible()
1941 // return true if not all of the customization selection dirs or files are deleted
1942 const std::vector< OUString >& rDirs = getCustomizationDirNames();
1944 for (const auto& a : rDirs)
1946 if (DirectoryHelper::dirExists(maUserConfigWorkURL + "/" + a))
1948 return true;
1952 const std::vector< OUString >& rFiles = getCustomizationFileNames();
1954 for (const auto& b : rFiles)
1956 if (DirectoryHelper::fileExists(maUserConfigWorkURL + "/" + b))
1958 return true;
1962 return false;
1965 void BackupFileHelper::tryResetCustomizations()
1967 // delete all of the customization selection dirs
1968 const std::vector< OUString >& rDirs = getCustomizationDirNames();
1970 for (const auto& a : rDirs)
1972 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL + "/" + a);
1975 const std::vector< OUString >& rFiles = getCustomizationFileNames();
1977 for (const auto& b : rFiles)
1979 osl::File::remove(maUserConfigWorkURL + "/" + b);
1983 void BackupFileHelper::tryResetUserProfile()
1985 // completely delete the current UserProfile
1986 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL);
1989 const OUString& BackupFileHelper::getUserProfileURL()
1991 return maUserConfigBaseURL;
1994 const OUString& BackupFileHelper::getUserProfileWorkURL()
1996 return maUserConfigWorkURL;
1999 /////////////////// helpers ///////////////////////
2001 OUString BackupFileHelper::getPackURL()
2003 return OUString(maUserConfigWorkURL + "/pack");
2006 /////////////////// file push helpers ///////////////////////
2008 bool BackupFileHelper::tryPush_Files(
2009 const std::set< OUString >& rDirs,
2010 const std::set< std::pair< OUString, OUString > >& rFiles,
2011 std::u16string_view rSourceURL, // source dir without trailing '/'
2012 const OUString& rTargetURL // target dir without trailing '/'
2015 bool bDidPush(false);
2016 osl::Directory::createPath(rTargetURL);
2018 // process files
2019 for (const auto& file : rFiles)
2021 bDidPush |= tryPush_file(
2022 rSourceURL,
2023 rTargetURL,
2024 file.first,
2025 file.second);
2028 // process dirs
2029 for (const auto& dir : rDirs)
2031 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
2032 OUString aNewTargetURL(rTargetURL + "/" + dir);
2033 std::set< OUString > aNewDirs;
2034 std::set< std::pair< OUString, OUString > > aNewFiles;
2036 DirectoryHelper::scanDirsAndFiles(
2037 aNewSourceURL,
2038 aNewDirs,
2039 aNewFiles);
2041 if (!aNewDirs.empty() || !aNewFiles.empty())
2043 bDidPush |= tryPush_Files(
2044 aNewDirs,
2045 aNewFiles,
2046 aNewSourceURL,
2047 aNewTargetURL);
2051 if (!bDidPush)
2053 // try removal of evtl. empty directory
2054 osl::Directory::remove(rTargetURL);
2057 return bDidPush;
2060 bool BackupFileHelper::tryPush_file(
2061 std::u16string_view rSourceURL, // source dir without trailing '/'
2062 std::u16string_view rTargetURL, // target dir without trailing '/'
2063 std::u16string_view rName, // filename
2064 std::u16string_view rExt // extension (or empty)
2067 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
2069 if (DirectoryHelper::fileExists(aFileURL))
2071 const OUString aPackURL(createPackURL(rTargetURL, rName));
2072 PackedFile aPackedFile(aPackURL);
2073 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aFileURL);
2075 if (aPackedFile.tryPush(aBaseFile, mbCompress))
2077 // reduce to allowed number and flush
2078 aPackedFile.tryReduceToNumBackups(mnNumBackups);
2079 aPackedFile.flush();
2081 return true;
2085 return false;
2088 /////////////////// file pop possibilities helper ///////////////////////
2090 bool BackupFileHelper::isPopPossible_files(
2091 const std::set< OUString >& rDirs,
2092 const std::set< std::pair< OUString, OUString > >& rFiles,
2093 std::u16string_view rSourceURL, // source dir without trailing '/'
2094 std::u16string_view rTargetURL // target dir without trailing '/'
2097 bool bPopPossible(false);
2099 // process files
2100 for (const auto& file : rFiles)
2102 bPopPossible |= isPopPossible_file(
2103 rSourceURL,
2104 rTargetURL,
2105 file.first,
2106 file.second);
2109 // process dirs
2110 for (const auto& dir : rDirs)
2112 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
2113 OUString aNewTargetURL(OUString::Concat(rTargetURL) + "/" + dir);
2114 std::set< OUString > aNewDirs;
2115 std::set< std::pair< OUString, OUString > > aNewFiles;
2117 DirectoryHelper::scanDirsAndFiles(
2118 aNewSourceURL,
2119 aNewDirs,
2120 aNewFiles);
2122 if (!aNewDirs.empty() || !aNewFiles.empty())
2124 bPopPossible |= isPopPossible_files(
2125 aNewDirs,
2126 aNewFiles,
2127 aNewSourceURL,
2128 aNewTargetURL);
2132 return bPopPossible;
2135 bool BackupFileHelper::isPopPossible_file(
2136 std::u16string_view rSourceURL, // source dir without trailing '/'
2137 std::u16string_view rTargetURL, // target dir without trailing '/'
2138 std::u16string_view rName, // filename
2139 std::u16string_view rExt // extension (or empty)
2142 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
2144 if (DirectoryHelper::fileExists(aFileURL))
2146 const OUString aPackURL(createPackURL(rTargetURL, rName));
2147 PackedFile aPackedFile(aPackURL);
2149 return !aPackedFile.empty();
2152 return false;
2155 /////////////////// file pop helpers ///////////////////////
2157 bool BackupFileHelper::tryPop_files(
2158 const std::set< OUString >& rDirs,
2159 const std::set< std::pair< OUString, OUString > >& rFiles,
2160 std::u16string_view rSourceURL, // source dir without trailing '/'
2161 const OUString& rTargetURL // target dir without trailing '/'
2164 bool bDidPop(false);
2166 // process files
2167 for (const auto& file : rFiles)
2169 bDidPop |= tryPop_file(
2170 rSourceURL,
2171 rTargetURL,
2172 file.first,
2173 file.second);
2176 // process dirs
2177 for (const auto& dir : rDirs)
2179 OUString aNewSourceURL(OUString::Concat(rSourceURL) + "/" + dir);
2180 OUString aNewTargetURL(rTargetURL + "/" + dir);
2181 std::set< OUString > aNewDirs;
2182 std::set< std::pair< OUString, OUString > > aNewFiles;
2184 DirectoryHelper::scanDirsAndFiles(
2185 aNewSourceURL,
2186 aNewDirs,
2187 aNewFiles);
2189 if (!aNewDirs.empty() || !aNewFiles.empty())
2191 bDidPop |= tryPop_files(
2192 aNewDirs,
2193 aNewFiles,
2194 aNewSourceURL,
2195 aNewTargetURL);
2199 if (bDidPop)
2201 // try removal of evtl. empty directory
2202 osl::Directory::remove(rTargetURL);
2205 return bDidPop;
2208 bool BackupFileHelper::tryPop_file(
2209 std::u16string_view rSourceURL, // source dir without trailing '/'
2210 std::u16string_view rTargetURL, // target dir without trailing '/'
2211 std::u16string_view rName, // filename
2212 std::u16string_view rExt // extension (or empty)
2215 const OUString aFileURL(createFileURL(rSourceURL, rName, rExt));
2217 if (!DirectoryHelper::fileExists(aFileURL))
2218 return false;
2220 // try Pop for base file
2221 const OUString aPackURL(createPackURL(rTargetURL, rName));
2222 PackedFile aPackedFile(aPackURL);
2224 if (aPackedFile.empty())
2225 return false;
2227 oslFileHandle aHandle;
2228 OUString aTempURL;
2230 // open target temp file - it exists until deleted
2231 if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
2232 return false;
2234 bool bRetval(aPackedFile.tryPop(aHandle));
2236 // close temp file (in all cases) - it exists until deleted
2237 osl_closeFile(aHandle);
2239 if (bRetval)
2241 // copy over existing file by first deleting original
2242 // and moving the temp file to old original
2243 osl::File::remove(aFileURL);
2244 osl::File::move(aTempURL, aFileURL);
2246 // reduce to allowed number and flush
2247 aPackedFile.tryReduceToNumBackups(mnNumBackups);
2248 aPackedFile.flush();
2251 // delete temp file (in all cases - it may be moved already)
2252 osl::File::remove(aTempURL);
2254 return bRetval;
2257 /////////////////// ExtensionInfo helpers ///////////////////////
2259 bool BackupFileHelper::tryPush_extensionInfo(
2260 std::u16string_view rTargetURL // target dir without trailing '/'
2263 ExtensionInfo aExtensionInfo;
2264 OUString aTempURL;
2265 bool bRetval(false);
2267 // create current configuration and write to temp file - it exists until deleted
2268 if (aExtensionInfo.createTempFile(aTempURL))
2270 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
2271 PackedFile aPackedFile(aPackURL);
2272 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL);
2274 if (aPackedFile.tryPush(aBaseFile, mbCompress))
2276 // reduce to allowed number and flush
2277 aPackedFile.tryReduceToNumBackups(mnNumBackups);
2278 aPackedFile.flush();
2279 bRetval = true;
2283 // delete temp file (in all cases)
2284 osl::File::remove(aTempURL);
2285 return bRetval;
2288 bool BackupFileHelper::isPopPossible_extensionInfo(
2289 std::u16string_view rTargetURL // target dir without trailing '/'
2292 // extensionInfo always exists internally, no test needed
2293 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
2294 PackedFile aPackedFile(aPackURL);
2296 return !aPackedFile.empty();
2299 bool BackupFileHelper::tryPop_extensionInfo(
2300 std::u16string_view rTargetURL // target dir without trailing '/'
2303 // extensionInfo always exists internally, no test needed
2304 const OUString aPackURL(createPackURL(rTargetURL, u"ExtensionInfo"));
2305 PackedFile aPackedFile(aPackURL);
2307 if (aPackedFile.empty())
2308 return false;
2310 oslFileHandle aHandle;
2311 OUString aTempURL;
2313 // open target temp file - it exists until deleted
2314 if (osl::File::E_None != osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
2315 return false;
2317 bool bRetval(aPackedFile.tryPop(aHandle));
2319 // close temp file (in all cases) - it exists until deleted
2320 osl_closeFile(aHandle);
2322 if (bRetval)
2324 // last config is in temp file, load it to ExtensionInfo
2325 ExtensionInfo aLoadedExtensionInfo;
2326 FileSharedPtr aBaseFile = std::make_shared<osl::File>(aTempURL);
2328 if (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read))
2330 if (aLoadedExtensionInfo.read_entries(aBaseFile))
2332 // get current extension info, but from XML config files
2333 ExtensionInfo aCurrentExtensionInfo;
2335 aCurrentExtensionInfo.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL);
2337 // now we have loaded last_working (aLoadedExtensionInfo) and
2338 // current (aCurrentExtensionInfo) ExtensionInfo and may react on
2339 // differences by de/activating these as needed
2340 const ExtensionInfoEntryVector& aUserEntries = aCurrentExtensionInfo.getExtensionInfoEntryVector();
2341 const ExtensionInfoEntryVector& rLoadedVector = aLoadedExtensionInfo.getExtensionInfoEntryVector();
2342 ExtensionInfoEntryVector aToBeDisabled;
2343 ExtensionInfoEntryVector aToBeEnabled;
2345 for (const auto& rCurrentInfo : aUserEntries)
2347 const ExtensionInfoEntry* pLoadedInfo = nullptr;
2349 for (const auto& rLoadedInfo : rLoadedVector)
2351 if (rCurrentInfo.isSameExtension(rLoadedInfo))
2353 pLoadedInfo = &rLoadedInfo;
2354 break;
2358 if (nullptr != pLoadedInfo)
2360 // loaded info contains information about the Extension rCurrentInfo
2361 const bool bCurrentEnabled(rCurrentInfo.isEnabled());
2362 const bool bLoadedEnabled(pLoadedInfo->isEnabled());
2364 if (bCurrentEnabled && !bLoadedEnabled)
2366 aToBeDisabled.push_back(rCurrentInfo);
2368 else if (!bCurrentEnabled && bLoadedEnabled)
2370 aToBeEnabled.push_back(rCurrentInfo);
2373 else
2375 // There is no loaded info about the Extension rCurrentInfo.
2376 // It needs to be disabled
2377 if (rCurrentInfo.isEnabled())
2379 aToBeDisabled.push_back(rCurrentInfo);
2384 if (!aToBeDisabled.empty() || !aToBeEnabled.empty())
2386 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL, aToBeEnabled, aToBeDisabled);
2389 bRetval = true;
2393 // reduce to allowed number and flush
2394 aPackedFile.tryReduceToNumBackups(mnNumBackups);
2395 aPackedFile.flush();
2398 // delete temp file (in all cases - it may be moved already)
2399 osl::File::remove(aTempURL);
2401 return bRetval;
2404 /////////////////// FileDirInfo helpers ///////////////////////
2406 void BackupFileHelper::fillDirFileInfo()
2408 if (!maDirs.empty() || !maFiles.empty())
2410 // already done
2411 return;
2414 // Information about the configuration and the role/purpose of directories in
2415 // the UserConfiguration is taken from: https://wiki.documentfoundation.org/UserProfile
2417 // fill dir and file info list to work with dependent on work mode
2418 switch (mnMode)
2420 case 0:
2422 // simple mode: add just registrymodifications
2423 // (the orig file in maInitialBaseURL)
2424 maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt));
2425 break;
2427 case 1:
2429 // defined mode: Add a selection of dirs containing User-Defined and thus
2430 // valuable configuration information.
2431 // This is clearly discussable in every single point and may be adapted/corrected
2432 // over time. Main focus is to secure User-Defined/adapted values
2434 // add registrymodifications (the orig file in maInitialBaseURL)
2435 maFiles.insert(std::pair< OUString, OUString >(maRegModName, maExt));
2437 // User-defined substitution table (Tools/AutoCorrect)
2438 maDirs.insert("autocorr");
2440 // User-Defined AutoText (Edit/AutoText)
2441 maDirs.insert("autotext");
2443 // User-defined Macros
2444 maDirs.insert("basic");
2446 // User-adapted toolbars for modules
2447 maDirs.insert("config");
2449 // Initial and User-defined Databases
2450 maDirs.insert("database");
2452 // most part of registry files
2453 maDirs.insert("registry");
2455 // User-Defined Scripts
2456 maDirs.insert("Scripts");
2458 // Template files
2459 maDirs.insert("template");
2461 // Custom Dictionaries
2462 maDirs.insert("wordbook");
2464 // Questionable - where and how is Extension stuff held and how
2465 // does this interact with enabled/disabled states which are extra handled?
2466 // Keep out of business until deeper evaluated
2468 // maDirs.insert("extensions");
2469 // maDirs.insert("uno-packages");
2470 break;
2472 case 2:
2474 // whole directory. To do so, scan directory and exclude some dirs
2475 // from which we know they do not need to be secured explicitly. This
2476 // should already include registrymodifications, too.
2477 DirectoryHelper::scanDirsAndFiles(
2478 maUserConfigWorkURL,
2479 maDirs,
2480 maFiles);
2482 // should not exist, but for the case an error occurred and it got
2483 // copied somehow, avoid further recursive copying/saving
2484 maDirs.erase("SafeMode");
2486 // not really needed, can be abandoned
2487 maDirs.erase("psprint");
2489 // not really needed, can be abandoned
2490 maDirs.erase("store");
2492 // not really needed, can be abandoned
2493 maDirs.erase("temp");
2495 // exclude own backup dir to avoid recursion
2496 maDirs.erase("pack");
2498 break;
2504 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */