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