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