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