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