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