1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
21 #include <string_view>
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
;
50 using namespace css::xml::dom
;
52 const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE
= 16384;
56 typedef std::shared_ptr
< osl::File
> FileSharedPtr
;
58 sal_uInt32
createCrc32(FileSharedPtr
const & rCandidate
, sal_uInt32 nOffset
)
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);
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
73 if (osl::File::E_None
== rCandidate
->setPos(osl_Pos_Absolut
, sal_Int64(nOffset
)))
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
));
87 // error - reset to zero again
99 bool read_sal_uInt32(FileSharedPtr
const & rFile
, sal_uInt32
& rTarget
)
102 sal_uInt64
nBaseRead(0);
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]);
114 bool write_sal_uInt32(oslFileHandle
& rHandle
, sal_uInt32 nSource
)
117 sal_uInt64
nBaseWritten(0);
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
))
138 if (osl::File::E_None
!= rFile
->getPos(nPos
))
142 if (osl::File::E_None
!= rFile
->getSize(nSize
))
145 const auto nRemainingSize
= nSize
- nPos
;
146 if (nLength
> nRemainingSize
)
149 std::vector
<char> aTarget(nLength
);
150 sal_uInt64
nBaseRead(0);
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
));
162 bool write_OString(oslFileHandle
& rHandle
, const OString
& rSource
)
164 const sal_uInt32
nLength(rSource
.getLength());
166 if (!write_sal_uInt32(rHandle
, nLength
))
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
)
181 if (!rURL
.empty() && !rName
.empty())
183 aRetval
= OUString::Concat(rURL
) + "/" + rName
;
187 aRetval
+= OUString::Concat(".") + rExt
;
194 OUString
createPackURL(std::u16string_view rURL
, std::u16string_view rName
)
198 if (!rURL
.empty() && !rName
.empty())
200 aRetval
= OUString::Concat(rURL
) + "/" + rName
+ ".pack";
209 enum PackageRepository
{ USER
, SHARED
, BUNDLED
};
211 class ExtensionInfoEntry
214 OString maName
; // extension name
215 PackageRepository maRepository
; // user|shared|bundled
216 bool mbEnabled
; // state
220 : maRepository(USER
),
225 ExtensionInfoEntry(OString aName
, bool bEnabled
)
226 : maName(std::move(aName
)),
232 ExtensionInfoEntry(const uno::Reference
< deployment::XPackage
>& rxPackage
)
233 : maName(OUStringToOString(rxPackage
->getName(), RTL_TEXTENCODING_ASCII_US
)),
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
;
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
;
280 return 0 > maName
.compareTo(rComp
.maName
);
285 return maRepository
< rComp
.maRepository
;
289 bool read_entry(FileSharedPtr
const & rFile
)
292 if (!read_OString(rFile
, maName
))
298 sal_uInt32
nState(0);
300 if (read_sal_uInt32(rFile
, nState
))
302 maRepository
= static_cast< PackageRepository
>(nState
);
310 if (read_sal_uInt32(rFile
, nState
))
312 mbEnabled
= static_cast< bool >(nState
);
322 bool write_entry(oslFileHandle
& rHandle
) const
325 if (!write_OString(rHandle
, maName
))
330 // write maRepository
331 sal_uInt32
nState(maRepository
);
333 if (!write_sal_uInt32(rHandle
, nState
))
339 nState
= static_cast< sal_uInt32
>(mbEnabled
);
341 return write_sal_uInt32(rHandle
, nState
);
344 const OString
& getName() const
349 bool isEnabled() const
355 typedef std::vector
< ExtensionInfoEntry
> ExtensionInfoEntryVector
;
357 constexpr OUString gaRegPath
{ u
"/registry/com.sun.star.comp.deployment.bundle.PackageRegistryBackend/backenddb.xml"_ustr
};
362 ExtensionInfoEntryVector maEntries
;
369 const ExtensionInfoEntryVector
& getExtensionInfoEntryVector() const
380 void createUsingXExtensionManager()
385 // create content from current extension configuration
386 uno::Sequence
< uno::Sequence
< uno::Reference
< deployment::XPackage
> > > xAllPackages
;
387 const 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
&)
399 catch (const ucb::CommandFailedException
&)
403 catch (const ucb::CommandAbortedException
&)
407 catch (const lang::IllegalArgumentException
& e
)
409 css::uno::Any anyEx
= cppu::getCaughtException();
410 throw css::lang::WrappedTargetRuntimeException( e
.Message
,
414 for (const uno::Sequence
< uno::Reference
< deployment::XPackage
> > & xPackageList
: xAllPackages
)
416 for (const uno::Reference
< deployment::XPackage
> & xPackage
: xPackageList
)
420 maEntries
.emplace_back(xPackage
);
425 if (!maEntries
.empty())
428 std::sort(maEntries
.begin(), maEntries
.end());
433 void visitNodesXMLRead(const uno::Reference
< xml::dom::XElement
>& rElement
)
438 const OUString
aTagName(rElement
->getTagName());
440 if (aTagName
== "extension")
442 OUString
aAttrUrl(rElement
->getAttribute(u
"url"_ustr
));
443 const OUString
aAttrRevoked(rElement
->getAttribute(u
"revoked"_ustr
));
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
),
462 uno::Reference
< xml::dom::XNodeList
> aList
= rElement
->getChildNodes();
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
);
474 visitNodesXMLRead(aChild
);
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 const 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
);
514 visitNodesXMLRead(aDocument
->getDocumentElement());
518 if (!maEntries
.empty())
521 std::sort(maEntries
.begin(), maEntries
.end());
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);
536 const OUString
aTagName(rElement
->getTagName());
538 if (aTagName
== rTagToSearch
)
540 const OString
aAttrUrl(OUStringToOString(rElement
->getAttribute(u
"url"_ustr
), RTL_TEXTENCODING_ASCII_US
));
541 const OUString
aAttrRevoked(rElement
->getAttribute(u
"revoked"_ustr
));
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()))
552 // needs to be enabled
553 rElement
->removeAttribute(u
"revoked"_ustr
);
559 for (const auto& disable
: rToBeDisabled
)
561 if (-1 != aAttrUrl
.indexOf(disable
.getName()))
565 // needs to be disabled
566 rElement
->setAttribute(u
"revoked"_ustr
, u
"true"_ustr
);
575 uno::Reference
< xml::dom::XNodeList
> aList
= rElement
->getChildNodes();
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
);
587 bChanged
|= visitNodesXMLChange(
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
))
610 const 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
);
617 if (!visitNodesXMLChange(
619 aDocument
->getDocumentElement(),
624 // did change - write back
625 uno::Reference
< xml::sax::XSAXSerializable
> xSerializer(aDocument
, uno::UNO_QUERY
);
627 if (!xSerializer
.is())
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();
643 if (aTempURL
.isEmpty() || !DirectoryHelper::fileExists(aTempURL
))
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");
654 osl::File::move(aTempURL
, rUnoPackagReg
);
659 static void changeEnableDisableStateInXML(
660 std::u16string_view rUserConfigWorkURL
,
661 const ExtensionInfoEntryVector
& rToBeEnabled
,
662 const ExtensionInfoEntryVector
& rToBeDisabled
)
664 static constexpr OUString
aRegPathFront(u
"/uno_packages/cache/registry/com.sun.star.comp.deployment."_ustr
);
665 static constexpr OUString
aRegPathBack(u
".PackageRegistryBackend/backenddb.xml"_ustr
);
666 // first appearance to check
668 const OUString
aUnoPackagReg(OUString::Concat(rUserConfigWorkURL
) + aRegPathFront
+ "bundle" + aRegPathBack
);
670 visitNodesXMLChangeOneCase(
677 // second appearance to check
679 const OUString
aUnoPackagReg(OUString::Concat(rUserConfigWorkURL
) + aRegPathFront
+ "configuration" + aRegPathBack
);
681 visitNodesXMLChangeOneCase(
683 u
"configuration"_ustr
,
688 // third appearance to check
690 const OUString
aUnoPackagReg(OUString::Concat(rUserConfigWorkURL
) + aRegPathFront
+ "script" + aRegPathBack
);
692 visitNodesXMLChangeOneCase(
700 bool read_entries(FileSharedPtr
const & rFile
)
702 // read NumExtensionEntries
703 sal_uInt32
nExtEntries(0);
705 if (!read_sal_uInt32(rFile
, nExtEntries
))
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
)
717 for (sal_uInt32
a(0); a
< nExtEntries
; a
++)
719 ExtensionInfoEntry aNewEntry
;
721 if (aNewEntry
.read_entry(rFile
))
723 maEntries
.push_back(aNewEntry
);
734 bool write_entries(oslFileHandle
& rHandle
) const
736 const sal_uInt32
nExtEntries(maEntries
.size());
738 if (!write_sal_uInt32(rHandle
, nExtEntries
))
743 for (const auto& a
: maEntries
)
745 if (!a
.write_entry(rHandle
))
754 bool createTempFile(OUString
& rTempFileName
)
756 oslFileHandle aHandle
;
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
);
777 bool areThereEnabledExtensions() const
779 for (const auto& a
: maEntries
)
794 class PackedFileEntry
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
))
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())))
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
)
825 if (osl_File_E_None
!= osl_writeFile(rTargetHandle
, static_cast<const void*>(aArray
), nToTransfer
, &nBytesTransfer
) || nBytesTransfer
!= nToTransfer
)
830 nSize
-= nToTransfer
;
838 bool copy_content_compress(oslFileHandle
& rTargetHandle
)
840 if (!maFile
|| osl::File::E_None
!= maFile
->open(osl_File_OpenFlag_Read
))
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());
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())))
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
)
866 zstream
.avail_in
= nToTransfer
;
867 zstream
.next_in
= reinterpret_cast<unsigned char*>(aArray
);
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
));
875 const sal_Int64
nRetval(z_deflate(&zstream
, nSize
== nToTransfer
? Z_FINISH
: Z_NO_FLUSH
));
877 if (Z_STREAM_ERROR
== nRetval
)
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
)
890 } while (bOkay
&& 0 == zstream
.avail_out
);
897 nSize
-= nToTransfer
;
900 #if !defined Z_PREFIX
901 deflateEnd(&zstream
);
903 z_deflateEnd(&zstream
);
910 // get compressed size and add to entry
911 if (mnFullFileSize
== mnPackFileSize
&& mnFullFileSize
== zstream
.total_in
)
913 mnPackFileSize
= zstream
.total_out
;
919 bool copy_content_uncompress(oslFileHandle
& rTargetHandle
)
921 if (!maFile
|| osl::File::E_None
!= maFile
->open(osl_File_OpenFlag_Read
))
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());
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())))
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
)
947 zstream
.avail_in
= nToTransfer
;
948 zstream
.next_in
= reinterpret_cast<unsigned char*>(aArray
);
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
));
956 const sal_Int64
nRetval(z_inflate(&zstream
, Z_NO_FLUSH
));
958 if (Z_STREAM_ERROR
== nRetval
)
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
)
971 } while (bOkay
&& 0 == zstream
.avail_out
);
978 nSize
-= nToTransfer
;
981 #if !defined Z_PREFIX
982 deflateEnd(&zstream
);
984 z_deflateEnd(&zstream
);
995 // create new, uncompressed entry
997 sal_uInt32 nFullFileSize
,
1001 : mnFullFileSize(nFullFileSize
),
1002 mnPackFileSize(nFullFileSize
),
1005 maFile(std::move(xFile
)),
1006 mbDoCompress(bDoCompress
)
1010 // create entry to be loaded as header (read_header)
1012 : mnFullFileSize(0),
1020 sal_uInt32
getFullFileSize() const
1022 return mnFullFileSize
;
1025 sal_uInt32
getPackFileSize() const
1027 return mnPackFileSize
;
1030 sal_uInt32
getOffset() const
1035 void setOffset(sal_uInt32 nOffset
)
1040 static sal_uInt32
getEntrySize()
1045 sal_uInt32
getCrc32() const
1050 bool read_header(FileSharedPtr
const & rFile
)
1059 // read and compute full file size
1060 if (!read_sal_uInt32(rFile
, mnFullFileSize
))
1065 // read and compute entry crc32
1066 if (!read_sal_uInt32(rFile
, mnCrc32
))
1071 // read and compute packed size
1072 if (!read_sal_uInt32(rFile
, mnPackFileSize
))
1080 bool write_header(oslFileHandle
& rHandle
) const
1082 // write full file size
1083 if (!write_sal_uInt32(rHandle
, mnFullFileSize
))
1089 if (!write_sal_uInt32(rHandle
, mnCrc32
))
1094 // write packed file size
1095 if (!write_sal_uInt32(rHandle
, mnPackFileSize
))
1103 bool copy_content(oslFileHandle
& rTargetHandle
, bool bUncompress
)
1107 if (getFullFileSize() == getPackFileSize())
1109 // not compressed, just copy
1110 return copy_content_straight(rTargetHandle
);
1114 // compressed, need to uncompress on copy
1115 return copy_content_uncompress(rTargetHandle
);
1118 else if (0 == getOffset())
1122 // compressed wanted, need to compress on copy
1123 return copy_content_compress(rTargetHandle
);
1127 // not compressed, straight copy
1128 return copy_content_straight(rTargetHandle
);
1133 return copy_content_straight(rTargetHandle
);
1144 const OUString maURL
;
1145 std::deque
< PackedFileEntry
>
1146 maPackedFileEntryVector
;
1150 PackedFile(const OUString
& rURL
)
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
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
);
1200 // on read error clear local data
1201 maPackedFileEntryVector
.clear();
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
);
1238 if (maPackedFileEntryVector
.empty())
1240 // get rid of (now?) empty pack file
1241 osl::File::remove(maURL
);
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;
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);
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
)
1289 // write contents - this may adapt PackFileSize for new
1291 for (auto& candidate
: maPackedFileEntryVector
)
1293 if (!candidate
.copy_content(aHandle
, false))
1303 // seek back to header start (at position 8)
1304 if (osl_File_E_None
!= osl_setFilePos(aHandle
, osl_Pos_Absolut
, sal_Int64(8)))
1313 for (const auto& candidate
: maPackedFileEntryVector
)
1315 if (!candidate
.write_header(aHandle
))
1327 // close temp file (in all cases) - it exists until deleted
1328 osl_closeFile(aHandle
);
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();
1355 // empty file offered
1359 bool bNeedToAdd(false);
1360 sal_uInt32
nCrc32(0);
1362 if (maPackedFileEntryVector
.empty())
1364 // no backup yet, add as 1st backup
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
1380 // same size, check crc32
1381 nCrc32
= createCrc32(rFileCandidate
, 0);
1383 if (nCrc32
!= aLastEntry
.getCrc32())
1385 // different crc, different file
1393 // create crc32 if not yet done
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
),
1413 bool tryPop(oslFileHandle
& rHandle
)
1415 if (maPackedFileEntryVector
.empty())
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
1424 bool bRetval
= aLastEntry
.copy_content(rHandle
, true);
1428 maPackedFileEntryVector
.pop_back();
1435 void tryReduceToNumBackups(sal_uInt16 nNumBackups
)
1437 while (maPackedFileEntryVector
.size() > nNumBackups
)
1439 maPackedFileEntryVector
.pop_front();
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(u
"${CONFIGURATION_LAYERS}"_ustr
);
1469 rtl::Bootstrap::expandMacros(conf
);
1470 static constexpr OUString
aTokenUser(u
"user:"_ustr
);
1471 sal_Int32
nStart(conf
.indexOf(aTokenUser
));
1475 nStart
+= aTokenUser
.getLength();
1476 sal_Int32
nEnd(conf
.indexOf(' ', nStart
));
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
), '/',
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 constexpr OUString
aSafeMode(u
"SafeMode"_ustr
);
1520 BackupFileHelper::BackupFileHelper()
1529 // read configuration item 'SecureUserConfig' -> bool on/off
1530 if (rtl::Bootstrap::get(u
"SecureUserConfig"_ustr
, sTokenOut
))
1532 mbActive
= sTokenOut
.toBoolean();
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(u
"SecureUserConfigNumCopies"_ustr
, 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(u
"SecureUserConfigMode"_ustr
, 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(u
"SecureUserConfigExtensions"_ustr
, sTokenOut
))
1562 mbExtensions
= sTokenOut
.toBoolean();
1565 if (mbActive
&& rtl::Bootstrap::get(u
"SecureUserConfigCompress"_ustr
, sTokenOut
))
1567 mbCompress
= sTokenOut
.toBoolean();
1571 void BackupFileHelper::setExitWasCalled()
1573 mbExitWasCalled
= true;
1576 void BackupFileHelper::reactOnSafeMode(bool bSafeMode
)
1578 // ensure existence of needed paths
1579 getInitialBaseURL();
1581 if (maUserConfigBaseURL
.isEmpty())
1586 if (!mbSafeModeDirExists
)
1588 std::set
< OUString
> aExcludeList
;
1590 // do not move SafeMode directory itself
1591 aExcludeList
.insert(getSafeModeName());
1593 // init SafeMode by creating the 'SafeMode' directory and moving
1594 // all stuff there. All repairs will happen there. Both Dirs have to exist.
1595 // extend maUserConfigWorkURL as needed
1596 maUserConfigWorkURL
= maUserConfigBaseURL
+ "/" + getSafeModeName();
1598 osl::Directory::createPath(maUserConfigWorkURL
);
1599 DirectoryHelper::moveDirContent(maUserConfigBaseURL
, maUserConfigWorkURL
, aExcludeList
);
1601 // switch local flag, maUserConfigWorkURL is already reset
1602 mbSafeModeDirExists
= true;
1607 if (mbSafeModeDirExists
)
1609 // SafeMode has ended, return to normal mode by moving all content
1610 // from 'SafeMode' directory back to UserDirectory and deleting it.
1611 // Both Dirs have to exist
1612 std::set
< OUString
> aExcludeList
;
1614 DirectoryHelper::moveDirContent(maUserConfigWorkURL
, maUserConfigBaseURL
, aExcludeList
);
1615 osl::Directory::remove(maUserConfigWorkURL
);
1617 // switch local flag and reset maUserConfigWorkURL
1618 mbSafeModeDirExists
= false;
1619 maUserConfigWorkURL
= maUserConfigBaseURL
;
1624 void BackupFileHelper::tryPush()
1626 // no push when SafeModeDir exists, it may be Office's exit after SafeMode
1627 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
1628 // done yet (is done at next startup)
1629 if (!mbActive
|| mbSafeModeDirExists
)
1632 const OUString
aPackURL(getPackURL());
1634 // ensure dir and file vectors
1637 // process all files in question recursively
1638 if (!maDirs
.empty() || !maFiles
.empty())
1643 maUserConfigWorkURL
,
1648 void BackupFileHelper::tryPushExtensionInfo()
1650 // no push when SafeModeDir exists, it may be Office's exit after SafeMode
1651 // where SafeMode flag is already deleted, but SafeModeDir cleanup is not
1652 // done yet (is done at next startup)
1653 if (mbActive
&& mbExtensions
&& !mbSafeModeDirExists
)
1655 const OUString
aPackURL(getPackURL());
1657 tryPush_extensionInfo(aPackURL
);
1661 bool BackupFileHelper::isPopPossible()
1663 bool bPopPossible(false);
1667 const OUString
aPackURL(getPackURL());
1669 // ensure dir and file vectors
1672 // process all files in question recursively
1673 if (!maDirs
.empty() || !maFiles
.empty())
1675 bPopPossible
= isPopPossible_files(
1678 maUserConfigWorkURL
,
1683 return bPopPossible
;
1686 void BackupFileHelper::tryPop()
1691 bool bDidPop(false);
1692 const OUString
aPackURL(getPackURL());
1694 // ensure dir and file vectors
1697 // process all files in question recursively
1698 if (!maDirs
.empty() || !maFiles
.empty())
1700 bDidPop
= tryPop_files(
1703 maUserConfigWorkURL
,
1709 // try removal of evtl. empty directory
1710 osl::Directory::remove(aPackURL
);
1714 bool BackupFileHelper::isPopPossibleExtensionInfo() const
1716 bool bPopPossible(false);
1718 if (mbActive
&& mbExtensions
)
1720 const OUString
aPackURL(getPackURL());
1722 bPopPossible
= isPopPossible_extensionInfo(aPackURL
);
1725 return bPopPossible
;
1728 void BackupFileHelper::tryPopExtensionInfo()
1730 if (!(mbActive
&& mbExtensions
))
1733 bool bDidPop(false);
1734 const OUString
aPackURL(getPackURL());
1736 bDidPop
= tryPop_extensionInfo(aPackURL
);
1740 // try removal of evtl. empty directory
1741 osl::Directory::remove(aPackURL
);
1745 bool BackupFileHelper::isTryDisableAllExtensionsPossible()
1747 // check if there are still enabled extension which can be disabled,
1748 // but as we are now in SafeMode, use XML infos for this since the
1749 // extensions are not loaded from XExtensionManager
1750 class ExtensionInfo aExtensionInfo
;
1752 aExtensionInfo
.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
1754 return aExtensionInfo
.areThereEnabledExtensions();
1757 void BackupFileHelper::tryDisableAllExtensions()
1759 // disable all still enabled extensions,
1760 // but as we are now in SafeMode, use XML infos for this since the
1761 // extensions are not loaded from XExtensionManager
1762 ExtensionInfo aCurrentExtensionInfo
;
1763 const ExtensionInfoEntryVector aToBeEnabled
{};
1764 ExtensionInfoEntryVector aToBeDisabled
;
1766 aCurrentExtensionInfo
.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
1768 const ExtensionInfoEntryVector
& rCurrentVector
= aCurrentExtensionInfo
.getExtensionInfoEntryVector();
1770 for (const auto& rCurrentInfo
: rCurrentVector
)
1772 if (rCurrentInfo
.isEnabled())
1774 aToBeDisabled
.push_back(rCurrentInfo
);
1778 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL
, aToBeEnabled
, aToBeDisabled
);
1781 bool BackupFileHelper::isTryDeinstallUserExtensionsPossible()
1783 // check if there are User Extensions installed.
1784 class ExtensionInfo aExtensionInfo
;
1786 aExtensionInfo
.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
1788 return !aExtensionInfo
.getExtensionInfoEntryVector().empty();
1791 void BackupFileHelper::tryDeinstallUserExtensions()
1793 // delete User Extension installs
1794 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL
+ "/uno_packages");
1797 bool BackupFileHelper::isTryResetSharedExtensionsPossible()
1799 // check if there are shared Extensions installed
1800 class ExtensionInfo aExtensionInfo
;
1802 aExtensionInfo
.createSharedExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
1804 return !aExtensionInfo
.getExtensionInfoEntryVector().empty();
1807 void BackupFileHelper::tryResetSharedExtensions()
1809 // reset shared extension info
1810 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL
+ "/extensions/shared");
1813 bool BackupFileHelper::isTryResetBundledExtensionsPossible()
1815 // check if there are shared Extensions installed
1816 class ExtensionInfo aExtensionInfo
;
1818 aExtensionInfo
.createBundledExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
1820 return !aExtensionInfo
.getExtensionInfoEntryVector().empty();
1823 void BackupFileHelper::tryResetBundledExtensions()
1825 // reset shared extension info
1826 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL
+ "/extensions/bundled");
1829 const std::vector
< OUString
>& BackupFileHelper::getCustomizationDirNames()
1831 static std::vector
< OUString
> aDirNames
=
1833 u
"config"_ustr
, // UI config stuff
1834 u
"registry"_ustr
, // most of the registry stuff
1835 u
"psprint"_ustr
, // not really needed, can be abandoned
1836 u
"store"_ustr
, // not really needed, can be abandoned
1837 u
"temp"_ustr
, // not really needed, can be abandoned
1838 u
"pack"_ustr
// own backup dir
1844 const std::vector
< OUString
>& BackupFileHelper::getCustomizationFileNames()
1846 static std::vector
< OUString
> aFileNames
=
1848 u
"registrymodifications.xcu"_ustr
// personal registry stuff
1855 uno::Reference
<XElement
> lcl_getConfigElement(const uno::Reference
<XDocument
>& xDocument
, const OUString
& rPath
,
1856 const OUString
& rKey
, const OUString
& rValue
)
1858 uno::Reference
< XElement
> itemElement
= xDocument
->createElement(u
"item"_ustr
);
1859 itemElement
->setAttribute(u
"oor:path"_ustr
, rPath
);
1861 uno::Reference
< XElement
> propElement
= xDocument
->createElement(u
"prop"_ustr
);
1862 propElement
->setAttribute(u
"oor:name"_ustr
, rKey
);
1863 propElement
->setAttribute(u
"oor:op"_ustr
, u
"replace"_ustr
); // Replace any other options
1865 uno::Reference
< XElement
> valueElement
= xDocument
->createElement(u
"value"_ustr
);
1866 uno::Reference
< XText
> textElement
= xDocument
->createTextNode(rValue
);
1868 valueElement
->appendChild(textElement
);
1869 propElement
->appendChild(valueElement
);
1870 itemElement
->appendChild(propElement
);
1876 void BackupFileHelper::tryDisableHWAcceleration()
1878 const OUString
aRegistryModifications(maUserConfigWorkURL
+ "/registrymodifications.xcu");
1879 if (!DirectoryHelper::fileExists(aRegistryModifications
))
1882 const uno::Reference
< uno::XComponentContext
>& xContext
= ::comphelper::getProcessComponentContext();
1883 uno::Reference
< XDocumentBuilder
> xBuilder
= DocumentBuilder::create(xContext
);
1884 uno::Reference
< XDocument
> xDocument
= xBuilder
->parseURI(aRegistryModifications
);
1885 uno::Reference
< XElement
> xRootElement
= xDocument
->getDocumentElement();
1887 xRootElement
->appendChild(lcl_getConfigElement(xDocument
, u
"/org.openoffice.Office.Common/VCL"_ustr
,
1888 u
"DisableOpenGL"_ustr
, u
"true"_ustr
));
1889 xRootElement
->appendChild(lcl_getConfigElement(xDocument
, u
"/org.openoffice.Office.Common/Misc"_ustr
,
1890 u
"UseOpenCL"_ustr
, u
"false"_ustr
));
1891 // Do not disable Skia entirely, just force its CPU-based raster mode.
1892 xRootElement
->appendChild(lcl_getConfigElement(xDocument
, u
"/org.openoffice.Office.Common/VCL"_ustr
,
1893 u
"ForceSkia"_ustr
, u
"false"_ustr
));
1894 xRootElement
->appendChild(lcl_getConfigElement(xDocument
, u
"/org.openoffice.Office.Common/VCL"_ustr
,
1895 u
"ForceSkiaRaster"_ustr
, u
"true"_ustr
));
1899 // use the scope to make sure that the temp file gets properly closed before move
1902 uno::Reference
< xml::sax::XSAXSerializable
> xSerializer(xDocument
, uno::UNO_QUERY
);
1904 if (!xSerializer
.is())
1907 // create a SAXWriter
1908 uno::Reference
< xml::sax::XWriter
> const xSaxWriter
= xml::sax::Writer::create(xContext
);
1909 uno::Reference
< io::XTempFile
> xTempFile
= io::TempFile::create(xContext
);
1910 xTempFile
->setRemoveFile(false); // avoid removal of tempfile when leaving the scope
1911 uno::Reference
< io::XOutputStream
> xOutStrm
= xTempFile
->getOutputStream();
1913 // set output stream and do the serialization
1914 xSaxWriter
->setOutputStream(xOutStrm
);
1915 xSerializer
->serialize(xSaxWriter
, uno::Sequence
< beans::StringPair
>());
1917 // get URL from temp file
1918 aTempURL
= xTempFile
->getUri();
1922 if (aTempURL
.isEmpty() || !DirectoryHelper::fileExists(aTempURL
))
1925 if (DirectoryHelper::fileExists(aRegistryModifications
))
1927 osl::File::remove(aRegistryModifications
);
1930 int result
= osl::File::move(aTempURL
, aRegistryModifications
);
1931 SAL_WARN_IF(result
!= osl::FileBase::E_None
, "comphelper.backupfilehelper", "could not copy back modified Extension configuration file");
1934 bool BackupFileHelper::isTryResetCustomizationsPossible()
1936 // return true if not all of the customization selection dirs or files are deleted
1937 const std::vector
< OUString
>& rDirs
= getCustomizationDirNames();
1939 for (const auto& a
: rDirs
)
1941 if (DirectoryHelper::dirExists(maUserConfigWorkURL
+ "/" + a
))
1947 const std::vector
< OUString
>& rFiles
= getCustomizationFileNames();
1949 for (const auto& b
: rFiles
)
1951 if (DirectoryHelper::fileExists(maUserConfigWorkURL
+ "/" + b
))
1960 void BackupFileHelper::tryResetCustomizations()
1962 // delete all of the customization selection dirs
1963 const std::vector
< OUString
>& rDirs
= getCustomizationDirNames();
1965 for (const auto& a
: rDirs
)
1967 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL
+ "/" + a
);
1970 const std::vector
< OUString
>& rFiles
= getCustomizationFileNames();
1972 for (const auto& b
: rFiles
)
1974 osl::File::remove(maUserConfigWorkURL
+ "/" + b
);
1978 void BackupFileHelper::tryResetUserProfile()
1980 // completely delete the current UserProfile
1981 DirectoryHelper::deleteDirRecursively(maUserConfigWorkURL
);
1984 const OUString
& BackupFileHelper::getUserProfileURL()
1986 return maUserConfigBaseURL
;
1989 const OUString
& BackupFileHelper::getUserProfileWorkURL()
1991 return maUserConfigWorkURL
;
1994 /////////////////// helpers ///////////////////////
1996 OUString
BackupFileHelper::getPackURL()
1998 return OUString(maUserConfigWorkURL
+ "/pack");
2001 /////////////////// file push helpers ///////////////////////
2003 bool BackupFileHelper::tryPush_Files(
2004 const std::set
< OUString
>& rDirs
,
2005 const std::set
< std::pair
< OUString
, OUString
> >& rFiles
,
2006 std::u16string_view rSourceURL
, // source dir without trailing '/'
2007 const OUString
& rTargetURL
// target dir without trailing '/'
2010 bool bDidPush(false);
2011 osl::Directory::createPath(rTargetURL
);
2014 for (const auto& file
: rFiles
)
2016 bDidPush
|= tryPush_file(
2024 for (const auto& dir
: rDirs
)
2026 OUString
aNewSourceURL(OUString::Concat(rSourceURL
) + "/" + dir
);
2027 OUString
aNewTargetURL(rTargetURL
+ "/" + dir
);
2028 std::set
< OUString
> aNewDirs
;
2029 std::set
< std::pair
< OUString
, OUString
> > aNewFiles
;
2031 DirectoryHelper::scanDirsAndFiles(
2036 if (!aNewDirs
.empty() || !aNewFiles
.empty())
2038 bDidPush
|= tryPush_Files(
2048 // try removal of evtl. empty directory
2049 osl::Directory::remove(rTargetURL
);
2055 bool BackupFileHelper::tryPush_file(
2056 std::u16string_view rSourceURL
, // source dir without trailing '/'
2057 std::u16string_view rTargetURL
, // target dir without trailing '/'
2058 std::u16string_view rName
, // filename
2059 std::u16string_view rExt
// extension (or empty)
2062 const OUString
aFileURL(createFileURL(rSourceURL
, rName
, rExt
));
2064 if (DirectoryHelper::fileExists(aFileURL
))
2066 const OUString
aPackURL(createPackURL(rTargetURL
, rName
));
2067 PackedFile
aPackedFile(aPackURL
);
2068 FileSharedPtr aBaseFile
= std::make_shared
<osl::File
>(aFileURL
);
2070 if (aPackedFile
.tryPush(aBaseFile
, mbCompress
))
2072 // reduce to allowed number and flush
2073 aPackedFile
.tryReduceToNumBackups(mnNumBackups
);
2074 aPackedFile
.flush();
2083 /////////////////// file pop possibilities helper ///////////////////////
2085 bool BackupFileHelper::isPopPossible_files(
2086 const std::set
< OUString
>& rDirs
,
2087 const std::set
< std::pair
< OUString
, OUString
> >& rFiles
,
2088 std::u16string_view rSourceURL
, // source dir without trailing '/'
2089 std::u16string_view rTargetURL
// target dir without trailing '/'
2092 bool bPopPossible(false);
2095 for (const auto& file
: rFiles
)
2097 bPopPossible
|= isPopPossible_file(
2105 for (const auto& dir
: rDirs
)
2107 OUString
aNewSourceURL(OUString::Concat(rSourceURL
) + "/" + dir
);
2108 OUString
aNewTargetURL(OUString::Concat(rTargetURL
) + "/" + dir
);
2109 std::set
< OUString
> aNewDirs
;
2110 std::set
< std::pair
< OUString
, OUString
> > aNewFiles
;
2112 DirectoryHelper::scanDirsAndFiles(
2117 if (!aNewDirs
.empty() || !aNewFiles
.empty())
2119 bPopPossible
|= isPopPossible_files(
2127 return bPopPossible
;
2130 bool BackupFileHelper::isPopPossible_file(
2131 std::u16string_view rSourceURL
, // source dir without trailing '/'
2132 std::u16string_view rTargetURL
, // target dir without trailing '/'
2133 std::u16string_view rName
, // filename
2134 std::u16string_view rExt
// extension (or empty)
2137 const OUString
aFileURL(createFileURL(rSourceURL
, rName
, rExt
));
2139 if (DirectoryHelper::fileExists(aFileURL
))
2141 const OUString
aPackURL(createPackURL(rTargetURL
, rName
));
2142 PackedFile
aPackedFile(aPackURL
);
2144 return !aPackedFile
.empty();
2150 /////////////////// file pop helpers ///////////////////////
2152 bool BackupFileHelper::tryPop_files(
2153 const std::set
< OUString
>& rDirs
,
2154 const std::set
< std::pair
< OUString
, OUString
> >& rFiles
,
2155 std::u16string_view rSourceURL
, // source dir without trailing '/'
2156 const OUString
& rTargetURL
// target dir without trailing '/'
2159 bool bDidPop(false);
2162 for (const auto& file
: rFiles
)
2164 bDidPop
|= tryPop_file(
2172 for (const auto& dir
: rDirs
)
2174 OUString
aNewSourceURL(OUString::Concat(rSourceURL
) + "/" + dir
);
2175 OUString
aNewTargetURL(rTargetURL
+ "/" + dir
);
2176 std::set
< OUString
> aNewDirs
;
2177 std::set
< std::pair
< OUString
, OUString
> > aNewFiles
;
2179 DirectoryHelper::scanDirsAndFiles(
2184 if (!aNewDirs
.empty() || !aNewFiles
.empty())
2186 bDidPop
|= tryPop_files(
2196 // try removal of evtl. empty directory
2197 osl::Directory::remove(rTargetURL
);
2203 bool BackupFileHelper::tryPop_file(
2204 std::u16string_view rSourceURL
, // source dir without trailing '/'
2205 std::u16string_view rTargetURL
, // target dir without trailing '/'
2206 std::u16string_view rName
, // filename
2207 std::u16string_view rExt
// extension (or empty)
2210 const OUString
aFileURL(createFileURL(rSourceURL
, rName
, rExt
));
2212 if (!DirectoryHelper::fileExists(aFileURL
))
2215 // try Pop for base file
2216 const OUString
aPackURL(createPackURL(rTargetURL
, rName
));
2217 PackedFile
aPackedFile(aPackURL
);
2219 if (aPackedFile
.empty())
2222 oslFileHandle aHandle
;
2225 // open target temp file - it exists until deleted
2226 if (osl::File::E_None
!= osl::FileBase::createTempFile(nullptr, &aHandle
, &aTempURL
))
2229 bool bRetval(aPackedFile
.tryPop(aHandle
));
2231 // close temp file (in all cases) - it exists until deleted
2232 osl_closeFile(aHandle
);
2236 // copy over existing file by first deleting original
2237 // and moving the temp file to old original
2238 osl::File::remove(aFileURL
);
2239 osl::File::move(aTempURL
, aFileURL
);
2241 // reduce to allowed number and flush
2242 aPackedFile
.tryReduceToNumBackups(mnNumBackups
);
2243 aPackedFile
.flush();
2246 // delete temp file (in all cases - it may be moved already)
2247 osl::File::remove(aTempURL
);
2252 /////////////////// ExtensionInfo helpers ///////////////////////
2254 bool BackupFileHelper::tryPush_extensionInfo(
2255 std::u16string_view rTargetURL
// target dir without trailing '/'
2258 ExtensionInfo aExtensionInfo
;
2260 bool bRetval(false);
2262 // create current configuration and write to temp file - it exists until deleted
2263 if (aExtensionInfo
.createTempFile(aTempURL
))
2265 const OUString
aPackURL(createPackURL(rTargetURL
, u
"ExtensionInfo"));
2266 PackedFile
aPackedFile(aPackURL
);
2267 FileSharedPtr aBaseFile
= std::make_shared
<osl::File
>(aTempURL
);
2269 if (aPackedFile
.tryPush(aBaseFile
, mbCompress
))
2271 // reduce to allowed number and flush
2272 aPackedFile
.tryReduceToNumBackups(mnNumBackups
);
2273 aPackedFile
.flush();
2278 // delete temp file (in all cases)
2279 osl::File::remove(aTempURL
);
2283 bool BackupFileHelper::isPopPossible_extensionInfo(
2284 std::u16string_view rTargetURL
// target dir without trailing '/'
2287 // extensionInfo always exists internally, no test needed
2288 const OUString
aPackURL(createPackURL(rTargetURL
, u
"ExtensionInfo"));
2289 PackedFile
aPackedFile(aPackURL
);
2291 return !aPackedFile
.empty();
2294 bool BackupFileHelper::tryPop_extensionInfo(
2295 std::u16string_view rTargetURL
// target dir without trailing '/'
2298 // extensionInfo always exists internally, no test needed
2299 const OUString
aPackURL(createPackURL(rTargetURL
, u
"ExtensionInfo"));
2300 PackedFile
aPackedFile(aPackURL
);
2302 if (aPackedFile
.empty())
2305 oslFileHandle aHandle
;
2308 // open target temp file - it exists until deleted
2309 if (osl::File::E_None
!= osl::FileBase::createTempFile(nullptr, &aHandle
, &aTempURL
))
2312 bool bRetval(aPackedFile
.tryPop(aHandle
));
2314 // close temp file (in all cases) - it exists until deleted
2315 osl_closeFile(aHandle
);
2319 // last config is in temp file, load it to ExtensionInfo
2320 ExtensionInfo aLoadedExtensionInfo
;
2321 FileSharedPtr aBaseFile
= std::make_shared
<osl::File
>(aTempURL
);
2323 if (osl::File::E_None
== aBaseFile
->open(osl_File_OpenFlag_Read
))
2325 if (aLoadedExtensionInfo
.read_entries(aBaseFile
))
2327 // get current extension info, but from XML config files
2328 ExtensionInfo aCurrentExtensionInfo
;
2330 aCurrentExtensionInfo
.createUserExtensionRegistryEntriesFromXML(maUserConfigWorkURL
);
2332 // now we have loaded last_working (aLoadedExtensionInfo) and
2333 // current (aCurrentExtensionInfo) ExtensionInfo and may react on
2334 // differences by de/activating these as needed
2335 const ExtensionInfoEntryVector
& aUserEntries
= aCurrentExtensionInfo
.getExtensionInfoEntryVector();
2336 const ExtensionInfoEntryVector
& rLoadedVector
= aLoadedExtensionInfo
.getExtensionInfoEntryVector();
2337 ExtensionInfoEntryVector aToBeDisabled
;
2338 ExtensionInfoEntryVector aToBeEnabled
;
2340 for (const auto& rCurrentInfo
: aUserEntries
)
2342 const ExtensionInfoEntry
* pLoadedInfo
= nullptr;
2344 for (const auto& rLoadedInfo
: rLoadedVector
)
2346 if (rCurrentInfo
.isSameExtension(rLoadedInfo
))
2348 pLoadedInfo
= &rLoadedInfo
;
2353 if (nullptr != pLoadedInfo
)
2355 // loaded info contains information about the Extension rCurrentInfo
2356 const bool bCurrentEnabled(rCurrentInfo
.isEnabled());
2357 const bool bLoadedEnabled(pLoadedInfo
->isEnabled());
2359 if (bCurrentEnabled
&& !bLoadedEnabled
)
2361 aToBeDisabled
.push_back(rCurrentInfo
);
2363 else if (!bCurrentEnabled
&& bLoadedEnabled
)
2365 aToBeEnabled
.push_back(rCurrentInfo
);
2370 // There is no loaded info about the Extension rCurrentInfo.
2371 // It needs to be disabled
2372 if (rCurrentInfo
.isEnabled())
2374 aToBeDisabled
.push_back(rCurrentInfo
);
2379 if (!aToBeDisabled
.empty() || !aToBeEnabled
.empty())
2381 ExtensionInfo::changeEnableDisableStateInXML(maUserConfigWorkURL
, aToBeEnabled
, aToBeDisabled
);
2388 // reduce to allowed number and flush
2389 aPackedFile
.tryReduceToNumBackups(mnNumBackups
);
2390 aPackedFile
.flush();
2393 // delete temp file (in all cases - it may be moved already)
2394 osl::File::remove(aTempURL
);
2399 /////////////////// FileDirInfo helpers ///////////////////////
2401 void BackupFileHelper::fillDirFileInfo()
2403 if (!maDirs
.empty() || !maFiles
.empty())
2409 // Information about the configuration and the role/purpose of directories in
2410 // the UserConfiguration is taken from: https://wiki.documentfoundation.org/UserProfile
2412 // fill dir and file info list to work with dependent on work mode
2417 // simple mode: add just registrymodifications
2418 // (the orig file in maInitialBaseURL)
2419 maFiles
.insert(std::pair
< OUString
, OUString
>(maRegModName
, maExt
));
2424 // defined mode: Add a selection of dirs containing User-Defined and thus
2425 // valuable configuration information.
2426 // This is clearly discussable in every single point and may be adapted/corrected
2427 // over time. Main focus is to secure User-Defined/adapted values
2429 // add registrymodifications (the orig file in maInitialBaseURL)
2430 maFiles
.insert(std::pair
< OUString
, OUString
>(maRegModName
, maExt
));
2432 // User-defined substitution table (Tools/AutoCorrect)
2433 maDirs
.insert(u
"autocorr"_ustr
);
2435 // User-Defined AutoText (Edit/AutoText)
2436 maDirs
.insert(u
"autotext"_ustr
);
2438 // User-defined Macros
2439 maDirs
.insert(u
"basic"_ustr
);
2441 // User-adapted toolbars for modules
2442 maDirs
.insert(u
"config"_ustr
);
2444 // Initial and User-defined Databases
2445 maDirs
.insert(u
"database"_ustr
);
2447 // most part of registry files
2448 maDirs
.insert(u
"registry"_ustr
);
2450 // User-Defined Scripts
2451 maDirs
.insert(u
"Scripts"_ustr
);
2454 maDirs
.insert(u
"template"_ustr
);
2456 // Custom Dictionaries
2457 maDirs
.insert(u
"wordbook"_ustr
);
2459 // Questionable - where and how is Extension stuff held and how
2460 // does this interact with enabled/disabled states which are extra handled?
2461 // Keep out of business until deeper evaluated
2463 // maDirs.insert("extensions");
2464 // maDirs.insert("uno-packages");
2469 // whole directory. To do so, scan directory and exclude some dirs
2470 // from which we know they do not need to be secured explicitly. This
2471 // should already include registrymodifications, too.
2472 DirectoryHelper::scanDirsAndFiles(
2473 maUserConfigWorkURL
,
2477 // should not exist, but for the case an error occurred and it got
2478 // copied somehow, avoid further recursive copying/saving
2479 maDirs
.erase(u
"SafeMode"_ustr
);
2481 // not really needed, can be abandoned
2482 maDirs
.erase(u
"psprint"_ustr
);
2484 // not really needed, can be abandoned
2485 maDirs
.erase(u
"store"_ustr
);
2487 // not really needed, can be abandoned
2488 maDirs
.erase(u
"temp"_ustr
);
2490 // exclude own backup dir to avoid recursion
2491 maDirs
.erase(u
"pack"_ustr
);
2499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */