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