summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--comphelper/source/misc/backupfilehelper.cxx766
-rw-r--r--configmgr/source/components.cxx29
-rw-r--r--desktop/source/app/app.cxx144
-rw-r--r--desktop/source/app/app.cxx.orig2735
-rw-r--r--include/comphelper/backupfilehelper.hxx10
5 files changed, 3490 insertions, 194 deletions
diff --git a/comphelper/source/misc/backupfilehelper.cxx b/comphelper/source/misc/backupfilehelper.cxx
index 8b842deee4d9..f45221351710 100644
--- a/comphelper/source/misc/backupfilehelper.cxx
+++ b/comphelper/source/misc/backupfilehelper.cxx
@@ -8,15 +8,25 @@
*/
#include <sal/config.h>
-
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
#include <rtl/ustring.hxx>
#include <rtl/bootstrap.hxx>
#include <comphelper/backupfilehelper.hxx>
#include <rtl/crc.h>
#include <algorithm>
#include <deque>
+#include <vector>
#include <zlib.h>
+using namespace css;
typedef std::shared_ptr< osl::File > FileSharedPtr;
static const sal_uInt32 BACKUP_FILE_HELPER_BLOCK_SIZE = 16384;
@@ -84,6 +94,403 @@ namespace
return nCrc32;
}
+
+ bool read_sal_uInt32(FileSharedPtr& rFile, sal_uInt32& rTarget)
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseRead(0);
+
+ // read rTarget
+ if (osl::File::E_None == rFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
+ {
+ rTarget = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
+ return true;
+ }
+
+ return false;
+ }
+
+ bool write_sal_uInt32(oslFileHandle& rHandle, sal_uInt32 nSource)
+ {
+ sal_uInt8 aArray[4];
+ sal_uInt64 nBaseWritten(0);
+
+ // write nSource
+ aArray[0] = sal_uInt8((nSource & 0xff000000) >> 24);
+ aArray[1] = sal_uInt8((nSource & 0x00ff0000) >> 16);
+ aArray[2] = sal_uInt8((nSource & 0x0000ff00) >> 8);
+ aArray[3] = sal_uInt8(nSource & 0x000000ff);
+
+ if (osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool read_OString(FileSharedPtr& rFile, OString& rTarget)
+ {
+ sal_uInt32 nLength(0);
+
+ if (!read_sal_uInt32(rFile, nLength))
+ {
+ return false;
+ }
+
+ std::vector< sal_Char > aTarget(nLength);
+ sal_uInt64 nBaseRead(0);
+
+ // read rTarget
+ if (osl::File::E_None == rFile->read(static_cast<void*>(&aTarget[0]), nLength, nBaseRead) && nLength == nBaseRead)
+ {
+ rTarget = OString(&aTarget[0], static_cast< sal_Int32 >(nLength));
+ return true;
+ }
+
+ return false;
+ }
+
+ bool write_OString(oslFileHandle& rHandle, const OString& rSource)
+ {
+ const sal_uInt32 nLength(rSource.getLength());
+
+ if (!write_sal_uInt32(rHandle, nLength))
+ {
+ return false;
+ }
+
+ sal_uInt64 nBaseWritten(0);
+
+ if (osl_File_E_None == osl_writeFile(rHandle, static_cast<const void*>(rSource.getStr()), nLength, &nBaseWritten) && nLength == nBaseWritten)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ bool fileExists(const OUString& rBaseURL)
+ {
+ if (!rBaseURL.isEmpty())
+ {
+ FileSharedPtr aBaseFile(new osl::File(rBaseURL));
+
+ return (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read));
+ }
+
+ return false;
+ }
+}
+
+namespace
+{
+ enum PackageState { REGISTERED, NOT_REGISTERED, AMBIGUOUS, NOT_AVAILABLE };
+
+ class ExtensionInfoEntry
+ {
+ private:
+ PackageState meState; // REGISTERED, NOT_REGISTERED, AMBIGUOUS, NOT_AVAILABLE
+ OString maRepositoryName; // user|shared|bundled
+ OString maName;
+ OString maIdentifier;
+ OString maVersion;
+
+ public:
+ ExtensionInfoEntry()
+ : meState(NOT_AVAILABLE),
+ maRepositoryName(),
+ maName(),
+ maIdentifier(),
+ maVersion()
+ {
+ }
+
+ ExtensionInfoEntry(const uno::Reference< deployment::XPackage >& rxPackage)
+ : meState(NOT_AVAILABLE),
+ maRepositoryName(OUStringToOString(rxPackage->getRepositoryName(), RTL_TEXTENCODING_ASCII_US)),
+ maName(OUStringToOString(rxPackage->getName(), RTL_TEXTENCODING_ASCII_US)),
+ maIdentifier(OUStringToOString(rxPackage->getIdentifier().Value, RTL_TEXTENCODING_ASCII_US)),
+ maVersion(OUStringToOString(rxPackage->getVersion(), RTL_TEXTENCODING_ASCII_US))
+ {
+ const beans::Optional< beans::Ambiguous< sal_Bool > > option(
+ rxPackage->isRegistered(uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >()));
+
+ if (option.IsPresent)
+ {
+ ::beans::Ambiguous< sal_Bool > const& reg = option.Value;
+
+ if (reg.IsAmbiguous)
+ {
+ meState = AMBIGUOUS;
+ }
+ else
+ {
+ meState = reg.Value ? REGISTERED : NOT_REGISTERED;
+ }
+ }
+ else
+ {
+ meState = NOT_AVAILABLE;
+ }
+ }
+
+ bool operator<(const ExtensionInfoEntry& rComp) const
+ {
+ if (0 == maRepositoryName.compareTo(rComp.maRepositoryName))
+ {
+ if (0 == maName.compareTo(rComp.maName))
+ {
+ if (0 == maVersion.compareTo(rComp.maVersion))
+ {
+ if (0 == maIdentifier.compareTo(rComp.maIdentifier))
+ {
+ return meState < rComp.meState;
+ }
+ else
+ {
+ return 0 > maIdentifier.compareTo(rComp.maIdentifier);
+ }
+ }
+ else
+ {
+ return 0 > maVersion.compareTo(rComp.maVersion);
+ }
+ }
+ else
+ {
+ return 0 > maName.compareTo(rComp.maName);
+ }
+ }
+ else
+ {
+ return 0 > maRepositoryName.compareTo(rComp.maRepositoryName);
+ }
+ }
+
+ bool read_entry(FileSharedPtr& rFile)
+ {
+ // read meState
+ sal_uInt32 nState(0);
+
+ if (read_sal_uInt32(rFile, nState))
+ {
+ meState = static_cast< PackageState >(nState);
+ }
+ else
+ {
+ return false;
+ }
+
+ // read maRepositoryName;
+ if (!read_OString(rFile, maRepositoryName))
+ {
+ return false;
+ }
+
+ // read maName;
+ if (!read_OString(rFile, maName))
+ {
+ return false;
+ }
+
+ // read maIdentifier;
+ if (!read_OString(rFile, maIdentifier))
+ {
+ return false;
+ }
+
+ // read maVersion;
+ if (!read_OString(rFile, maVersion))
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool write_entry(oslFileHandle& rHandle) const
+ {
+ // write meState
+ const sal_uInt32 nState(meState);
+
+ if (!write_sal_uInt32(rHandle, nState))
+ {
+ return false;
+ }
+
+ // write maRepositoryName
+ if (!write_OString(rHandle, maRepositoryName))
+ {
+ return false;
+ }
+
+ // write maName;
+ if (!write_OString(rHandle, maName))
+ {
+ return false;
+ }
+
+ // write maIdentifier;
+ if (!write_OString(rHandle, maIdentifier))
+ {
+ return false;
+ }
+
+ // write maVersion;
+ if (!write_OString(rHandle, maVersion))
+ {
+ return false;
+ }
+
+ return true;
+ }
+ };
+
+ typedef ::std::vector< ExtensionInfoEntry > ExtensionInfoEntryVector;
+
+ class ExtensionInfo
+ {
+ private:
+ ExtensionInfoEntryVector maEntries;
+
+ public:
+ ExtensionInfo()
+ : maEntries()
+ {
+ }
+
+ void reset()
+ {
+ // clear all data
+ maEntries.clear();
+ }
+
+ void createCurrent()
+ {
+ // clear all data
+ reset();
+
+ // create content from current extension configuration
+ uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
+ uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ uno::Reference< deployment::XExtensionManager > m_xExtensionManager = deployment::ExtensionManager::get(xContext);
+
+ try
+ {
+ xAllPackages = m_xExtensionManager->getAllExtensions(uno::Reference< task::XAbortChannel >(),
+ uno::Reference< ucb::XCommandEnvironment >());
+ }
+ catch (const deployment::DeploymentException &)
+ {
+ return;
+ }
+ catch (const ucb::CommandFailedException &)
+ {
+ return;
+ }
+ catch (const ucb::CommandAbortedException &)
+ {
+ return;
+ }
+ catch (const lang::IllegalArgumentException & e)
+ {
+ throw uno::RuntimeException(e.Message, e.Context);
+ }
+
+ for (sal_Int32 i = 0; i < xAllPackages.getLength(); ++i)
+ {
+ uno::Sequence< uno::Reference< deployment::XPackage > > xPackageList = xAllPackages[i];
+
+ for (sal_Int32 j = 0; j < xPackageList.getLength(); ++j)
+ {
+ uno::Reference< deployment::XPackage > xPackage = xPackageList[j];
+
+ if (xPackage.is())
+ {
+ maEntries.push_back(ExtensionInfoEntry(xPackage));
+ }
+ }
+ }
+
+ if (!maEntries.empty())
+ {
+ // sort the list
+ std::sort(maEntries.begin(), maEntries.end());
+ }
+ }
+
+ bool read_entries(FileSharedPtr& rFile)
+ {
+ // read NumExtensionEntries
+ sal_uInt32 nExtEntries(0);
+
+ if (!read_sal_uInt32(rFile, nExtEntries))
+ {
+ return false;
+ }
+
+ for (sal_uInt32 a(0); a < nExtEntries; a++)
+ {
+ ExtensionInfoEntry aNewEntry;
+
+ if (aNewEntry.read_entry(rFile))
+ {
+ maEntries.push_back(aNewEntry);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool write_entries(oslFileHandle& rHandle) const
+ {
+ const sal_uInt32 nExtEntries(maEntries.size());
+
+ if (!write_sal_uInt32(rHandle, nExtEntries))
+ {
+ return false;
+ }
+
+ for (const auto& a : maEntries)
+ {
+ if (!a.write_entry(rHandle))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool createTempFile(OUString& rTempFileName)
+ {
+ oslFileHandle aHandle;
+ bool bRetval(false);
+
+ // create current configuration
+ if (maEntries.empty())
+ {
+ createCurrent();
+ }
+
+ // open target temp file and write current configuration to it - it exists until deleted
+ if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &rTempFileName))
+ {
+ bRetval = write_entries(aHandle);
+
+ // close temp file - it exists until deleted
+ osl_closeFile(aHandle);
+ }
+
+ return bRetval;
+ }
+ };
}
namespace
@@ -91,12 +498,12 @@ namespace
class PackedFileEntry
{
private:
- sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file
- sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not)
- sal_uInt32 mnOffset; // offset in File (zero identifies new file)
- sal_uInt32 mnCrc32; // checksum
- FileSharedPtr maFile; // file where to find the data (at offset)
- bool mbDoCompress; // flag if this file is scheduled to be compredded when written
+ sal_uInt32 mnFullFileSize; // size in bytes of unpacked original file
+ sal_uInt32 mnPackFileSize; // size in bytes in file backup package (smaller if compressed, same if not)
+ sal_uInt32 mnOffset; // offset in File (zero identifies new file)
+ sal_uInt32 mnCrc32; // checksum
+ FileSharedPtr maFile; // file where to find the data (at offset)
+ bool mbDoCompress; // flag if this file is scheduled to be compredded when written
bool copy_content_straight(oslFileHandle& rTargetHandle)
{
@@ -336,93 +743,67 @@ namespace
return mnOffset;
}
+ void setOffset(sal_uInt32 nOffset)
+ {
+ mnOffset = nOffset;
+ }
+
+ static sal_uInt32 getEntrySize()
+ {
+ return 12;
+ }
+
sal_uInt32 getCrc32() const
{
return mnCrc32;
}
- bool read_header(
- FileSharedPtr& rFile,
- sal_uInt32 nOffset)
+ bool read_header(FileSharedPtr& rFile)
{
- mnOffset = nOffset;
- maFile = rFile;
-
- if (maFile)
+ if (!rFile)
{
- sal_uInt8 aArray[4];
- sal_uInt64 nBaseRead(0);
+ return false;
+ }
- // read and compute full file size
- if (osl::File::E_None == maFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
- {
- mnFullFileSize = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
- }
- else
- {
- return false;
- }
+ maFile = rFile;
- // read and compute entry crc32
- if (osl::File::E_None == maFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
- {
- mnCrc32 = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
- }
- else
- {
- return false;
- }
+ // read and compute full file size
+ if (!read_sal_uInt32(rFile, mnFullFileSize))
+ {
+ return false;
+ }
- // read and compute packed size
- if (osl::File::E_None == maFile->read(static_cast<void*>(aArray), 4, nBaseRead) && 4 == nBaseRead)
- {
- mnPackFileSize = (sal_uInt32(aArray[0]) << 24) + (sal_uInt32(aArray[1]) << 16) + (sal_uInt32(aArray[2]) << 8) + sal_uInt32(aArray[3]);
- }
- else
- {
- return false;
- }
+ // read and compute entry crc32
+ if (!read_sal_uInt32(rFile, mnCrc32))
+ {
+ return false;
+ }
- return true;
+ // read and compute packed size
+ if (!read_sal_uInt32(rFile, mnPackFileSize))
+ {
+ return false;
}
- return false;
+ return true;
}
- bool write_header(oslFileHandle& rHandle)
+ bool write_header(oslFileHandle& rHandle) const
{
- sal_uInt8 aArray[4];
- sal_uInt64 nBaseWritten(0);
-
// write full file size
- aArray[0] = sal_uInt8((mnFullFileSize & 0xff000000) >> 24);
- aArray[1] = sal_uInt8((mnFullFileSize & 0x00ff0000) >> 16);
- aArray[2] = sal_uInt8((mnFullFileSize & 0x0000ff00) >> 8);
- aArray[3] = sal_uInt8(mnFullFileSize & 0x000000ff);
-
- if (osl_File_E_None != osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) || 4 != nBaseWritten)
+ if (!write_sal_uInt32(rHandle, mnFullFileSize))
{
return false;
}
// write crc32
- aArray[0] = sal_uInt8((mnCrc32 & 0xff000000) >> 24);
- aArray[1] = sal_uInt8((mnCrc32 & 0x00ff0000) >> 16);
- aArray[2] = sal_uInt8((mnCrc32 & 0x0000ff00) >> 8);
- aArray[3] = sal_uInt8(mnCrc32 & 0x000000ff);
-
- if (osl_File_E_None != osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) || 4 != nBaseWritten)
+ if (!write_sal_uInt32(rHandle, mnCrc32))
{
return false;
}
// write packed file size
- aArray[0] = sal_uInt8((mnPackFileSize & 0xff000000) >> 24);
- aArray[1] = sal_uInt8((mnPackFileSize & 0x00ff0000) >> 16);
- aArray[2] = sal_uInt8((mnPackFileSize & 0x0000ff00) >> 8);
- aArray[3] = sal_uInt8(mnPackFileSize & 0x000000ff);
-
- if (osl_File_E_None != osl_writeFile(rHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) || 4 != nBaseWritten)
+ if (!write_sal_uInt32(rHandle, mnPackFileSize))
{
return false;
}
@@ -509,23 +890,16 @@ namespace
// if there are entries (and less than max), read them
if (nEntries >= 1 && nEntries <= 10)
{
- // offset in souce file starts with 8 Byte for header + numEntries and
- // 12 byte for each entry (size, crc32 and PackedSize)
- sal_uInt32 nOffset(8 + (12 * nEntries));
-
for (sal_uInt32 a(0); a < nEntries; a++)
{
// create new entry, read header (size, crc and PackedSize),
// set offset and source file
PackedFileEntry aEntry;
- if (aEntry.read_header(aSourceFile, nOffset))
+ if (aEntry.read_header(aSourceFile))
{
// add to local data
maPackedFileEntryVector.push_back(aEntry);
-
- // increase offset for next entry
- nOffset += aEntry.getPackFileSize();
}
else
{
@@ -539,6 +913,21 @@ namespace
// on read error clear local data
maPackedFileEntryVector.clear();
}
+ else
+ {
+ // calculate and set offsets to file binary content
+ sal_uInt32 nHeaderSize(8);
+
+ nHeaderSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
+
+ sal_uInt32 nOffset(nHeaderSize);
+
+ for (auto& b : maPackedFileEntryVector)
+ {
+ b.setOffset(nOffset);
+ nOffset += b.getPackFileSize();
+ }
+ }
}
}
}
@@ -571,7 +960,7 @@ namespace
oslFileHandle aHandle;
OUString aTempURL;
- // open target temp file
+ // open target temp file - it exists until deleted
if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
{
sal_uInt8 aArray[4];
@@ -586,27 +975,25 @@ namespace
if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten)
{
const sal_uInt32 nSize(maPackedFileEntryVector.size());
- aArray[0] = sal_uInt8((nSize & 0xff000000) >> 24);
- aArray[1] = sal_uInt8((nSize & 0x00ff0000) >> 16);
- aArray[2] = sal_uInt8((nSize & 0x0000ff00) >> 8);
- aArray[3] = sal_uInt8(nSize & 0x000000ff);
// write number of entries
- if (osl_File_E_None == osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) && 4 == nBaseWritten)
+ if (write_sal_uInt32(aHandle, nSize))
{
if (bRetval)
{
// write placeholder for headers. Due to the fact that
// PackFileSize for newly added files gets set during
// writing the content entry, write headers after content
- // is written. To do so, write placeholders here. We know
- // the number of entries to write
- const sal_uInt32 nWriteSize(3 * maPackedFileEntryVector.size());
+ // is written. To do so, write placeholders here
+ sal_uInt32 nWriteSize(0);
+
+ nWriteSize += maPackedFileEntryVector.size() * PackedFileEntry::getEntrySize();
+
aArray[0] = aArray[1] = aArray[2] = aArray[3] = 0;
for (sal_uInt32 a(0); bRetval && a < nWriteSize; a++)
{
- if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 4, &nBaseWritten) || 4 != nBaseWritten)
+ if (osl_File_E_None != osl_writeFile(aHandle, static_cast<const void*>(aArray), 1, &nBaseWritten) || 1 != nBaseWritten)
{
bRetval = false;
}
@@ -653,7 +1040,7 @@ namespace
}
}
- // close temp file (in all cases)
+ // close temp file (in all cases) - it exists until deleted
osl_closeFile(aHandle);
if (bRetval)
@@ -690,11 +1077,17 @@ namespace
bool bNeedToAdd(false);
sal_uInt32 nCrc32(0);
- if (!maPackedFileEntryVector.empty())
+ if (maPackedFileEntryVector.empty())
+ {
+ // no backup yet, add as 1st backup
+ bNeedToAdd = true;
+ }
+ else
{
// already backups there, check if different from last entry
const PackedFileEntry& aLastEntry = maPackedFileEntryVector.back();
+ // check if file is different
if (aLastEntry.getFullFileSize() != static_cast<sal_uInt32>(nFileSize))
{
// different size, different file
@@ -712,11 +1105,6 @@ namespace
}
}
}
- else
- {
- // no backup yet, add
- bNeedToAdd = true;
- }
if (bNeedToAdd)
{
@@ -829,16 +1217,89 @@ namespace comphelper
return bRetval;
}
- rtl::OUString BackupFileHelper::getName()
+ bool BackupFileHelper::tryPush(bool bCompress)
{
- return OUString(maBase + "/." + maName + ".pack");
+ bool bDidPush(false);
+
+ if (splitBaseURL())
+ {
+ // ensure directory existence
+ osl::Directory::createPath(getPackDirName());
+
+ // try push for base file (usually registrymodifications)
+ bDidPush = tryPush_basefile(bCompress);
+
+ // Try Push of ExtensionInfo
+ bDidPush |= tryPush_extensionInfo(bCompress);
+ }
+
+ return bDidPush;
}
- bool BackupFileHelper::tryPush(bool bCompress)
+ bool BackupFileHelper::isPopPossible()
+ {
+ bool bPopPossible(false);
+
+ if (splitBaseURL())
+ {
+ // try for base file (usually registrymodifications)
+ bPopPossible = isPopPossible_basefile();
+
+ // try for ExtensionInfo
+ bPopPossible |= isPopPossible_extensionInfo();
+ }
+
+ return bPopPossible;
+ }
+
+ bool BackupFileHelper::tryPop()
+ {
+ bool bDidPop(false);
+
+ if (splitBaseURL())
+ {
+ // try for base file (usually registrymodifications)
+ bDidPop = tryPop_basefile();
+
+ // try for ExtensionInfo
+ bDidPop |= tryPop_extensionInfo();
+
+ if (bDidPop)
+ {
+ // try removal of evtl. empty directory
+ osl::Directory::remove(getPackDirName());
+ }
+ }
+
+ return bDidPop;
+ }
+
+ bool BackupFileHelper::splitBaseURL()
+ {
+ if (maBase.isEmpty() && !mrBaseURL.isEmpty())
+ {
+ // split URL at extension and at last path separator
+ maBase = splitAtLastToken(splitAtLastToken(mrBaseURL, '.', maExt), '/', maName);
+ }
+
+ return !maBase.isEmpty() && !maName.isEmpty();
+ }
+
+ const rtl::OUString BackupFileHelper::getPackDirName() const
+ {
+ return rtl::OUString(maBase + "/pack");
+ }
+
+ const rtl::OUString BackupFileHelper::getPackFileName(const rtl::OUString& rFileName) const
+ {
+ return rtl::OUString(getPackDirName() + "/" + rFileName + ".pack");
+ }
+
+ bool BackupFileHelper::tryPush_basefile(bool bCompress)
{
- if (splitBaseURL() && baseFileExists())
+ if (fileExists(mrBaseURL))
{
- PackedFile aPackedFile(getName());
+ PackedFile aPackedFile(getPackFileName(maName));
FileSharedPtr aBaseFile(new osl::File(mrBaseURL));
if (aPackedFile.tryPush(aBaseFile, bCompress))
@@ -854,11 +1315,37 @@ namespace comphelper
return false;
}
- bool BackupFileHelper::isPopPossible()
+ bool BackupFileHelper::tryPush_extensionInfo(bool bCompress)
+ {
+ ExtensionInfo aExtensionInfo;
+ OUString aTempURL;
+ bool bRetval(false);
+
+ // create current configuration and write to temp file - it exists until deleted
+ if (aExtensionInfo.createTempFile(aTempURL))
+ {
+ PackedFile aPackedFile(getPackFileName("ExtensionInfo"));
+ FileSharedPtr aBaseFile(new osl::File(aTempURL));
+
+ if (aPackedFile.tryPush(aBaseFile, bCompress))
+ {
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+ bRetval = true;
+ }
+ }
+
+ // delete temp file (in all cases)
+ osl::File::remove(aTempURL);
+ return bRetval;
+ }
+
+ bool BackupFileHelper::isPopPossible_basefile()
{
- if (splitBaseURL() && baseFileExists())
+ if (fileExists(mrBaseURL))
{
- PackedFile aPackedFile(getName());
+ PackedFile aPackedFile(getPackFileName(maName));
return !aPackedFile.empty();
}
@@ -866,23 +1353,32 @@ namespace comphelper
return false;
}
- bool BackupFileHelper::tryPop()
+ bool BackupFileHelper::isPopPossible_extensionInfo()
+ {
+ // extensionInfo always exists internally, no test needed
+ PackedFile aPackedFile(getPackFileName("ExtensionInfo"));
+
+ return !aPackedFile.empty();
+ }
+
+ bool BackupFileHelper::tryPop_basefile()
{
- if (splitBaseURL() && baseFileExists())
+ if (fileExists(mrBaseURL))
{
- PackedFile aPackedFile(getName());
+ // try Pop for base file (usually registrymodifications)
+ PackedFile aPackedFile(getPackFileName(maName));
if (!aPackedFile.empty())
{
oslFileHandle aHandle;
OUString aTempURL;
- // open target temp file
+ // open target temp file - it exists until deleted
if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
{
bool bRetval(aPackedFile.tryPop(aHandle));
- // close temp file (in all cases)
+ // close temp file (in all cases) - it exists until deleted
osl_closeFile(aHandle);
if (bRetval)
@@ -908,24 +1404,58 @@ namespace comphelper
return false;
}
- bool BackupFileHelper::splitBaseURL()
+ bool BackupFileHelper::tryPop_extensionInfo()
{
- if (maBase.isEmpty() && !mrBaseURL.isEmpty())
+ // extensionInfo always exists internally, no test needed
+ PackedFile aPackedFile(getPackFileName("ExtensionInfo"));
+
+ if (!aPackedFile.empty())
{
- // split URL at extension and at last path separator
- maBase = splitAtLastToken(splitAtLastToken(mrBaseURL, '.', maExt), '/', maName);
- }
+ oslFileHandle aHandle;
+ OUString aTempURL;
- return !maBase.isEmpty() && !maName.isEmpty();
- }
+ // open target temp file - it exists until deleted
+ if (osl::File::E_None == osl::FileBase::createTempFile(nullptr, &aHandle, &aTempURL))
+ {
+ bool bRetval(aPackedFile.tryPop(aHandle));
- bool BackupFileHelper::baseFileExists()
- {
- if (!mrBaseURL.isEmpty())
- {
- FileSharedPtr aBaseFile(new osl::File(mrBaseURL));
+ // close temp file (in all cases) - it exists until deleted
+ osl_closeFile(aHandle);
- return (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read));
+ if (bRetval)
+ {
+ // last config is in temp file, load it to ExtensionInfo
+ ExtensionInfo aLoadedExtensionInfo;
+ FileSharedPtr aBaseFile(new osl::File(aTempURL));
+
+ if (osl::File::E_None == aBaseFile->open(osl_File_OpenFlag_Read))
+ {
+ if (aLoadedExtensionInfo.read_entries(aBaseFile))
+ {
+ ExtensionInfo aCurrentExtensionInfo;
+
+ aCurrentExtensionInfo.createCurrent();
+
+ // now we have loaded and current ExtensionInfo and may react on differences
+
+
+
+
+
+ bRetval = true;
+ }
+ }
+
+ // reduce to allowed number and flush
+ aPackedFile.tryReduceToNumBackups(mnNumBackups);
+ aPackedFile.flush();
+ }
+
+ // delete temp file (in all cases - it may be moved already)
+ osl::File::remove(aTempURL);
+
+ return bRetval;
+ }
}
return false;
diff --git a/configmgr/source/components.cxx b/configmgr/source/components.cxx
index 17f09a3c0c72..9072da43e844 100644
--- a/configmgr/source/components.cxx
+++ b/configmgr/source/components.cxx
@@ -613,18 +613,22 @@ Components::Components(
Components::~Components()
{
- // get flag if _exit was already called which is a sign to not to secure user config
+ // get flag if _exit was already called which is a sign to not secure user config.
+ // this is used for win only currently where calling _exit() unfortunately still
+ // calls destructors (what is not wanted). May be needed for other systems, too
+ // (unknown yet) but can do no harm
const bool bExitWasCalled(comphelper::BackupFileHelper::getExitWasCalled());
#ifndef WNT
// we can add a SAL_WARN here for other systems where the destructor gets called after
- // an _exit() call - which should not happen. Still safe - the getExitWasCalled() is
- // used, but a hint that _exit behaves different on a system
+ // an _exit() call. Still safe - the getExitWasCalled() is used, but a hint that _exit
+ // behaves different on a system
SAL_WARN_IF(bExitWasCalled, "configmgr", "Components::~Components() called after _exit() call");
#endif
if (bExitWasCalled)
{
+ // do not write, re-join thereads
osl::MutexGuard g(*lock_);
if (writeThread_.is())
@@ -634,30 +638,13 @@ Components::~Components()
}
else
{
+ // write changes
flushModifications();
}
for (WeakRootSet::iterator i(roots_.begin()); i != roots_.end(); ++i) {
(*i)->setAlive(false);
}
-
- if (!bExitWasCalled &&
- ModificationTarget::File == modificationTarget_ &&
- !modificationFileUrl_.isEmpty())
- {
- // test backup of registrymodifications
- sal_uInt16 nSecureUserConfigNumCopies(0);
-
- // read configuration from soffice.ini
- const bool bSecureUserConfig(comphelper::BackupFileHelper::getSecureUserConfig(nSecureUserConfigNumCopies));
-
- if (bSecureUserConfig)
- {
- comphelper::BackupFileHelper aBackupFileHelper(modificationFileUrl_, nSecureUserConfigNumCopies);
-
- aBackupFileHelper.tryPush();
- }
- }
}
void Components::parseFileLeniently(
diff --git a/desktop/source/app/app.cxx b/desktop/source/app/app.cxx
index 69da758aac42..b9e5401c7246 100644
--- a/desktop/source/app/app.cxx
+++ b/desktop/source/app/app.cxx
@@ -818,6 +818,42 @@ OUString Desktop::CreateErrorMsgString(
return MakeStartupErrorMessage( aMsg );
}
+// helper method to test if SecureUserConfig is active, detect the num copies
+// and extract the User's config directory URL
+bool testSecureUserConfigActive(sal_uInt16& rnSecureUserConfigNumCopies, OUString& raUserConfigDir)
+{
+ // read configuration from soffice.ini
+ if(comphelper::BackupFileHelper::getSecureUserConfig(rnSecureUserConfigNumCopies))
+ {
+ // try to asccess user layer configuration file
+ OUString conf("${CONFIGURATION_LAYERS}");
+ rtl::Bootstrap::expandMacros(conf);
+ const OUString aTokenUser("user:");
+ sal_Int32 nStart(conf.indexOf(aTokenUser));
+
+ if (-1 != nStart)
+ {
+ nStart += aTokenUser.getLength();
+ sal_Int32 nEnd(conf.indexOf(' ', nStart));
+
+ if (-1 == nEnd)
+ {
+ nEnd = conf.getLength();
+ }
+
+ raUserConfigDir = conf.copy(nStart, nEnd - nStart);
+ raUserConfigDir.startsWith("!", &raUserConfigDir);
+ }
+
+ if (!raUserConfigDir.isEmpty())
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
void Desktop::HandleBootstrapErrors(
BootstrapError aBootstrapError, OUString const & aErrorMessage )
{
@@ -951,75 +987,50 @@ void Desktop::HandleBootstrapErrors(
}
else if ( aBootstrapError == BE_OFFICECONFIG_BROKEN )
{
- // test restore of registrymodifications
+ // test if SecureUserConfig is active
sal_uInt16 nSecureUserConfigNumCopies(0);
+ OUString aUserConfigDir;
bool bFireOriginalError(true);
- // read configuration from soffice.ini
- const bool bSecureUserConfig(comphelper::BackupFileHelper::getSecureUserConfig(nSecureUserConfigNumCopies));
-
- if (bSecureUserConfig)
+ if (testSecureUserConfigActive(nSecureUserConfigNumCopies, aUserConfigDir))
{
- // try to asccess user layer configuration file
- OUString conf("${CONFIGURATION_LAYERS}");
- rtl::Bootstrap::expandMacros(conf);
- const OUString aTokenUser("user:");
- sal_Int32 nStart(conf.indexOf(aTokenUser));
- OUString aUser;
+ comphelper::BackupFileHelper aBackupFileHelper(aUserConfigDir, nSecureUserConfigNumCopies);
- if (-1 != nStart)
+ if (aBackupFileHelper.isPopPossible())
{
- nStart += aTokenUser.getLength();
- sal_Int32 nEnd(conf.indexOf(' ', nStart));
-
- if (-1 == nEnd)
+ // for linux (and probably others?) we need to instantiate XDesktop2
+ // to be able to open a *.ui-file based dialog, so do this here locally.
+ // does no harm on win, so better always do this (in error case only anyways)
+ Reference< XComponentContext > xLocalContext = ::comphelper::getProcessComponentContext();
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xLocalContext);
+
+ ScopedVclPtrInstance< MessageDialog > aQueryShouldRestore(
+ Application::GetDefDialogParent(),
+ "QueryTryToRestoreConfigurationDialog",
+ "desktop/ui/querytrytorestoreconfigurationdialog.ui");
+
+ if (aQueryShouldRestore.get())
{
- nEnd = conf.getLength();
- }
-
- aUser = conf.copy(nStart, nEnd - nStart);
- aUser.startsWith("!", &aUser);
- }
+ if (!aErrorMessage.isEmpty())
+ {
+ OUString aPrimaryText(aQueryShouldRestore->get_primary_text());
- if (!aUser.isEmpty())
- {
- comphelper::BackupFileHelper aBackupFileHelper(aUser, nSecureUserConfigNumCopies);
+ aPrimaryText += "\n(\"" + aErrorMessage + "\")";
+ aQueryShouldRestore->set_primary_text(aPrimaryText);
+ }
- if (aBackupFileHelper.isPopPossible())
- {
- // for linux (and probably others?) we need to instantiate XDesktop2
- // to be able to open a *.ui-file based dialog, so do this here locally.
- // does no harm on win, so better always do this (in error case only anyways)
- Reference< XComponentContext > xLocalContext = ::comphelper::getProcessComponentContext();
- Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xLocalContext);
-
- ScopedVclPtrInstance< MessageDialog > aQueryShouldRestore(
- Application::GetDefDialogParent(),
- "QueryTryToRestoreConfigurationDialog",
- "desktop/ui/querytrytorestoreconfigurationdialog.ui");
-
- if (aQueryShouldRestore.get())
+ if (RET_YES == aQueryShouldRestore->Execute())
{
- if (!aErrorMessage.isEmpty())
- {
- OUString aPrimaryText(aQueryShouldRestore->get_primary_text());
-
- aPrimaryText += "\n(\"" + aErrorMessage + "\")";
- aQueryShouldRestore->set_primary_text(aPrimaryText);
- }
-
- if (RET_YES == aQueryShouldRestore->Execute())
- {
- aBackupFileHelper.tryPop();
- bFireOriginalError = false;
- }
+ aBackupFileHelper.tryPop();
+ bFireOriginalError = false;
}
}
}
}
// set flag at BackupFileHelper to be able to know if _exit was called and
- // actions are executed after this
+ // actions are executed after this. This method we are in will not return,
+ // but end up in a _exit() call
comphelper::BackupFileHelper::setExitWasCalled();
if (bFireOriginalError)
@@ -1836,7 +1847,34 @@ int Desktop::doShutdown()
// remove temp directory
RemoveTemporaryDirectory();
+
+ // flush evtl. configuration changes so that all config files in user
+ // dir are written
FlushConfiguration();
+
+ if (pExecGlobals->bRestartRequested)
+ {
+ // a restart is already requested, usually due to a configuration change
+ // that needs a restart to get active. If this is the case, do not try
+ // to use SecureUserConfig to safe this still untested new configuration
+ }
+ else
+ {
+ // Test if SecureUserConfig is active. If yes and we are at this point, regular shutdown
+ // is in progress and the currently used configuration was working. Try to secure this
+ // working configuration for later eventually necessary restores
+ sal_uInt16 nSecureUserConfigNumCopies(0);
+ OUString aUserConfigDir;
+
+ if (testSecureUserConfigActive(nSecureUserConfigNumCopies, aUserConfigDir))
+ {
+ // try to push registrymodifications.xcu
+ comphelper::BackupFileHelper aBackupFileHelper(aUserConfigDir, nSecureUserConfigNumCopies);
+
+ aBackupFileHelper.tryPush();
+ }
+ }
+
// The acceptors in the AcceptorMap must be released (in DeregisterServices)
// with the solar mutex unlocked, to avoid deadlock:
{
diff --git a/desktop/source/app/app.cxx.orig b/desktop/source/app/app.cxx.orig
new file mode 100644
index 000000000000..a9d10c3a99ba
--- /dev/null
+++ b/desktop/source/app/app.cxx.orig
@@ -0,0 +1,2735 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_features.h>
+#include <config_folders.h>
+
+#include <sal/config.h>
+
+#include <iostream>
+#if defined UNX
+#include <signal.h>
+#endif
+
+#include "app.hxx"
+#include "desktop.hrc"
+#include "cmdlineargs.hxx"
+#include "cmdlinehelp.hxx"
+#include "dispatchwatcher.hxx"
+#include "lockfile.hxx"
+#include "userinstall.hxx"
+#include "desktopcontext.hxx"
+#include "migration.hxx"
+
+#include <svl/languageoptions.hxx>
+#include <svtools/javacontext.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/frame/theAutoRecovery.hpp>
+#include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
+#include <com/sun/star/frame/SessionListener.hpp>
+#include <com/sun/star/frame/XSessionManagerListener.hpp>
+#include <com/sun/star/frame/XSynchronousDispatch.hpp>
+#include <com/sun/star/document/CorruptedFilterConfigurationException.hpp>
+#include <com/sun/star/configuration/CorruptedConfigurationException.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/util/XFlushable.hpp>
+#include <com/sun/star/util/XModifiable.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/StartModule.hpp>
+#include <com/sun/star/frame/XComponentLoader.hpp>
+#include <com/sun/star/view/XPrintable.hpp>
+#include <com/sun/star/awt/XTopWindow.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <com/sun/star/util/XURLTransformer.hpp>
+#include <com/sun/star/frame/XDispatchProvider.hpp>
+#include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
+#include <com/sun/star/configuration/MissingBootstrapFileException.hpp>
+#include <com/sun/star/configuration/InvalidBootstrapFileException.hpp>
+#include <com/sun/star/configuration/InstallationIncompleteException.hpp>
+#include <com/sun/star/configuration/backend/BackendSetupException.hpp>
+#include <com/sun/star/configuration/backend/BackendAccessException.hpp>
+#include <com/sun/star/task/theJobExecutor.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <com/sun/star/task/XRestartManager.hpp>
+#include <com/sun/star/document/XDocumentEventListener.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/ui/theUIElementFactoryManager.hpp>
+#include <com/sun/star/ui/theWindowStateConfiguration.hpp>
+#include <com/sun/star/frame/thePopupMenuControllerFactory.hpp>
+#include <com/sun/star/office/Quickstart.hpp>
+
+#include <desktop/exithelper.h>
+#include <sal/log.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <comphelper/configuration.hxx>
+#include <comphelper/fileurl.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/backupfilehelper.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <officecfg/Office/Recovery.hxx>
+#include <officecfg/Setup.hxx>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/uri.hxx>
+#include <unotools/pathoptions.hxx>
+#include <svtools/miscopt.hxx>
+#include <svtools/menuoptions.hxx>
+#include <rtl/bootstrap.hxx>
+#include <vcl/help.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/settings.hxx>
+#include <sfx2/sfx.hrc>
+#include <sfx2/app.hxx>
+#include <svl/itemset.hxx>
+#include <svl/eitem.hxx>
+#include <basic/sbstar.hxx>
+#include <desktop/crashreport.hxx>
+
+#include <svtools/fontsubstconfig.hxx>
+#include <svtools/accessibilityoptions.hxx>
+#include <svtools/apearcfg.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#include "langselect.hxx"
+
+#include <config_telepathy.h>
+
+#if ENABLE_TELEPATHY
+#include <tubes/manager.hxx>
+#endif
+
+#if HAVE_FEATURE_BREAKPAD
+#include <fstream>
+#endif
+
+#if defined MACOSX
+#include <errno.h>
+#include <sys/wait.h>
+#endif
+
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(push, 1) /* disable warnings within system headers */
+#pragma warning (disable: 4005)
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+#endif //WNT
+
+#if defined(_WIN32)
+#include <process.h>
+#define GETPID _getpid
+#else
+#include <unistd.h>
+#define GETPID getpid
+#endif
+
+using namespace ::com::sun::star::awt;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::system;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::ui::dialogs;
+using namespace ::com::sun::star::container;
+
+ResMgr* desktop::Desktop::pResMgr = nullptr;
+
+namespace desktop
+{
+
+static oslSignalHandler pSignalHandler = nullptr;
+
+namespace {
+
+#if HAVE_FEATURE_EXTENSIONS
+
+// Remove any existing UserInstallation's extensions cache data remaining from
+// old installations. This addresses at least two problems:
+//
+// For one, apparently due to the old share/prereg/bundled mechanism (disabled
+// since 5c47e5f63a79a9e72ec4a100786b1bbf65137ed4 "fdo#51252 Disable copying
+// share/prereg/bundled to avoid startup crashes"), the user/extensions/bundled
+// cache could contain corrupted information (like a UNO component registered
+// twice, which got changed from active to passive registration in one LO
+// version, but the version of the corresponding bundled extension only
+// incremented in a later LO version).
+//
+// For another, UserInstallations have been seen in the wild where no extensions
+// were installed per-user (any longer), but user/uno_packages/cache/registry/
+// com.sun.star.comp.deployment.component.PackageRegistryBackend/*.rdb files
+// contained data nevertheless.
+//
+// When a LO upgrade is detected (i.e., no user/extensions/buildid or one
+// containing an old build ID), then user/extensions and
+// user/uno_packages/cache/registry/
+// com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc are
+// removed. That should prevent any problems starting the service manager due
+// to old junk. Later on in Desktop::SynchronizeExtensionRepositories, the
+// removed cache data is recreated.
+//
+// Multiple instances of soffice.bin can execute this code in parallel for a
+// single UserInstallation, as it is called before RequestHandler is set up.
+// Therefore, any errors here only lead to SAL_WARNs.
+//
+// At least in theory, this function could be removed again once no
+// UserInstallation can be poisoned by old junk any more.
+bool cleanExtensionCache() {
+ OUString buildId(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(buildId); //TODO: detect failure
+ OUString extDir(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
+ ":UserInstallation}/user/extensions");
+ rtl::Bootstrap::expandMacros(extDir); //TODO: detect failure
+ OUString buildIdFile(extDir + "/buildid");
+ osl::File fr(buildIdFile);
+ osl::FileBase::RC rc = fr.open(osl_File_OpenFlag_Read);
+ switch (rc) {
+ case osl::FileBase::E_None:
+ {
+ rtl::ByteSequence s1;
+ rc = fr.readLine(s1);
+ osl::FileBase::RC rc2 = fr.close();
+ SAL_WARN_IF(
+ rc2 != osl::FileBase::E_None, "desktop.app",
+ "cannot close " << fr.getURL() << " after reading: " << +rc2);
+ // readLine returns E_AGAIN for a zero-size file:
+ if (rc != osl::FileBase::E_None && rc != osl::FileBase::E_AGAIN) {
+ SAL_WARN( "desktop.app", "cannot read from " << fr.getURL() << ": " << +rc);
+ break;
+ }
+ OUString s2(
+ reinterpret_cast< char const * >(s1.getConstArray()),
+ s1.getLength(), RTL_TEXTENCODING_ISO_8859_1);
+ // using ISO 8859-1 avoids any and all conversion errors; the
+ // content should only be a subset of ASCII, anyway
+ if (s2 == buildId) {
+ return false;
+ }
+ break;
+ }
+ case osl::FileBase::E_NOENT:
+ break;
+ default:
+ SAL_WARN( "desktop.app", "cannot open " << fr.getURL() << " for reading: " << +rc);
+ break;
+ }
+ utl::removeTree(extDir);
+ OUString userRcFile(
+ "$UNO_USER_PACKAGES_CACHE/registry/"
+ "com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc");
+ rtl::Bootstrap::expandMacros(userRcFile); //TODO: detect failure
+ rc = osl::File::remove(userRcFile);
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None && rc != osl::FileBase::E_NOENT, "desktop.app",
+ "cannot remove file " << userRcFile << ": " << +rc);
+ rc = osl::Directory::createPath(extDir);
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None && rc != osl::FileBase::E_EXIST, "desktop.app",
+ "cannot create path " << extDir << ": " << +rc);
+ osl::File fw(buildIdFile);
+ rc = fw.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+ if (rc != osl::FileBase::E_None) {
+ SAL_WARN( "desktop.app", "cannot open " << fw.getURL() << " for writing: " << +rc);
+ return true;
+ }
+ OString buf(OUStringToOString(buildId, RTL_TEXTENCODING_UTF8));
+ // using UTF-8 avoids almost all conversion errors (and buildid
+ // containing single surrogate halves should never happen, anyway); the
+ // content should only be a subset of ASCII, anyway
+ sal_uInt64 n = 0;
+ rc = fw.write(buf.getStr(), buf.getLength(), n);
+ SAL_WARN_IF(
+ (rc != osl::FileBase::E_None
+ || n != static_cast< sal_uInt32 >(buf.getLength())),
+ "desktop.app",
+ "cannot write to " << fw.getURL() << ": " << +rc << ", " << n);
+ rc = fw.close();
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None, "desktop.app",
+ "cannot close " << fw.getURL() << " after writing: " << +rc);
+ return true;
+}
+
+#endif
+
+bool shouldLaunchQuickstart()
+{
+ bool bQuickstart = Desktop::GetCommandLineArgs().IsQuickstart();
+ if (!bQuickstart)
+ {
+ const SfxPoolItem* pItem=nullptr;
+ SfxItemSet aQLSet(SfxGetpApp()->GetPool(), SID_ATTR_QUICKLAUNCHER, SID_ATTR_QUICKLAUNCHER);
+ SfxGetpApp()->GetOptions(aQLSet);
+ SfxItemState eState = aQLSet.GetItemState(SID_ATTR_QUICKLAUNCHER, false, &pItem);
+ if (SfxItemState::SET == eState)
+ bQuickstart = static_cast<const SfxBoolItem*>(pItem)->GetValue();
+ }
+ return bQuickstart;
+}
+
+void SetRestartState() {
+ try {
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Office::OfficeRestartInProgress::set(true, batch);
+ batch->commit();
+ } catch (css::uno::Exception & e) {
+ SAL_WARN("desktop.app", "ignoring Exception \"" << e.Message << "\"");
+ }
+}
+
+void DoRestartActionsIfNecessary(bool quickstart) {
+ if (quickstart) {
+ try {
+ if (officecfg::Setup::Office::OfficeRestartInProgress::get()) {
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Setup::Office::OfficeRestartInProgress::set(
+ false, batch);
+ batch->commit();
+ css::office::Quickstart::createStart(
+ comphelper::getProcessComponentContext(),
+ shouldLaunchQuickstart());
+ }
+ } catch (css::uno::Exception & e) {
+ SAL_WARN(
+ "desktop.app", "ignoring Exception \"" << e.Message << "\"");
+ }
+ }
+}
+
+}
+
+
+ResMgr* Desktop::GetDesktopResManager()
+{
+ if ( !Desktop::pResMgr )
+ {
+ // Create desktop resource manager and bootstrap process
+ // was successful. Use default way to get language specific message.
+ if ( Application::IsInExecute() )
+ Desktop::pResMgr = ResMgr::CreateResMgr("dkt");
+
+ if ( !Desktop::pResMgr )
+ {
+ // Use VCL to get the correct language specific message as we
+ // are in the bootstrap process and not able to get the installed
+ // language!!
+ OUString aUILocaleString = langselect::getEmergencyLocale();
+ LanguageTag aLanguageTag( aUILocaleString);
+ //! ResMgr may modify the Locale for fallback!
+ Desktop::pResMgr = ResMgr::SearchCreateResMgr( "dkt", aLanguageTag);
+ }
+ }
+
+ return Desktop::pResMgr;
+}
+
+namespace {
+
+
+// Get a message string securely. There is a fallback string if the resource
+// is not available.
+
+OUString GetMsgString(
+ sal_uInt16 nId, const OUString& aFallbackMsg,
+ bool bAlwaysUseFallbackMsg = false )
+{
+ if ( !bAlwaysUseFallbackMsg )
+ {
+ ResMgr* resMgr = Desktop::GetDesktopResManager();
+ if ( resMgr )
+ return ResId(nId, *resMgr).toString();
+ }
+ return aFallbackMsg;
+}
+
+OUString MakeStartupErrorMessage(
+ OUString const & aErrorMessage, bool bAlwaysUseFallbackMsg = false )
+{
+ OUStringBuffer aDiagnosticMessage( 100 );
+
+ aDiagnosticMessage.append(
+ GetMsgString(
+ STR_BOOTSTRAP_ERR_CANNOT_START, "The program cannot be started.",
+ bAlwaysUseFallbackMsg ) );
+
+ aDiagnosticMessage.append( "\n" );
+
+ aDiagnosticMessage.append( aErrorMessage );
+
+ return aDiagnosticMessage.makeStringAndClear();
+}
+
+OUString MakeStartupConfigAccessErrorMessage( OUString const & aInternalErrMsg )
+{
+ OUStringBuffer aDiagnosticMessage( 200 );
+
+ ResMgr* pResMgr = Desktop::GetDesktopResManager();
+ if ( pResMgr )
+ aDiagnosticMessage.append( ResId(STR_BOOTSTRAP_ERR_CFG_DATAACCESS, *pResMgr).toString() );
+ else
+ aDiagnosticMessage.append( "The program cannot be started." );
+
+ if ( !aInternalErrMsg.isEmpty() )
+ {
+ aDiagnosticMessage.append( "\n\n" );
+ if ( pResMgr )
+ aDiagnosticMessage.append( ResId(STR_INTERNAL_ERRMSG, *pResMgr).toString() );
+ else
+ aDiagnosticMessage.append( "The following internal error has occurred:\n\n" );
+ aDiagnosticMessage.append( aInternalErrMsg );
+ }
+
+ return aDiagnosticMessage.makeStringAndClear();
+}
+
+
+// shows a simple error box with the given message ... but exits from these process !
+// Fatal errors can't be solved by the process ... nor any recovery can help.
+// Mostly the installation was damaged and must be repaired manually .. or by calling
+// setup again.
+// On the other side we must make sure that no further actions will be possible within
+// the current office process ! No pipe requests, no menu/toolbar/shortcut actions
+// are allowed. Otherwise we will force a "crash inside a crash".
+// Thats why we have to use a special native message box here which does not use yield :-)
+
+void FatalError(const OUString& sMessage)
+{
+ OUString sProductKey = ::utl::Bootstrap::getProductKey();
+ if ( sProductKey.isEmpty())
+ {
+ osl_getExecutableFile( &sProductKey.pData );
+
+ ::sal_uInt32 nLastIndex = sProductKey.lastIndexOf('/');
+ if ( nLastIndex > 0 )
+ sProductKey = sProductKey.copy( nLastIndex+1 );
+ }
+
+ OUStringBuffer sTitle (128);
+ sTitle.append (sProductKey );
+ sTitle.append (" - Fatal Error");
+
+ Application::ShowNativeErrorBox (sTitle.makeStringAndClear (), sMessage);
+ _exit(EXITHELPER_FATAL_ERROR);
+}
+
+struct theCommandLineArgs : public rtl::Static< CommandLineArgs, theCommandLineArgs > {};
+
+}
+
+CommandLineArgs& Desktop::GetCommandLineArgs()
+{
+ return theCommandLineArgs::get();
+}
+
+namespace
+{
+ struct BrandName
+ : public rtl::Static< OUString, BrandName > {};
+ struct Version
+ : public rtl::Static< OUString, Version > {};
+ struct AboutBoxVersion
+ : public rtl::Static< OUString, AboutBoxVersion > {};
+ struct AboutBoxVersionSuffix
+ : public rtl::Static< OUString, AboutBoxVersionSuffix > {};
+ struct OOOVendor
+ : public rtl::Static< OUString, OOOVendor > {};
+ struct Extension
+ : public rtl::Static< OUString, Extension > {};
+}
+
+OUString ReplaceStringHookProc( const OUString& rStr )
+{
+ OUString sRet(rStr);
+
+ if (sRet.indexOf("%PRODUCT") != -1 || sRet.indexOf("%ABOUTBOX") != -1)
+ {
+ OUString sBrandName = BrandName::get();
+ OUString sVersion = Version::get();
+ OUString sBuildId = utl::Bootstrap::getBuildIdData("development");
+ OUString sAboutBoxVersion = AboutBoxVersion::get();
+ OUString sAboutBoxVersionSuffix = AboutBoxVersionSuffix::get();
+ OUString sExtension = Extension::get();
+
+ if ( sBrandName.isEmpty() )
+ {
+ sBrandName = utl::ConfigManager::getProductName();
+ sVersion = utl::ConfigManager::getProductVersion();
+ sAboutBoxVersion = utl::ConfigManager::getAboutBoxProductVersion();
+ sAboutBoxVersionSuffix = utl::ConfigManager::getAboutBoxProductVersionSuffix();
+ if ( sExtension.isEmpty() )
+ {
+ sExtension = utl::ConfigManager::getProductExtension();
+ }
+ }
+
+ sRet = sRet.replaceAll( "%PRODUCTNAME", sBrandName );
+ sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
+ sRet = sRet.replaceAll( "%BUILDID", sBuildId );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
+ sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
+ sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
+ }
+
+ if ( sRet.indexOf( "%OOOVENDOR" ) != -1 )
+ {
+ OUString sOOOVendor = OOOVendor::get();
+
+ if ( sOOOVendor.isEmpty() )
+ {
+ sOOOVendor = utl::ConfigManager::getVendor();
+ }
+
+ sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
+ }
+
+ return sRet;
+}
+
+Desktop::Desktop()
+ : m_bCleanedExtensionCache(false)
+ , m_bServicesRegistered(false)
+ , m_aBootstrapError(BE_OK)
+ , m_aBootstrapStatus(BS_OK)
+{
+}
+
+Desktop::~Desktop()
+{
+#if ENABLE_TELEPATHY
+ TeleManager::finalize();
+#endif
+}
+
+void Desktop::Init()
+{
+ SetBootstrapStatus(BS_OK);
+
+#if HAVE_FEATURE_EXTENSIONS
+ m_bCleanedExtensionCache = cleanExtensionCache();
+#endif
+
+ // We need to have service factory before going further, but see fdo#37195.
+ // Doing this will mmap common.rdb, making it not overwritable on windows,
+ // so this can't happen before the synchronization above. Lets rework this
+ // so that the above is called *from* CreateApplicationServiceManager or
+ // something to enforce this gotcha
+ try
+ {
+ InitApplicationServiceManager();
+ }
+ catch (css::uno::Exception & e)
+ {
+ SetBootstrapError( BE_UNO_SERVICEMANAGER, e.Message );
+ }
+
+ if ( m_aBootstrapError == BE_OK )
+ {
+ try
+ {
+ if (!langselect::prepareLocale())
+ {
+ SetBootstrapError( BE_LANGUAGE_MISSING, OUString() );
+ }
+ }
+ catch (css::uno::Exception & e)
+ {
+ SetBootstrapError( BE_OFFICECONFIG_BROKEN, e.Message );
+ }
+
+ // test code for ProfileSafeMode to allow testing the fail
+ // of loading the office configuration initially. To use,
+ // either set to true and compile, or set a breakpoint
+ // in debugger and change the local bool
+ static bool bTryHardOfficeconfigBroken(false);
+
+ if (bTryHardOfficeconfigBroken)
+ {
+ SetBootstrapError(BE_OFFICECONFIG_BROKEN, OUString());
+ }
+ }
+
+ if ( true )
+ {
+ const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+
+ // start ipc thread only for non-remote offices
+ RequestHandler::Status aStatus = RequestHandler::Enable(true);
+ if ( aStatus == RequestHandler::IPC_STATUS_PIPE_ERROR )
+ {
+#if defined ANDROID
+ // Ignore crack pipe errors on Android
+#else
+ // Keep using this oddly named BE_PATHINFO_MISSING value
+ // for pipe-related errors on other platforms. Of course
+ // this crack with two (if not more) levels of our own
+ // error codes hiding the actual system error code is
+ // broken, but that is done all over the code, let's leave
+ // reengineering that to another year.
+ SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
+#endif
+ }
+ else if ( aStatus == RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR )
+ {
+ SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
+ }
+ else if ( aStatus == RequestHandler::IPC_STATUS_2ND_OFFICE )
+ {
+ // 2nd office startup should terminate after sending cmdlineargs through pipe
+ SetBootstrapStatus(BS_TERMINATE);
+ }
+ else if ( !rCmdLineArgs.GetUnknown().isEmpty()
+ || rCmdLineArgs.IsHelp() || rCmdLineArgs.IsVersion() )
+ {
+ // disable IPC thread in an instance that is just showing a help message
+ RequestHandler::Disable();
+ }
+ pSignalHandler = osl_addSignalHandler(SalMainPipeExchangeSignal_impl, nullptr);
+ }
+}
+
+void Desktop::InitFinished()
+{
+ CloseSplashScreen();
+}
+
+void Desktop::DeInit()
+{
+ try {
+ // instead of removing of the configManager just let it commit all the changes
+ utl::ConfigManager::storeConfigItems();
+ FlushConfiguration();
+
+ // close splashscreen if it's still open
+ CloseSplashScreen();
+ Reference< XComponent >(
+ comphelper::getProcessComponentContext(), UNO_QUERY_THROW )->
+ dispose();
+ // nobody should get a destroyed service factory...
+ ::comphelper::setProcessServiceFactory( nullptr );
+
+ // clear lockfile
+ m_xLockfile.reset();
+
+ RequestHandler::Disable();
+ if( pSignalHandler )
+ osl_removeSignalHandler( pSignalHandler );
+ } catch (const RuntimeException&) {
+ // someone threw an exception during shutdown
+ // this will leave some garbage behind..
+ }
+}
+
+bool Desktop::QueryExit()
+{
+ try
+ {
+ utl::ConfigManager::storeConfigItems();
+ }
+ catch ( const RuntimeException& )
+ {
+ }
+
+ const sal_Char SUSPEND_QUICKSTARTVETO[] = "SuspendQuickstartVeto";
+
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XPropertySet > xPropertySet(xDesktop, UNO_QUERY_THROW);
+ xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(true) );
+
+ bool bExit = xDesktop->terminate();
+
+ if ( !bExit )
+ {
+ xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(false) );
+ }
+ else if (!Application::IsEventTestingModeEnabled())
+ {
+ FlushConfiguration();
+ try
+ {
+ // it is no problem to call RequestHandler::Disable() more than once
+ // it also looks to be threadsafe
+ RequestHandler::Disable();
+ }
+ catch ( const RuntimeException& )
+ {
+ }
+
+ m_xLockfile.reset();
+
+ }
+
+ return bExit;
+}
+
+void Desktop::HandleBootstrapPathErrors( ::utl::Bootstrap::Status aBootstrapStatus, const OUString& aDiagnosticMessage )
+{
+ if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
+ {
+ OUString aProductKey;
+ OUString aTemp;
+
+ osl_getExecutableFile( &aProductKey.pData );
+ sal_uInt32 lastIndex = aProductKey.lastIndexOf('/');
+ if ( lastIndex > 0 )
+ aProductKey = aProductKey.copy( lastIndex+1 );
+
+ aTemp = ::utl::Bootstrap::getProductKey( aProductKey );
+ if ( !aTemp.isEmpty() )
+ aProductKey = aTemp;
+
+ OUString const aMessage(aDiagnosticMessage + "\n");
+
+ ScopedVclPtrInstance< MessageDialog > aBootstrapFailedBox(nullptr, aMessage);
+ aBootstrapFailedBox->SetText( aProductKey );
+ aBootstrapFailedBox->Execute();
+ }
+}
+
+// Create a error message depending on bootstrap failure code and an optional file url
+OUString Desktop::CreateErrorMsgString(
+ utl::Bootstrap::FailureCode nFailureCode,
+ const OUString& aFileURL )
+{
+ OUString aMsg;
+ OUString aFilePath;
+ bool bFileInfo = true;
+
+ switch ( nFailureCode )
+ {
+ /// the shared installation directory could not be located
+ case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_PATH_INVALID,
+ "The installation path is not available." );
+ bFileInfo = false;
+ }
+ break;
+
+ /// the bootstrap INI file could not be found or read
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_FILE_MISSING,
+ "The configuration file \"$1\" is missing." );
+ }
+ break;
+
+ /// the bootstrap INI is missing a required entry
+ /// the bootstrap INI contains invalid data
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_FILE_CORRUPT,
+ "The configuration file \"$1\" is corrupt." );
+ }
+ break;
+
+ /// the version locator INI file could not be found or read
+ case ::utl::Bootstrap::MISSING_VERSION_FILE:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_FILE_MISSING,
+ "The configuration file \"$1\" is missing." );
+ }
+ break;
+
+ /// the version locator INI has no entry for this version
+ case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_NO_SUPPORT,
+ "The main configuration file \"$1\" does not support the current version." );
+ }
+ break;
+
+ /// the user installation directory does not exist
+ case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_DIR_MISSING,
+ "The configuration directory \"$1\" is missing." );
+ }
+ break;
+
+ /// some bootstrap data was invalid in unexpected ways
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
+ {
+ aMsg = GetMsgString( STR_BOOTSTRAP_ERR_INTERNAL,
+ "An internal failure occurred." );
+ bFileInfo = false;
+ }
+ break;
+
+ case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
+ {
+ // This needs to be improved, see #i67575#:
+ aMsg = "Invalid version file entry";
+ bFileInfo = false;
+ }
+ break;
+
+ case ::utl::Bootstrap::NO_FAILURE:
+ {
+ OSL_ASSERT(false);
+ }
+ break;
+ }
+
+ if ( bFileInfo )
+ {
+ OUString aMsgString( aMsg );
+
+ osl::File::getSystemPathFromFileURL( aFileURL, aFilePath );
+
+ aMsgString = aMsgString.replaceFirst( "$1", aFilePath );
+ aMsg = aMsgString;
+ }
+
+ return MakeStartupErrorMessage( aMsg );
+}
+
+void Desktop::HandleBootstrapErrors(
+ BootstrapError aBootstrapError, OUString const & aErrorMessage )
+{
+ if ( aBootstrapError == BE_PATHINFO_MISSING )
+ {
+ OUString aErrorMsg;
+ OUString aBuffer;
+ utl::Bootstrap::Status aBootstrapStatus;
+ utl::Bootstrap::FailureCode nFailureCode;
+
+ aBootstrapStatus = ::utl::Bootstrap::checkBootstrapStatus( aBuffer, nFailureCode );
+ if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
+ {
+ switch ( nFailureCode )
+ {
+ case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
+ {
+ aErrorMsg = CreateErrorMsgString( nFailureCode, OUString() );
+ }
+ break;
+
+ /// the bootstrap INI file could not be found or read
+ /// the bootstrap INI is missing a required entry
+ /// the bootstrap INI contains invalid data
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
+ {
+ OUString aBootstrapFileURL;
+
+ utl::Bootstrap::locateBootstrapFile( aBootstrapFileURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aBootstrapFileURL );
+ }
+ break;
+
+ /// the version locator INI file could not be found or read
+ /// the version locator INI has no entry for this version
+ /// the version locator INI entry is not a valid directory URL
+ case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
+ case ::utl::Bootstrap::MISSING_VERSION_FILE:
+ {
+ OUString aVersionFileURL;
+
+ utl::Bootstrap::locateVersionFile( aVersionFileURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aVersionFileURL );
+ }
+ break;
+
+ /// the user installation directory does not exist
+ case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
+ {
+ OUString aUserInstallationURL;
+
+ utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
+ aErrorMsg = CreateErrorMsgString( nFailureCode, aUserInstallationURL );
+ }
+ break;
+
+ case ::utl::Bootstrap::NO_FAILURE:
+ {
+ OSL_ASSERT(false);
+ }
+ break;
+ }
+
+ HandleBootstrapPathErrors( aBootstrapStatus, aErrorMsg );
+ }
+ }
+ else if ( aBootstrapError == BE_UNO_SERVICEMANAGER || aBootstrapError == BE_UNO_SERVICE_CONFIG_MISSING )
+ {
+ // Uno service manager is not available. VCL needs a uno service manager to display a message box!!!
+ // Currently we are not able to display a message box with a service manager due to this limitations inside VCL.
+
+ // When UNO is not properly initialized, all kinds of things can fail
+ // and cause the process to crash (e.g., a call to GetMsgString may
+ // crash when somewhere deep within that call Any::operator <= is used
+ // with a PropertyValue, and no binary UNO type description for
+ // PropertyValue is available). To give the user a hint even if
+ // generating and displaying a message box below crashes, print a
+ // hard-coded message on stderr first:
+ std::cerr
+ << "The application cannot be started.\n"
+ // STR_BOOTSTRAP_ERR_CANNOT_START
+ << (aBootstrapError == BE_UNO_SERVICEMANAGER
+ ? "The component manager is not available.\n"
+ // STR_BOOTSTRAP_ERR_NO_SERVICE
+ : "The configuration service is not available.\n");
+ // STR_BOOTSTRAP_ERR_NO_CFG_SERVICE
+ if ( !aErrorMessage.isEmpty() )
+ {
+ std::cerr << "(\"" << aErrorMessage << "\")\n";
+ }
+
+ // First sentence. We cannot bootstrap office further!
+ OUString aMessage;
+ OUStringBuffer aDiagnosticMessage( 100 );
+
+ OUString aErrorMsg;
+
+ if ( aBootstrapError == BE_UNO_SERVICEMANAGER )
+ aErrorMsg = "The service manager is not available.";
+ else
+ aErrorMsg = GetMsgString( STR_BOOTSTRAP_ERR_NO_CFG_SERVICE,
+ "The configuration service is not available." );
+
+ aDiagnosticMessage.append( aErrorMsg );
+ aDiagnosticMessage.append( "\n" );
+ if ( !aErrorMessage.isEmpty() )
+ {
+ aDiagnosticMessage.append( "(\"" );
+ aDiagnosticMessage.append( aErrorMessage );
+ aDiagnosticMessage.append( "\")\n" );
+ }
+
+ // Due to the fact the we haven't a backup applicat.rdb file anymore it is not possible to
+ // repair the installation with the setup executable besides the office executable. Now
+ // we have to ask the user to start the setup on CD/installation directory manually!!
+ OUString aStartSetupManually( GetMsgString(
+ STR_ASK_START_SETUP_MANUALLY,
+ "Start setup application to repair the installation from CD, or the folder containing the installation packages.",
+ aBootstrapError == BE_UNO_SERVICEMANAGER ) );
+
+ aDiagnosticMessage.append( aStartSetupManually );
+ aMessage = MakeStartupErrorMessage(
+ aDiagnosticMessage.makeStringAndClear(),
+ aBootstrapError == BE_UNO_SERVICEMANAGER );
+
+ FatalError( aMessage);
+ }
+ else if ( aBootstrapError == BE_OFFICECONFIG_BROKEN )
+ {
+ // test restore of registrymodifications
+ sal_uInt16 nSecureUserConfigNumCopies(0);
+ bool bFireOriginalError(true);
+
+ // read configuration from soffice.ini
+ const bool bSecureUserConfig(comphelper::BackupFileHelper::getSecureUserConfig(nSecureUserConfigNumCopies));
+
+ if (bSecureUserConfig)
+ {
+ // try to asccess user layer configuration file
+ OUString conf("${CONFIGURATION_LAYERS}");
+ rtl::Bootstrap::expandMacros(conf);
+ const OUString aTokenUser("user:");
+ sal_Int32 nStart(conf.indexOf(aTokenUser));
+ OUString aUser;
+
+ if (-1 != nStart)
+ {
+ nStart += aTokenUser.getLength();
+ sal_Int32 nEnd(conf.indexOf(' ', nStart));
+
+ if (-1 == nEnd)
+ {
+ nEnd = conf.getLength();
+ }
+
+ aUser = conf.copy(nStart, nEnd - nStart);
+ aUser.startsWith("!", &aUser);
+ }
+
+ if (!aUser.isEmpty())
+ {
+ comphelper::BackupFileHelper aBackupFileHelper(aUser, nSecureUserConfigNumCopies);
+
+ if (aBackupFileHelper.isPopPossible())
+ {
+ // for linux (and probably others?) we need to instantiate XDesktop2
+ // to be able to open a *.ui-file based dialog, so do this here locally.
+ // does no harm on win, so better always do this (in error case only anyways)
+ Reference< XComponentContext > xLocalContext = ::comphelper::getProcessComponentContext();
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xLocalContext);
+
+ ScopedVclPtrInstance< MessageDialog > aQueryShouldRestore(
+ Application::GetDefDialogParent(),
+ "QueryTryToRestoreConfigurationDialog",
+ "desktop/ui/querytrytorestoreconfigurationdialog.ui");
+
+ if (aQueryShouldRestore.get())
+ {
+ if (!aErrorMessage.isEmpty())
+ {
+ OUString aPrimaryText(aQueryShouldRestore->get_primary_text());
+
+ aPrimaryText += "\n(\"" + aErrorMessage + "\")";
+ aQueryShouldRestore->set_primary_text(aPrimaryText);
+ }
+
+ if (RET_YES == aQueryShouldRestore->Execute())
+ {
+ aBackupFileHelper.tryPop();
+ bFireOriginalError = false;
+ }
+ }
+ }
+ }
+ }
+
+ // set flag at BackupFileHelper to be able to know if _exit was called and
+ // actions are executed after this
+ comphelper::BackupFileHelper::setExitWasCalled();
+
+ if (bFireOriginalError)
+ {
+ OUString msg(
+ GetMsgString(
+ STR_CONFIG_ERR_ACCESS_GENERAL,
+ ("A general error occurred while accessing your central"
+ " configuration.")));
+ if (!aErrorMessage.isEmpty()) {
+ msg += "\n(\"" + aErrorMessage + "\")";
+ }
+ FatalError(MakeStartupErrorMessage(msg));
+ }
+ else
+ {
+ // Already presented all information to the user.
+ // just do what FatalError does at it's end
+ _exit(EXITHELPER_FATAL_ERROR);
+ }
+ }
+ else if ( aBootstrapError == BE_USERINSTALL_FAILED )
+ {
+ OUString aMessage;
+ OUStringBuffer aDiagnosticMessage( 100 );
+ OUString aErrorMsg;
+ aErrorMsg = GetMsgString( STR_BOOTSTRAP_ERR_USERINSTALL_FAILED,
+ "User installation could not be completed" );
+ aDiagnosticMessage.append( aErrorMsg );
+ aMessage = MakeStartupErrorMessage( aDiagnosticMessage.makeStringAndClear() );
+ FatalError(aMessage);
+ }
+ else if ( aBootstrapError == BE_LANGUAGE_MISSING )
+ {
+ OUString aMessage;
+ OUStringBuffer aDiagnosticMessage( 100 );
+ OUString aErrorMsg;
+ aErrorMsg = GetMsgString(
+ //@@@ FIXME: should use an own resource string => #i36213#
+ STR_BOOTSTRAP_ERR_LANGUAGE_MISSING,
+ "Language could not be determined." );
+ aDiagnosticMessage.append( aErrorMsg );
+ aMessage = MakeStartupErrorMessage(
+ aDiagnosticMessage.makeStringAndClear() );
+ FatalError(aMessage);
+ }
+ else if (( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE ) ||
+ ( aBootstrapError == BE_USERINSTALL_NOWRITEACCESS ))
+ {
+ OUString aUserInstallationURL;
+ OUString aUserInstallationPath;
+ OUString aMessage;
+ OUString aErrorMsg;
+ OUStringBuffer aDiagnosticMessage( 100 );
+
+ utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
+
+ if ( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE )
+ aErrorMsg = GetMsgString(
+ STR_BOOSTRAP_ERR_NOTENOUGHDISKSPACE,
+ "User installation could not be completed due to insufficient free disk space." );
+ else
+ aErrorMsg = GetMsgString(
+ STR_BOOSTRAP_ERR_NOACCESSRIGHTS,
+ "User installation could not be processed due to missing access rights." );
+
+ osl::File::getSystemPathFromFileURL( aUserInstallationURL, aUserInstallationPath );
+
+ aDiagnosticMessage.append( aErrorMsg );
+ aDiagnosticMessage.append( aUserInstallationPath );
+ aMessage = MakeStartupErrorMessage(
+ aDiagnosticMessage.makeStringAndClear() );
+ FatalError(aMessage);
+ }
+
+ return;
+}
+
+
+bool Desktop::isUIOnSessionShutdownAllowed()
+{
+ return officecfg::Office::Recovery::SessionShutdown::DocumentStoreUIEnabled
+ ::get();
+}
+
+namespace {
+
+bool crashReportInfoExists()
+{
+#if HAVE_FEATURE_BREAKPAD
+ std::string path = CrashReporter::getIniFileName();
+ std::ifstream aFile(path);
+ while (aFile.good())
+ {
+ std::string line;
+ std::getline(aFile, line);
+ int sep = line.find('=');
+ if (sep >= 0)
+ {
+ std::string key = line.substr(0, sep);
+ if (key == "DumpFile")
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+#if HAVE_FEATURE_BREAKPAD
+void handleCrashReport()
+{
+ static const char SERVICENAME_CRASHREPORT[] = "com.sun.star.comp.svx.CrashReportUI";
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XSynchronousDispatch > xRecoveryUI(
+ xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_CRASHREPORT, xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ Reference< css::util::XURLTransformer > xURLParser =
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
+
+ css::util::URL aURL;
+ css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+}
+#endif
+
+void handleSafeMode()
+{
+ static const char SERVICENAME_SAFEMODE[] = "com.sun.star.comp.svx.SafeModeUI";
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XSynchronousDispatch > xSafeModeUI(
+ xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_SAFEMODE, xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ css::util::URL aURL;
+ css::uno::Any aRet = xSafeModeUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+}
+
+/** @short check if recovery must be started or not.
+
+ @param bCrashed [boolean ... out!]
+ the office crashed last times.
+ But may be there are no recovery data.
+ Useful to trigger the error report tool without
+ showing the recovery UI.
+
+ @param bRecoveryDataExists [boolean ... out!]
+ there exists some recovery data.
+
+ @param bSessionDataExists [boolean ... out!]
+ there exists some session data.
+ Because the user may be logged out last time from its
+ unix session...
+*/
+void impl_checkRecoveryState(bool& bCrashed ,
+ bool& bRecoveryDataExists,
+ bool& bSessionDataExists )
+{
+ bCrashed = officecfg::Office::Recovery::RecoveryInfo::Crashed::get() || crashReportInfoExists();
+ bool elements = officecfg::Office::Recovery::RecoveryList::get()->
+ hasElements();
+ bool session
+ = officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
+ bRecoveryDataExists = elements && !session;
+ bSessionDataExists = elements && session;
+}
+
+
+/* @short start the recovery wizard.
+
+ @param bEmergencySave
+ differs between EMERGENCY_SAVE and RECOVERY
+*/
+bool impl_callRecoveryUI(bool bEmergencySave ,
+ bool bExistsRecoveryData)
+{
+ static const char SERVICENAME_RECOVERYUI[] = "com.sun.star.comp.svx.RecoveryUI";
+ static const char COMMAND_EMERGENCYSAVE[] = "vnd.sun.star.autorecovery:/doEmergencySave";
+ static const char COMMAND_RECOVERY[] = "vnd.sun.star.autorecovery:/doAutoRecovery";
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XSynchronousDispatch > xRecoveryUI(
+ xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_RECOVERYUI, xContext),
+ css::uno::UNO_QUERY_THROW);
+
+ Reference< css::util::XURLTransformer > xURLParser =
+ css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
+
+ css::util::URL aURL;
+ if (bEmergencySave)
+ aURL.Complete = COMMAND_EMERGENCYSAVE;
+ else if (bExistsRecoveryData)
+ aURL.Complete = COMMAND_RECOVERY;
+ else
+ return false;
+
+ xURLParser->parseStrict(aURL);
+
+ css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
+ bool bRet = false;
+ aRet >>= bRet;
+ return bRet;
+}
+
+}
+
+/*
+ * Save all open documents so they will be reopened
+ * the next time the application is started
+ *
+ * returns sal_True if at least one document could be saved...
+ *
+ */
+bool Desktop::SaveTasks()
+{
+ return impl_callRecoveryUI(
+ true , // sal_True => force emergency save
+ false);
+}
+
+namespace {
+
+void restartOnMac(bool passArguments) {
+#if defined MACOSX
+ RequestHandler::Disable();
+#if HAVE_FEATURE_MACOSX_SANDBOX
+ (void) passArguments; // avoid warnings
+ ResMgr *resMgr = Desktop::GetDesktopResManager();
+ OUString aMessage = ResId(STR_LO_MUST_BE_RESTARTED, *resMgr).toString();
+
+ MessageDialog aRestartBox(NULL, aMessage);
+ aRestartBox.Execute();
+#else
+ OUString execUrl;
+ OSL_VERIFY(osl_getExecutableFile(&execUrl.pData) == osl_Process_E_None);
+ OUString execPath;
+ OString execPath8;
+ if ((osl::FileBase::getSystemPathFromFileURL(execUrl, execPath)
+ != osl::FileBase::E_None) ||
+ !execPath.convertToString(
+ &execPath8, osl_getThreadTextEncoding(),
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::abort();
+ }
+ std::vector< OString > args;
+ args.push_back(execPath8);
+ bool wait = false;
+ if (passArguments) {
+ sal_uInt32 n = osl_getCommandArgCount();
+ for (sal_uInt32 i = 0; i < n; ++i) {
+ OUString arg;
+ osl_getCommandArg(i, &arg.pData);
+ if (arg.match("--accept=")) {
+ wait = true;
+ }
+ OString arg8;
+ if (!arg.convertToString(
+ &arg8, osl_getThreadTextEncoding(),
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
+ RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::abort();
+ }
+ args.push_back(arg8);
+ }
+ }
+ std::vector< char const * > argPtrs;
+ for (std::vector< OString >::iterator i(args.begin()); i != args.end();
+ ++i)
+ {
+ argPtrs.push_back(i->getStr());
+ }
+ argPtrs.push_back(nullptr);
+ execv(execPath8.getStr(), const_cast< char ** >(&argPtrs[0]));
+ if (errno == ENOTSUP) { // happens when multithreaded on OS X < 10.6
+ pid_t pid = fork();
+ if (pid == 0) {
+ execv(execPath8.getStr(), const_cast< char ** >(&argPtrs[0]));
+ } else if (pid > 0) {
+ // Two simultaneously running soffice processes lead to two dock
+ // icons, so avoid waiting here unless it must be assumed that the
+ // process invoking soffice itself wants to wait for soffice to
+ // finish:
+ if (!wait) {
+ return;
+ }
+ int stat;
+ if (waitpid(pid, &stat, 0) == pid && WIFEXITED(stat)) {
+ _exit(WEXITSTATUS(stat));
+ }
+ }
+ }
+ std::abort();
+#endif
+#else
+ (void) passArguments; // avoid warnings
+#endif
+}
+
+}
+
+void Desktop::Exception(ExceptionCategory nCategory)
+{
+ // protect against recursive calls
+ static bool bInException = false;
+
+ SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
+ Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE );
+ if ( bInException )
+ {
+ OUString aDoubleExceptionString;
+ Application::Abort( aDoubleExceptionString );
+ }
+
+ bInException = true;
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+
+ // save all modified documents ... if it's allowed doing so.
+ bool bRestart = false;
+ bool bAllowRecoveryAndSessionManagement = (
+ ( !rArgs.IsNoRestore() ) && // some use cases of office must work without recovery
+ ( !rArgs.IsHeadless() ) &&
+ ( nCategory != ExceptionCategory::UserInterface ) && // recovery can't work without UI ... but UI layer seems to be the reason for this crash
+ ( Application::IsInExecute() ) // crashes during startup and shutdown should be ignored (they indicates a corrupt installation ...)
+ );
+ if ( bAllowRecoveryAndSessionManagement )
+ bRestart = SaveTasks();
+
+ FlushConfiguration();
+
+ switch( nCategory )
+ {
+ case ExceptionCategory::ResourceNotLoaded:
+ {
+ OUString aResExceptionString;
+ Application::Abort( aResExceptionString );
+ break;
+ }
+
+ default:
+ {
+ m_xLockfile.reset();
+
+ if( bRestart )
+ {
+ RequestHandler::Disable();
+ if( pSignalHandler )
+ osl_removeSignalHandler( pSignalHandler );
+
+ restartOnMac(false);
+ if ( m_rSplashScreen.is() )
+ m_rSplashScreen->reset();
+
+ _exit( EXITHELPER_CRASH_WITH_RESTART );
+ }
+ else
+ {
+ Application::Abort( OUString() );
+ }
+
+ break;
+ }
+ }
+
+ OSL_ASSERT(false); // unreachable
+}
+
+void Desktop::AppEvent( const ApplicationEvent& rAppEvent )
+{
+ HandleAppEvent( rAppEvent );
+}
+
+struct ExecuteGlobals
+{
+ Reference < css::document::XDocumentEventListener > xGlobalBroadcaster;
+ bool bRestartRequested;
+ bool bUseSystemFileDialog;
+ std::unique_ptr<SvtLanguageOptions> pLanguageOptions;
+ std::unique_ptr<SvtPathOptions> pPathOptions;
+
+ ExecuteGlobals()
+ : bRestartRequested( false )
+ , bUseSystemFileDialog( true )
+ {}
+};
+
+static ExecuteGlobals* pExecGlobals = nullptr;
+
+
+//This just calls Execute() for all normal uses of LibreOffice, but for
+//ui-testing if built with afl-clang-fast++ then on exit it will pseudo-restart
+//(up to 100 times)
+void Desktop::DoExecute()
+{
+#if !defined(__AFL_HAVE_MANUAL_CONTROL)
+ Execute();
+#else
+ while (__AFL_LOOP(1000))
+ {
+ Execute();
+ OpenDefault();
+ }
+#endif
+}
+
+int Desktop::Main()
+{
+ pExecGlobals = new ExecuteGlobals();
+
+ // Remember current context object
+ css::uno::ContextLayer layer( css::uno::getCurrentContext() );
+
+ if ( m_aBootstrapError != BE_OK )
+ {
+ HandleBootstrapErrors( m_aBootstrapError, m_aBootstrapErrorMessage );
+ return EXIT_FAILURE;
+ }
+
+ BootstrapStatus eStatus = GetBootstrapStatus();
+ if (eStatus == BS_TERMINATE) {
+ return EXIT_SUCCESS;
+ }
+
+ // Detect desktop environment - need to do this as early as possible
+ css::uno::setCurrentContext( new DesktopContext( css::uno::getCurrentContext() ) );
+
+ CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+
+#if HAVE_FEATURE_DESKTOP
+ OUString aUnknown( rCmdLineArgs.GetUnknown() );
+ if ( !aUnknown.isEmpty() )
+ {
+ displayCmdlineHelp( aUnknown );
+ return EXIT_FAILURE;
+ }
+ if ( rCmdLineArgs.IsHelp() )
+ {
+ displayCmdlineHelp( OUString() );
+ return EXIT_SUCCESS;
+ }
+ if ( rCmdLineArgs.IsVersion() )
+ {
+ displayVersion();
+ return EXIT_SUCCESS;
+ }
+#endif
+
+ ResMgr::SetReadStringHook( ReplaceStringHookProc );
+
+ // Startup screen
+ OpenSplashScreen();
+
+ SetSplashScreenProgress(10);
+
+ userinstall::Status inst_fin = userinstall::finalize();
+ if (inst_fin != userinstall::EXISTED && inst_fin != userinstall::CREATED)
+ {
+ SAL_WARN( "desktop.app", "userinstall failed");
+ if ( inst_fin == userinstall::ERROR_NO_SPACE )
+ HandleBootstrapErrors(
+ BE_USERINSTALL_NOTENOUGHDISKSPACE, OUString() );
+ else if ( inst_fin == userinstall::ERROR_CANT_WRITE )
+ HandleBootstrapErrors( BE_USERINSTALL_NOWRITEACCESS, OUString() );
+ else
+ HandleBootstrapErrors( BE_USERINSTALL_FAILED, OUString() );
+ return EXIT_FAILURE;
+ }
+ // refresh path information
+ utl::Bootstrap::reloadData();
+ SetSplashScreenProgress(20);
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< XRestartManager > xRestartManager( OfficeRestartManager::get(xContext) );
+
+ Reference< XDesktop2 > xDesktop;
+ try
+ {
+ RegisterServices(xContext);
+
+ SetSplashScreenProgress(25);
+
+#if HAVE_FEATURE_DESKTOP
+ // check user installation directory for lockfile so we can be sure
+ // there is no other instance using our data files from a remote host
+ m_xLockfile.reset(new Lockfile);
+
+ if ( !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsInvisible() &&
+ !rCmdLineArgs.IsNoLockcheck() && !m_xLockfile->check( Lockfile_execWarning ))
+ {
+ // Lockfile exists, and user clicked 'no'
+ return EXIT_FAILURE;
+ }
+
+ // check if accessibility is enabled but not working and allow to quit
+ if( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() )
+ {
+ if( !InitAccessBridge() )
+ return EXIT_FAILURE;
+ }
+#endif
+
+ // terminate if requested...
+ if( rCmdLineArgs.IsTerminateAfterInit() )
+ return EXIT_SUCCESS;
+
+ // Read the common configuration items for optimization purpose
+ if ( !InitializeConfiguration() )
+ return EXIT_FAILURE;
+
+ SetSplashScreenProgress(30);
+
+ // create title string
+ LanguageTag aLocale( LANGUAGE_SYSTEM);
+ ResMgr* pLabelResMgr = GetDesktopResManager();
+ OUString aTitle = pLabelResMgr ? ResId(RID_APPTITLE, *pLabelResMgr).toString() : OUString();
+
+#ifdef DBG_UTIL
+ //include buildid in non product builds
+ OUString aDefault("development");
+ aTitle += " [";
+ aTitle += utl::Bootstrap::getBuildIdData(aDefault);
+ aTitle += "]";
+#endif
+
+ SetDisplayName( aTitle );
+ SetSplashScreenProgress(35);
+ pExecGlobals->pPathOptions.reset( new SvtPathOptions);
+ SetSplashScreenProgress(40);
+
+ xDesktop = css::frame::Desktop::create( xContext );
+
+ // create service for loading SFX (still needed in startup)
+ pExecGlobals->xGlobalBroadcaster = Reference < css::document::XDocumentEventListener >
+ ( css::frame::theGlobalEventBroadcaster::get(xContext), UNO_SET_THROW );
+
+ /* ensure existence of a default window that messages can be dispatched to
+ This is for the benefit of testtool which uses PostUserEvent extensively
+ and else can deadlock while creating this window from another thread while
+ the main thread is not yet in the event loop.
+ */
+ Application::GetDefaultDevice();
+
+#if HAVE_FEATURE_EXTENSIONS
+ // Check if bundled or shared extensions were added /removed
+ // and process those extensions (has to be done before checking
+ // the extension dependencies!
+ SynchronizeExtensionRepositories();
+ bool bAbort = CheckExtensionDependencies();
+ if ( bAbort )
+ return EXIT_FAILURE;
+
+ if (inst_fin == userinstall::CREATED)
+ {
+ Migration::migrateSettingsIfNecessary();
+ }
+#endif
+
+ // keep a language options instance...
+ pExecGlobals->pLanguageOptions.reset( new SvtLanguageOptions(true));
+
+ css::document::DocumentEvent aEvent;
+ aEvent.EventName = "OnStartApp";
+ pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
+
+ SetSplashScreenProgress(50);
+
+ // Backing Component
+ bool bCrashed = false;
+ bool bExistsRecoveryData = false;
+ bool bExistsSessionData = false;
+
+ impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
+
+ OUString pidfileName = rCmdLineArgs.GetPidfileName();
+ if ( !pidfileName.isEmpty() )
+ {
+ OUString pidfileURL;
+
+ if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
+ {
+ osl::File pidfile( pidfileURL );
+ osl::FileBase::RC rc;
+
+ osl::File::remove( pidfileURL );
+ if ( (rc = pidfile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ) == osl::File::E_None )
+ {
+ OString pid( OString::number( GETPID() ) );
+ sal_uInt64 written = 0;
+ if ( pidfile.write(pid.getStr(), pid.getLength(), written) != osl::File::E_None )
+ {
+ SAL_WARN("desktop.app", "cannot write pidfile " << pidfile.getURL());
+ }
+ pidfile.close();
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "cannot open pidfile " << pidfile.getURL() << osl::FileBase::RC(rc));
+ }
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "cannot get pidfile URL from path" << pidfileName);
+ }
+ }
+
+ if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
+ {
+ // Ensure that we use not the system file dialogs as
+ // headless mode relies on Application::EnableHeadlessMode()
+ // which does only work for VCL dialogs!!
+ SvtMiscOptions aMiscOptions;
+ pExecGlobals->bUseSystemFileDialog = aMiscOptions.UseSystemFileDialog();
+ aMiscOptions.SetUseSystemFileDialog( false );
+ }
+
+ pExecGlobals->bRestartRequested = xRestartManager->isRestartRequested(
+ true);
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ if ((!rCmdLineArgs.WantsToLoadDocument() && !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsQuickstart()) &&
+ (SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) &&
+ (!bExistsRecoveryData ) &&
+ (!bExistsSessionData ) &&
+ (!Application::AnyInput( VclInputFlags::APPEVENT ) ))
+ {
+ ShowBackingComponent(this);
+ }
+ }
+ }
+ catch ( const css::lang::WrappedTargetException& wte )
+ {
+ css::uno::Exception te;
+ wte.TargetException >>= te;
+ FatalError( MakeStartupConfigAccessErrorMessage(wte.Message + te.Message) );
+ }
+ catch ( const css::uno::Exception& e )
+ {
+ FatalError( MakeStartupErrorMessage(e.Message) );
+ }
+ SetSplashScreenProgress(55);
+
+ SvtFontSubstConfig().Apply();
+
+ SvtTabAppearanceCfg aAppearanceCfg;
+ SvtTabAppearanceCfg::SetInitialized();
+ aAppearanceCfg.SetApplicationDefaults( this );
+ SvtAccessibilityOptions aOptions;
+ aOptions.SetVCLSettings();
+ SetSplashScreenProgress(60);
+
+#if ENABLE_TELEPATHY
+ bool bListen = rCmdLineArgs.IsInvisible();
+ TeleManager::init( bListen );
+#endif
+
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ Application::SetFilterHdl( LINK( this, Desktop, ImplInitFilterHdl ) );
+ bool bTerminateRequested = false;
+
+ // Preload function depends on an initialized sfx application!
+ SetSplashScreenProgress(75);
+
+ // use system window dialogs
+ Application::SetSystemWindowMode( SystemWindowFlags::DIALOG );
+
+ SetSplashScreenProgress(80);
+
+ if ( !bTerminateRequested && !rCmdLineArgs.IsInvisible() &&
+ !rCmdLineArgs.IsNoQuickstart() )
+ InitializeQuickstartMode( xContext );
+
+ try
+ {
+ if ( xDesktop.is() )
+ xDesktop->addTerminateListener( new RequestHandlerController );
+ SetSplashScreenProgress(100);
+ }
+ catch ( const css::uno::Exception& e )
+ {
+ FatalError( MakeStartupErrorMessage(e.Message) );
+ }
+
+ // FIXME: move this somewhere sensible.
+#if HAVE_FEATURE_OPENCL
+ CheckOpenCLCompute(xDesktop);
+#endif
+
+ // Release solar mutex just before we wait for our client to connect
+ {
+ SolarMutexReleaser aReleaser;
+
+ // Post user event to startup first application component window
+ // We have to send this OpenClients message short before execute() to
+ // minimize the risk that this message overtakes type detection construction!!
+ Application::PostUserEvent( LINK( this, Desktop, OpenClients_Impl ) );
+
+ // Post event to enable acceptors
+ Application::PostUserEvent( LINK( this, Desktop, EnableAcceptors_Impl) );
+
+ // Acquire solar mutex just before we enter our message loop
+ }
+
+ // call Application::Execute to process messages in vcl message loop
+ try
+ {
+#if HAVE_FEATURE_JAVA
+ // The JavaContext contains an interaction handler which is used when
+ // the creation of a Java Virtual Machine fails
+ css::uno::ContextLayer layer2(
+ new svt::JavaContext( css::uno::getCurrentContext() ) );
+#endif
+ // check whether the shutdown is caused by restart just before entering the Execute
+ pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
+ xRestartManager->isRestartRequested(true);
+
+ if ( !pExecGlobals->bRestartRequested )
+ {
+ // if this run of the office is triggered by restart, some additional actions should be done
+ DoRestartActionsIfNecessary( !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsNoQuickstart() );
+
+ DoExecute();
+ }
+ }
+ catch(const css::document::CorruptedFilterConfigurationException& exFilterCfg)
+ {
+ RequestHandler::SetDowning();
+ FatalError( MakeStartupErrorMessage(exFilterCfg.Message) );
+ }
+ catch(const css::configuration::CorruptedConfigurationException& exAnyCfg)
+ {
+ RequestHandler::SetDowning();
+ FatalError( MakeStartupErrorMessage(exAnyCfg.Message) );
+ }
+ catch( const css::uno::Exception& exUNO)
+ {
+ RequestHandler::SetDowning();
+ FatalError( exUNO.Message);
+ }
+ catch( const std::exception& exSTD)
+ {
+ RequestHandler::SetDowning();
+ FatalError( OUString::createFromAscii( exSTD.what()));
+ }
+ catch( ...)
+ {
+ RequestHandler::SetDowning();
+ FatalError( "Caught Unknown Exception: Aborting!");
+ }
+ }
+ else
+ {
+ if (xDesktop.is())
+ xDesktop->terminate();
+ }
+ // CAUTION: you do not necessarily get here e.g. on the Mac.
+ // please put all deinitialization code into doShutdown
+ return doShutdown();
+}
+
+int Desktop::doShutdown()
+{
+ if( ! pExecGlobals )
+ return EXIT_SUCCESS;
+
+ pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
+ OfficeRestartManager::get(comphelper::getProcessComponentContext())->
+ isRestartRequested(true);
+ if ( pExecGlobals->bRestartRequested )
+ SetRestartState();
+
+ if (pExecGlobals->xGlobalBroadcaster.is())
+ {
+ css::document::DocumentEvent aEvent;
+ aEvent.EventName = "OnCloseApp";
+ pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
+ }
+
+ delete pResMgr;
+ pResMgr = nullptr;
+ // Restore old value
+ const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
+ if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
+ SvtMiscOptions().SetUseSystemFileDialog( pExecGlobals->bUseSystemFileDialog );
+
+ OUString pidfileName = rCmdLineArgs.GetPidfileName();
+ if ( !pidfileName.isEmpty() )
+ {
+ OUString pidfileURL;
+
+ if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
+ {
+ if ( osl::File::remove( pidfileURL ) != osl::FileBase::E_None )
+ {
+ SAL_WARN("desktop.app", "shutdown: cannot remove pidfile " << pidfileURL);
+ }
+ }
+ else
+ {
+ SAL_WARN("desktop.app", "shutdown: cannot get pidfile URL from path" << pidfileName);
+ }
+ }
+
+ // remove temp directory
+ RemoveTemporaryDirectory();
+ FlushConfiguration();
+ // The acceptors in the AcceptorMap must be released (in DeregisterServices)
+ // with the solar mutex unlocked, to avoid deadlock:
+ {
+ SolarMutexReleaser aReleaser;
+ DeregisterServices();
+#if HAVE_FEATURE_SCRIPTING
+ StarBASIC::DetachAllDocBasicItems();
+#endif
+ }
+ // be sure that path/language options gets destroyed before
+ // UCB is deinitialized
+ pExecGlobals->pLanguageOptions.reset( nullptr );
+ pExecGlobals->pPathOptions.reset( nullptr );
+
+ bool bRR = pExecGlobals->bRestartRequested;
+ delete pExecGlobals;
+ pExecGlobals = nullptr;
+
+ if ( bRR )
+ {
+ restartOnMac(true);
+ if ( m_rSplashScreen.is() )
+ m_rSplashScreen->reset();
+
+ return EXITHELPER_NORMAL_RESTART;
+ }
+ return EXIT_SUCCESS;
+}
+
+IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool )
+{
+ return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData );
+}
+
+bool Desktop::InitializeConfiguration()
+{
+ try
+ {
+ css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext() );
+ return true;
+ }
+ catch( css::lang::ServiceNotRegisteredException & e )
+ {
+ HandleBootstrapErrors(
+ Desktop::BE_UNO_SERVICE_CONFIG_MISSING, e.Message );
+ }
+ catch( const css::configuration::MissingBootstrapFileException& e )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::MISSING_BOOTSTRAP_FILE,
+ e.BootstrapFileURL ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_USER_INSTALL, aMsg );
+ }
+ catch( const css::configuration::InvalidBootstrapFileException& e )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY,
+ e.BootstrapFileURL ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ catch( const css::configuration::InstallationIncompleteException& )
+ {
+ OUString aVersionFileURL;
+ OUString aMsg;
+ utl::Bootstrap::PathStatus aPathStatus = utl::Bootstrap::locateVersionFile( aVersionFileURL );
+ if ( aPathStatus == utl::Bootstrap::PATH_EXISTS )
+ aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE_ENTRY, aVersionFileURL );
+ else
+ aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE, aVersionFileURL );
+
+ HandleBootstrapPathErrors( ::utl::Bootstrap::MISSING_USER_INSTALL, aMsg );
+ }
+ catch ( const css::configuration::backend::BackendAccessException& exception)
+ {
+ // [cm122549] It is assumed in this case that the message
+ // coming from InitConfiguration (in fact CreateApplicationConf...)
+ // is suitable for display directly.
+ FatalError( MakeStartupErrorMessage( exception.Message ) );
+ }
+ catch ( const css::configuration::backend::BackendSetupException& exception)
+ {
+ // [cm122549] It is assumed in this case that the message
+ // coming from InitConfiguration (in fact CreateApplicationConf...)
+ // is suitable for display directly.
+ FatalError( MakeStartupErrorMessage( exception.Message ) );
+ }
+ catch ( const css::configuration::CannotLoadConfigurationException& )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
+ OUString() ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ catch( const css::uno::Exception& )
+ {
+ OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
+ OUString() ));
+ HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
+ }
+ return false;
+}
+
+void Desktop::FlushConfiguration()
+{
+ css::uno::Reference< css::util::XFlushable >(
+ css::configuration::theDefaultProvider::get(
+ comphelper::getProcessComponentContext()),
+ css::uno::UNO_QUERY_THROW)->flush();
+}
+
+bool Desktop::InitializeQuickstartMode( const Reference< XComponentContext >& rxContext )
+{
+ try
+ {
+ // the shutdown icon sits in the systray and allows the user to keep
+ // the office instance running for quicker restart
+ // this will only be activated if --quickstart was specified on cmdline
+
+ bool bQuickstart = shouldLaunchQuickstart();
+
+ // Try to instantiate quickstart service. This service is not mandatory, so
+ // do nothing if service is not available
+
+ // #i105753# the following if was invented for performance
+ // unfortunately this broke the Mac behavior which is to always run
+ // in quickstart mode since Mac applications do not usually quit
+ // when the last document closes.
+ // Note that this claim that on OS X we "always run in quickstart mode"
+ // has nothing to do with (quick) *starting* (i.e. starting automatically
+ // when the user logs in), though, but with not quitting when no documents
+ // are open.
+ #ifndef MACOSX
+ if ( bQuickstart )
+ #endif
+ {
+ css::office::Quickstart::createStart(rxContext, bQuickstart);
+ }
+ return true;
+ }
+ catch( const css::uno::Exception& )
+ {
+ return false;
+ }
+}
+
+void Desktop::OverrideSystemSettings( AllSettings& rSettings )
+{
+ if ( !SvtTabAppearanceCfg::IsInitialized () )
+ return;
+
+ StyleSettings hStyleSettings = rSettings.GetStyleSettings();
+ MouseSettings hMouseSettings = rSettings.GetMouseSettings();
+
+ DragFullOptions nDragFullOptions = hStyleSettings.GetDragFullOptions();
+
+ SvtTabAppearanceCfg aAppearanceCfg;
+ DragMode nDragMode = aAppearanceCfg.GetDragMode();
+ switch ( nDragMode )
+ {
+ case DragMode::FullWindow:
+ nDragFullOptions |= DragFullOptions::All;
+ break;
+ case DragMode::Frame:
+ nDragFullOptions &= ~DragFullOptions::All;
+ break;
+ case DragMode::SystemDep:
+ default:
+ break;
+ }
+
+ MouseFollowFlags nFollow = hMouseSettings.GetFollow();
+ hMouseSettings.SetFollow( aAppearanceCfg.IsMenuMouseFollow() ? (nFollow|MouseFollowFlags::Menu) : (nFollow&~MouseFollowFlags::Menu));
+ rSettings.SetMouseSettings(hMouseSettings);
+
+ SvtMenuOptions aMenuOpt;
+ hStyleSettings.SetUseImagesInMenus(aMenuOpt.GetMenuIconsState());
+ hStyleSettings.SetContextMenuShortcuts(aMenuOpt.GetContextMenuShortcuts());
+ hStyleSettings.SetDragFullOptions( nDragFullOptions );
+ rSettings.SetStyleSettings ( hStyleSettings );
+}
+
+
+IMPL_STATIC_LINK(Desktop, AsyncInitFirstRun, Timer *, /*unused*/, void)
+{
+ DoFirstRunInitializations();
+}
+
+
+class ExitTimer : public Timer
+{
+ public:
+ ExitTimer()
+ {
+ SetTimeout(500);
+ Start();
+ }
+ virtual void Invoke() override
+ {
+ exit(42);
+ }
+};
+
+IMPL_LINK_NOARG(Desktop, OpenClients_Impl, void*, void)
+{
+ try {
+ // #i114963#
+ // Enable IPC thread before OpenClients
+ //
+ // This is because it is possible for another client to connect during the OpenClients() call.
+ // This can happen on Windows when document is printed (not opened) and another client wants to print (when printing multiple documents).
+ // If the IPC thread is enabled after OpenClients, then the client will not be processed because the application will exit after printing. i.e RequestHandler::AreRequestsPending() will always return false
+ //
+ // ALSO:
+ //
+ // Multiple clients may request simultaneous connections.
+ // When this server closes down it attempts to recreate the pipe (in RequestHandler::Disable()).
+ // It's possible that the client has a pending connection request.
+ // When the IPC thread is not running, this connection locks (because maPipe.accept()) is never called
+ RequestHandler::SetReady();
+ OpenClients();
+
+ CloseSplashScreen();
+ CheckFirstRun( );
+#ifdef _WIN32
+ // Registers a COM class factory of the service manager with the windows operating system.
+ Reference< XMultiServiceFactory > xSMgr= comphelper::getProcessServiceFactory();
+ xSMgr->createInstance("com.sun.star.bridge.OleApplicationRegistration");
+ xSMgr->createInstance("com.sun.star.comp.ole.EmbedServer");
+#endif
+ const char *pExitPostStartup = getenv ("OOO_EXIT_POST_STARTUP");
+ if (pExitPostStartup && *pExitPostStartup)
+ new ExitTimer();
+ } catch (const css::uno::Exception &e) {
+ OUString a( "UNO exception during client open:\n" );
+ Application::Abort( a + e.Message );
+ }
+}
+
+// enable acceptors
+IMPL_STATIC_LINK_NOARG(Desktop, EnableAcceptors_Impl, void*, void)
+{
+ enableAcceptors();
+}
+
+void Desktop::OpenClients()
+{
+
+ // check if a document has been recovered - if there is one of if a document was loaded by cmdline, no default document
+ // should be created
+ Reference < XComponent > xFirst;
+ bool bRecovery = false;
+
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+
+ if (!rArgs.IsQuickstart())
+ {
+ bool bShowHelp = false;
+ OUStringBuffer aHelpURLBuffer;
+ if (rArgs.IsHelpWriter()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://swriter/start");
+ } else if (rArgs.IsHelpCalc()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://scalc/start");
+ } else if (rArgs.IsHelpDraw()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sdraw/start");
+ } else if (rArgs.IsHelpImpress()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://simpress/start");
+ } else if (rArgs.IsHelpBase()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sdatabase/start");
+ } else if (rArgs.IsHelpBasic()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://sbasic/start");
+ } else if (rArgs.IsHelpMath()) {
+ bShowHelp = true;
+ aHelpURLBuffer.append("vnd.sun.star.help://smath/start");
+ }
+ if (bShowHelp) {
+ aHelpURLBuffer.append("?Language=");
+ aHelpURLBuffer.append(utl::ConfigManager::getLocale());
+#if defined UNX
+ aHelpURLBuffer.append("&System=UNX");
+#elif defined WNT
+ aHelpURLBuffer.appendAscii("&System=WIN");
+#endif
+ Application::GetHelp()->Start(
+ aHelpURLBuffer.makeStringAndClear(), nullptr);
+ return;
+ }
+ }
+
+ // Disable AutoSave feature in case "--norestore" or a similar command line switch is set on the command line.
+ // The reason behind: AutoSave/EmergencySave/AutoRecovery share the same data.
+ // But the require that all documents, which are saved as backup should exists inside
+ // memory. May be this mechanism will be inconsistent if the configuration exists...
+ // but no document inside memory corresponds to this data.
+ // Further it's not acceptable to recover such documents without any UI. It can
+ // need some time, where the user won't see any results and wait for finishing the office startup...
+ bool bAllowRecoveryAndSessionManagement = ( !rArgs.IsNoRestore() ) && ( !rArgs.IsHeadless() );
+
+ // Enter safe mode if requested
+ if (rArgs.IsSafeMode())
+ handleSafeMode();
+
+
+#if HAVE_FEATURE_BREAKPAD
+ if (crashReportInfoExists())
+ handleCrashReport();
+#endif
+
+ if ( ! bAllowRecoveryAndSessionManagement )
+ {
+ try
+ {
+ Reference< XDispatch > xRecovery = css::frame::theAutoRecovery::get( ::comphelper::getProcessComponentContext() );
+ Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
+
+ css::util::URL aCmd;
+ aCmd.Complete = "vnd.sun.star.autorecovery:/disableRecovery";
+ xParser->parseStrict(aCmd);
+
+ xRecovery->dispatch(aCmd, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ catch(const css::uno::Exception& e)
+ {
+ SAL_WARN( "desktop.app", "Could not disable AutoRecovery." << e.Message);
+ }
+ }
+ else
+ {
+ bool bCrashed = false;
+ bool bExistsRecoveryData = false;
+ bool bExistsSessionData = false;
+ bool const bDisableRecovery = getenv("OOO_DISABLE_RECOVERY") != nullptr;
+
+ impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
+
+ if ( !bDisableRecovery &&
+ (
+ ( bExistsRecoveryData ) || // => crash with files => recovery
+ ( bCrashed ) // => crash without files => error report
+ )
+ )
+ {
+ try
+ {
+ bRecovery = impl_callRecoveryUI(
+ false , // false => force recovery instead of emergency save
+ bExistsRecoveryData);
+ }
+ catch(const css::uno::Exception& e)
+ {
+ SAL_WARN( "desktop.app", "Error during recovery" << e.Message);
+ }
+ }
+ else if (bExistsRecoveryData && bDisableRecovery && !rArgs.HasModuleParam())
+ // prevent new Writer doc
+ bRecovery = true;
+
+ Reference< XSessionManagerListener2 > xSessionListener;
+ try
+ {
+ // specifies whether the UI-interaction on Session shutdown is allowed
+ xSessionListener = SessionListener::createWithOnQuitFlag(
+ ::comphelper::getProcessComponentContext(), isUIOnSessionShutdownAllowed());
+ }
+ catch(const css::uno::Exception& e)
+ {
+ SAL_WARN( "desktop.app", "Registration of session listener failed" << e.Message);
+ }
+
+ if ( !bExistsRecoveryData && xSessionListener.is() )
+ {
+ // session management
+ try
+ {
+ xSessionListener->doRestore();
+ }
+ catch(const css::uno::Exception& e)
+ {
+ SAL_WARN( "desktop.app", "Error in session management" << e.Message);
+ }
+ }
+ }
+#if HAVE_FEATURE_BREAKPAD
+ CrashReporter::writeCommonInfo();
+#endif
+
+ RequestHandler::EnableRequests();
+
+ ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
+ aRequest.aOpenList = rArgs.GetOpenList();
+ aRequest.aViewList = rArgs.GetViewList();
+ aRequest.aStartList = rArgs.GetStartList();
+ aRequest.aPrintList = rArgs.GetPrintList();
+ aRequest.aPrintToList = rArgs.GetPrintToList();
+ aRequest.aPrinterName = rArgs.GetPrinterName();
+ aRequest.aForceOpenList = rArgs.GetForceOpenList();
+ aRequest.aForceNewList = rArgs.GetForceNewList();
+ aRequest.aConversionList = rArgs.GetConversionList();
+ aRequest.aConversionParams = rArgs.GetConversionParams();
+ aRequest.aConversionOut = rArgs.GetConversionOut();
+ aRequest.aInFilter = rArgs.GetInFilter();
+ aRequest.bTextCat = rArgs.IsTextCat();
+
+ if ( !aRequest.aOpenList.empty() ||
+ !aRequest.aViewList.empty() ||
+ !aRequest.aStartList.empty() ||
+ !aRequest.aPrintList.empty() ||
+ !aRequest.aForceOpenList.empty() ||
+ !aRequest.aForceNewList.empty() ||
+ ( !aRequest.aPrintToList.empty() && !aRequest.aPrinterName.isEmpty() ) ||
+ !aRequest.aConversionList.empty() )
+ {
+ if ( rArgs.HasModuleParam() )
+ {
+ SvtModuleOptions aOpt;
+
+ // Support command line parameters to start a module (as preselection)
+ if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
+ else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
+ else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
+ }
+
+ // check for printing disabled
+ if( ( !(aRequest.aPrintList.empty() && aRequest.aPrintToList.empty()) )
+ && Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
+ {
+ aRequest.aPrintList.clear();
+ aRequest.aPrintToList.clear();
+ ResMgr* pDtResMgr = GetDesktopResManager();
+ if( pDtResMgr )
+ {
+ ScopedVclPtrInstance< MessageDialog > aBox(nullptr, ResId(STR_ERR_PRINTDISABLED, *pDtResMgr));
+ aBox->Execute();
+ }
+ }
+
+ // Process request
+ if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) )
+ {
+ // Don't do anything if we have successfully called terminate at desktop:
+ return;
+ }
+ }
+
+ // no default document if a document was loaded by recovery or by command line or if soffice is used as server
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
+ Reference< XElementAccess > xList( xDesktop->getFrames(), UNO_QUERY_THROW );
+ if ( xList->hasElements() )
+ return;
+
+ if ( rArgs.IsQuickstart() || rArgs.IsInvisible() || Application::AnyInput( VclInputFlags::APPEVENT ) )
+ // soffice was started as tray icon ...
+ return;
+
+ if ( bRecovery )
+ {
+ ShowBackingComponent(nullptr);
+ }
+ else
+ {
+ OpenDefault();
+ }
+}
+
+void Desktop::OpenDefault()
+{
+ OUString aName;
+ SvtModuleOptions aOpt;
+
+ const CommandLineArgs& rArgs = GetCommandLineArgs();
+ if ( rArgs.IsNoDefault() ) return;
+ if ( rArgs.HasModuleParam() )
+ {
+ // Support new command line parameters to start a module
+ if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
+ else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
+ else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( rArgs.IsBase() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
+ else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
+ else if ( rArgs.IsMath() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::MATH );
+ else if ( rArgs.IsGlobal() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERGLOBAL );
+ else if ( rArgs.IsWeb() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERWEB );
+ }
+
+ if ( aName.isEmpty() )
+ {
+ // Old way to create a default document
+ if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
+ else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
+ aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
+ else
+ return;
+ }
+
+ ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
+ aRequest.aOpenList.push_back(aName);
+ RequestHandler::ExecuteCmdLineRequests(aRequest, false);
+}
+
+
+OUString GetURL_Impl(
+ const OUString& rName, boost::optional< OUString > const & cwdUrl )
+{
+ // if rName is a vnd.sun.star.script URL do not attempt to parse it
+ // as INetURLObj does not handle handle there URLs
+ if (rName.startsWith("vnd.sun.star.script"))
+ {
+ return rName;
+ }
+
+ // don't touch file urls, those should already be in internal form
+ // they won't get better here (#112849#)
+ if (comphelper::isFileUrl(rName))
+ {
+ return rName;
+ }
+
+ if ( rName.startsWith("service:"))
+ {
+ return rName;
+ }
+
+ // Add path separator to these directory and make given URL (rName) absolute by using of current working directory
+ // Attention: "setFinalSlash()" is necessary for calling "smartRel2Abs()"!!!
+ // Otherwhise last part will be ignored and wrong result will be returned!!!
+ // "smartRel2Abs()" interpret given URL as file not as path. So he truncate last element to get the base path ...
+ // But if we add a separator - he doesn't do it anymore.
+ INetURLObject aObj;
+ if (cwdUrl) {
+ aObj.SetURL(*cwdUrl);
+ aObj.setFinalSlash();
+ }
+
+ // Use the provided parameters for smartRel2Abs to support the usage of '%' in system paths.
+ // Otherwise this char won't get encoded and we are not able to load such files later,
+ bool bWasAbsolute;
+ INetURLObject aURL = aObj.smartRel2Abs( rName, bWasAbsolute, false, INetURLObject::WAS_ENCODED,
+ RTL_TEXTENCODING_UTF8, true );
+ OUString aFileURL = aURL.GetMainURL(INetURLObject::NO_DECODE);
+
+ ::osl::FileStatus aStatus( osl_FileStatus_Mask_FileURL );
+ ::osl::DirectoryItem aItem;
+ if( ::osl::FileBase::E_None == ::osl::DirectoryItem::get( aFileURL, aItem ) &&
+ ::osl::FileBase::E_None == aItem.getFileStatus( aStatus ) )
+ aFileURL = aStatus.getFileURL();
+
+ return aFileURL;
+}
+
+void Desktop::HandleAppEvent( const ApplicationEvent& rAppEvent )
+{
+ switch ( rAppEvent.GetEvent() )
+ {
+ case ApplicationEvent::Type::Accept:
+ // every time an accept parameter is used we create an acceptor
+ // with the corresponding accept-string
+ createAcceptor(rAppEvent.GetStringData());
+ break;
+ case ApplicationEvent::Type::Appear:
+ if ( !GetCommandLineArgs().IsInvisible() )
+ {
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ // find active task - the active task is always a visible task
+ Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+ Reference< css::frame::XFrame > xTask = xDesktop->getActiveFrame();
+ if ( !xTask.is() )
+ {
+ // get any task if there is no active one
+ Reference< css::container::XIndexAccess > xList( xDesktop->getFrames(), css::uno::UNO_QUERY );
+ if ( xList->getCount() > 0 )
+ xList->getByIndex(0) >>= xTask;
+ }
+
+ if ( xTask.is() )
+ {
+ Reference< css::awt::XTopWindow > xTop( xTask->getContainerWindow(), UNO_QUERY );
+ xTop->toFront();
+ }
+ else
+ {
+ // no visible task that could be activated found
+ Reference< css::awt::XWindow > xContainerWindow;
+ Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
+ if (xBackingFrame.is())
+ xContainerWindow = xBackingFrame->getContainerWindow();
+ if (xContainerWindow.is())
+ {
+ Reference< XController > xStartModule = StartModule::createWithParentWindow(xContext, xContainerWindow);
+ Reference< css::awt::XWindow > xBackingWin(xStartModule, UNO_QUERY);
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ // Because the backing component set the property "IsBackingMode" of the frame
+ // to true inside attachFrame(). But setComponent() reset this state every time ...
+ xBackingFrame->setComponent(xBackingWin, xStartModule);
+ xStartModule->attachFrame(xBackingFrame);
+ xContainerWindow->setVisible(true);
+
+ vcl::Window* pCompWindow = VCLUnoHelper::GetWindow(xBackingFrame->getComponentWindow());
+ if (pCompWindow)
+ pCompWindow->Update();
+ }
+ }
+ }
+ break;
+ case ApplicationEvent::Type::Help:
+ displayCmdlineHelp(rAppEvent.GetStringData());
+ break;
+ case ApplicationEvent::Type::Version:
+ displayVersion();
+ break;
+ case ApplicationEvent::Type::Open:
+ {
+ const CommandLineArgs& rCmdLine = GetCommandLineArgs();
+ if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
+ {
+ ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
+ std::vector<OUString> const & data(rAppEvent.GetStringsData());
+ docsRequest.aOpenList.insert(
+ docsRequest.aOpenList.end(), data.begin(), data.end());
+ RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
+ }
+ }
+ break;
+ case ApplicationEvent::Type::OpenHelpUrl:
+ // start help for a specific URL
+ Application::GetHelp()->Start(rAppEvent.GetStringData(), nullptr);
+ break;
+ case ApplicationEvent::Type::Print:
+ {
+ const CommandLineArgs& rCmdLine = GetCommandLineArgs();
+ if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
+ {
+ ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
+ std::vector<OUString> const & data(rAppEvent.GetStringsData());
+ docsRequest.aPrintList.insert(
+ docsRequest.aPrintList.end(), data.begin(), data.end());
+ RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
+ }
+ }
+ break;
+ case ApplicationEvent::Type::PrivateDoShutdown:
+ {
+ Desktop* pD = dynamic_cast<Desktop*>(GetpApp());
+ OSL_ENSURE( pD, "no desktop ?!?" );
+ if( pD )
+ pD->doShutdown();
+ }
+ break;
+ case ApplicationEvent::Type::QuickStart:
+ if ( !GetCommandLineArgs().IsInvisible() )
+ {
+ // If the office has been started the second time its command line arguments are sent through a pipe
+ // connection to the first office. We want to reuse the quickstart option for the first office.
+ // NOTICE: The quickstart service must be initialized inside the "main thread", so we use the
+ // application events to do this (they are executed inside main thread)!!!
+ // Don't start quickstart service if the user specified "--invisible" on the command line!
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::office::Quickstart::createStart(xContext, true/*Quickstart*/);
+ }
+ break;
+ case ApplicationEvent::Type::ShowDialog:
+ // ignore all errors here. It's clicking a menu entry only ...
+ // The user will try it again, in case nothing happens .-)
+ try
+ {
+ Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
+
+ Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext);
+ css::util::URL aCommand;
+ if( rAppEvent.GetStringData() == "PREFERENCES" )
+ aCommand.Complete = ".uno:OptionsTreeDialog";
+ else if( rAppEvent.GetStringData() == "ABOUT" )
+ aCommand.Complete = ".uno:About";
+ if( !aCommand.Complete.isEmpty() )
+ {
+ xParser->parseStrict(aCommand);
+
+ css::uno::Reference< css::frame::XDispatch > xDispatch = xDesktop->queryDispatch(aCommand, OUString(), 0);
+ if (xDispatch.is())
+ xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
+ }
+ }
+ catch(const css::uno::Exception&)
+ {}
+ break;
+ case ApplicationEvent::Type::Unaccept:
+ // try to remove corresponding acceptor
+ destroyAcceptor(rAppEvent.GetStringData());
+ break;
+ default:
+ SAL_WARN( "desktop.app", "this cannot happen");
+ break;
+ }
+}
+
+void Desktop::OpenSplashScreen()
+{
+ const CommandLineArgs &rCmdLine = GetCommandLineArgs();
+ // Show intro only if this is normal start (e.g. no server, no quickstart, no printing )
+ if ( !rCmdLine.IsInvisible() &&
+ !rCmdLine.IsHeadless() &&
+ !rCmdLine.IsQuickstart() &&
+ !rCmdLine.IsMinimized() &&
+ !rCmdLine.IsNoLogo() &&
+ !rCmdLine.IsTerminateAfterInit() &&
+ rCmdLine.GetPrintList().empty() &&
+ rCmdLine.GetPrintToList().empty() &&
+ rCmdLine.GetConversionList().empty() )
+ {
+ // Determine application name from command line parameters
+ OUString aAppName;
+ if ( rCmdLine.IsWriter() )
+ aAppName = "writer";
+ else if ( rCmdLine.IsCalc() )
+ aAppName = "calc";
+ else if ( rCmdLine.IsDraw() )
+ aAppName = "draw";
+ else if ( rCmdLine.IsImpress() )
+ aAppName = "impress";
+ else if ( rCmdLine.IsBase() )
+ aAppName = "base";
+ else if ( rCmdLine.IsGlobal() )
+ aAppName = "global";
+ else if ( rCmdLine.IsMath() )
+ aAppName = "math";
+ else if ( rCmdLine.IsWeb() )
+ aAppName = "web";
+
+ // Which splash to use
+ OUString aSplashService( "com.sun.star.office.SplashScreen" );
+ if ( rCmdLine.HasSplashPipe() )
+ aSplashService = "com.sun.star.office.PipeSplashScreen";
+
+ bool bVisible = true;
+ Sequence< Any > aSeq( 2 );
+ aSeq[0] <<= bVisible;
+ aSeq[1] <<= aAppName;
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ m_rSplashScreen.set(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aSplashService, aSeq, xContext),
+ UNO_QUERY);
+
+ if(m_rSplashScreen.is())
+ m_rSplashScreen->start("SplashScreen", 100);
+ }
+
+}
+
+void Desktop::SetSplashScreenProgress(sal_Int32 iProgress)
+{
+ if(m_rSplashScreen.is())
+ {
+ m_rSplashScreen->setValue(iProgress);
+ }
+}
+
+void Desktop::SetSplashScreenText( const OUString& rText )
+{
+ if( m_rSplashScreen.is() )
+ {
+ m_rSplashScreen->setText( rText );
+ }
+}
+
+void Desktop::CloseSplashScreen()
+{
+ if(m_rSplashScreen.is())
+ {
+ m_rSplashScreen->end();
+ m_rSplashScreen = nullptr;
+ }
+}
+
+
+void Desktop::DoFirstRunInitializations()
+{
+ try
+ {
+ Reference< XJobExecutor > xExecutor = theJobExecutor::get( ::comphelper::getProcessComponentContext() );
+ xExecutor->trigger( "onFirstRunInitialization" );
+ }
+ catch(const css::uno::Exception&)
+ {
+ SAL_WARN( "desktop.app", "Desktop::DoFirstRunInitializations: caught an exception while trigger job executor ..." );
+ }
+}
+
+void Desktop::ShowBackingComponent(Desktop * progress)
+{
+ if (GetCommandLineArgs().IsNoDefault())
+ {
+ return;
+ }
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+ Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(60);
+ }
+ Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
+ Reference< css::awt::XWindow > xContainerWindow;
+
+ if (xBackingFrame.is())
+ xContainerWindow = xBackingFrame->getContainerWindow();
+ if (xContainerWindow.is())
+ {
+ // set the WB_EXT_DOCUMENT style. Normally, this is done by the TaskCreator service when a "_blank"
+ // frame/window is created. Since we do not use the TaskCreator here, we need to mimic its behavior,
+ // otherwise documents loaded into this frame will later on miss functionality depending on the style.
+ vcl::Window* pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
+ SAL_WARN_IF( !pContainerWindow, "desktop.app", "Desktop::Main: no implementation access to the frame's container window!" );
+ pContainerWindow->SetExtendedStyle( pContainerWindow->GetExtendedStyle() | WB_EXT_DOCUMENT );
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(75);
+ }
+
+ Reference< XController > xStartModule = StartModule::createWithParentWindow( xContext, xContainerWindow);
+ // Attention: You MUST(!) call setComponent() before you call attachFrame().
+ // Because the backing component set the property "IsBackingMode" of the frame
+ // to true inside attachFrame(). But setComponent() reset this state everytimes ...
+ xBackingFrame->setComponent(Reference< XWindow >(xStartModule, UNO_QUERY), xStartModule);
+ if (progress != nullptr)
+ {
+ progress->SetSplashScreenProgress(100);
+ }
+ xStartModule->attachFrame(xBackingFrame);
+ if (progress != nullptr)
+ {
+ progress->CloseSplashScreen();
+ }
+ xContainerWindow->setVisible(true);
+ }
+}
+
+
+void Desktop::CheckFirstRun( )
+{
+ if (officecfg::Office::Common::Misc::FirstRun::get())
+ {
+ // use VCL timer, which won't trigger during shutdown if the
+ // application exits before timeout
+ m_firstRunTimer.SetTimeout(3000); // 3 sec.
+ m_firstRunTimer.SetTimeoutHdl(LINK(this, Desktop, AsyncInitFirstRun));
+ m_firstRunTimer.Start();
+
+#ifdef _WIN32
+ // Check if Quickstarter should be started (on Windows only)
+ TCHAR szValue[8192];
+ DWORD nValueSize = sizeof(szValue);
+ HKEY hKey;
+ if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\LibreOffice", &hKey ) )
+ {
+ if ( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("RunQuickstartAtFirstStart"), NULL, NULL, (LPBYTE)szValue, &nValueSize ) )
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::office::Quickstart::createAutoStart(xContext, true/*Quickstart*/, true/*bAutostart*/);
+ RegCloseKey( hKey );
+ }
+ }
+#endif
+
+ std::shared_ptr< comphelper::ConfigurationChanges > batch(
+ comphelper::ConfigurationChanges::create());
+ officecfg::Office::Common::Misc::FirstRun::set(false, batch);
+ batch->commit();
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/include/comphelper/backupfilehelper.hxx b/include/comphelper/backupfilehelper.hxx
index 7cb20231927b..503bfc672549 100644
--- a/include/comphelper/backupfilehelper.hxx
+++ b/include/comphelper/backupfilehelper.hxx
@@ -144,8 +144,14 @@ namespace comphelper
private:
// internal helper methods
bool splitBaseURL();
- bool baseFileExists();
- rtl::OUString getName();
+ const rtl::OUString getPackDirName() const;
+ const rtl::OUString getPackFileName(const rtl::OUString& rFileName) const;
+ bool tryPush_basefile(bool bCompress);
+ bool tryPush_extensionInfo(bool bCompress);
+ bool isPopPossible_basefile();
+ bool isPopPossible_extensionInfo();
+ bool tryPop_basefile();
+ bool tryPop_extensionInfo();
};
}