1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <config_features.h>
22 #include <config_feature_desktop.h>
23 #include <config_feature_opencl.h>
24 #include <config_java.h>
25 #include <config_folders.h>
26 #include <config_extensions.h>
27 
28 #include <sal/config.h>
29 
30 #include <iostream>
31 #include <mutex>
32 #if defined UNX
33 #include <signal.h>
34 #endif
35 
36 #include <app.hxx>
37 #include <dp_shared.hxx>
38 #include <strings.hrc>
39 #include "cmdlineargs.hxx"
40 #include "cmdlinehelp.hxx"
41 #include "dispatchwatcher.hxx"
42 #include <lockfile.hxx>
43 #include "userinstall.hxx"
44 #include "desktopcontext.hxx"
45 #include <migration.hxx>
46 #if HAVE_FEATURE_UPDATE_MAR
47 #include "updater.hxx"
48 #endif
49 
50 #include <i18nlangtag/languagetag.hxx>
51 #include <o3tl/runtimetooustring.hxx>
52 #include <svl/languageoptions.hxx>
53 #include <svtools/javacontext.hxx>
54 #include <com/sun/star/beans/XPropertySet.hpp>
55 #include <com/sun/star/frame/theAutoRecovery.hpp>
56 #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
57 #include <com/sun/star/frame/SessionListener.hpp>
58 #include <com/sun/star/frame/XSynchronousDispatch.hpp>
59 #include <com/sun/star/document/CorruptedFilterConfigurationException.hpp>
60 #include <com/sun/star/configuration/CorruptedConfigurationException.hpp>
61 #include <com/sun/star/configuration/theDefaultProvider.hpp>
62 #include <com/sun/star/util/XFlushable.hpp>
63 #include <com/sun/star/util/XModifiable.hpp>
64 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
65 #include <com/sun/star/frame/Desktop.hpp>
66 #include <com/sun/star/frame/StartModule.hpp>
67 #include <com/sun/star/view/XPrintable.hpp>
68 #include <com/sun/star/awt/XTopWindow.hpp>
69 #include <com/sun/star/util/URLTransformer.hpp>
70 #include <com/sun/star/util/XURLTransformer.hpp>
71 #include <com/sun/star/lang/ServiceNotRegisteredException.hpp>
72 #include <com/sun/star/configuration/MissingBootstrapFileException.hpp>
73 #include <com/sun/star/configuration/InvalidBootstrapFileException.hpp>
74 #include <com/sun/star/configuration/InstallationIncompleteException.hpp>
75 #include <com/sun/star/configuration/backend/BackendSetupException.hpp>
76 #include <com/sun/star/configuration/backend/BackendAccessException.hpp>
77 #include <com/sun/star/task/theJobExecutor.hpp>
78 #include <com/sun/star/task/OfficeRestartManager.hpp>
79 #include <com/sun/star/task/XRestartManager.hpp>
80 #include <com/sun/star/document/XDocumentEventListener.hpp>
81 #include <com/sun/star/ui/theUIElementFactoryManager.hpp>
82 #include <com/sun/star/ui/theWindowStateConfiguration.hpp>
83 #include <com/sun/star/office/Quickstart.hpp>
84 #include <com/sun/star/system/XSystemShellExecute.hpp>
85 #include <com/sun/star/system/SystemShellExecute.hpp>
86 
87 #include <desktop/exithelper.h>
88 #include <sal/log.hxx>
89 #include <toolkit/helper/vclunohelper.hxx>
90 #include <comphelper/configuration.hxx>
91 #include <comphelper/fileurl.hxx>
92 #include <comphelper/threadpool.hxx>
93 #include <comphelper/processfactory.hxx>
94 #include <comphelper/backupfilehelper.hxx>
95 #include <unotools/bootstrap.hxx>
96 #include <unotools/configmgr.hxx>
97 #include <unotools/moduleoptions.hxx>
98 #include <unotools/localfilehelper.hxx>
99 #include <unotools/ucbhelper.hxx>
100 #include <officecfg/Office/Common.hxx>
101 #include <officecfg/Office/Recovery.hxx>
102 #include <officecfg/Office/Update.hxx>
103 #include <officecfg/Setup.hxx>
104 #include <osl/file.hxx>
105 #include <osl/process.h>
106 #include <rtl/byteseq.hxx>
107 #include <rtl/uri.hxx>
108 #include <unotools/pathoptions.hxx>
109 #include <svtools/miscopt.hxx>
110 #include <svtools/menuoptions.hxx>
111 #include <rtl/bootstrap.hxx>
112 #include <vcl/glxtestprocess.hxx>
113 #include <vcl/help.hxx>
114 #include <vcl/weld.hxx>
115 #include <vcl/settings.hxx>
116 #include <sfx2/flatpak.hxx>
117 #include <sfx2/sfxsids.hrc>
118 #include <sfx2/app.hxx>
119 #include <sfx2/safemode.hxx>
120 #include <svl/itemset.hxx>
121 #include <svl/eitem.hxx>
122 #include <basic/sbstar.hxx>
123 #include <desktop/crashreport.hxx>
124 #include <tools/urlobj.hxx>
125 #include <tools/diagnose_ex.h>
126 #include <svtools/fontsubstconfig.hxx>
127 #include <svtools/accessibilityoptions.hxx>
128 #include <svtools/apearcfg.hxx>
129 #include <vcl/graphicfilter.hxx>
130 #include <vcl/window.hxx>
131 #include "langselect.hxx"
132 
133 #if defined MACOSX
134 #include <errno.h>
135 #include <sys/wait.h>
136 #endif
137 
138 #ifdef _WIN32
139 #define WIN32_LEAN_AND_MEAN
140 #include <windows.h>
141 #endif
142 
143 #if defined(_WIN32)
144 #include <process.h>
145 #define GETPID _getpid
146 #else
147 #include <unistd.h>
148 #define GETPID getpid
149 #endif
150 
151 #include <strings.hxx>
152 
153 using namespace ::com::sun::star::awt;
154 using namespace ::com::sun::star::uno;
155 using namespace ::com::sun::star::util;
156 using namespace ::com::sun::star::lang;
157 using namespace ::com::sun::star::beans;
158 using namespace ::com::sun::star::frame;
159 using namespace ::com::sun::star::document;
160 using namespace ::com::sun::star::view;
161 using namespace ::com::sun::star::task;
162 using namespace ::com::sun::star::system;
163 using namespace ::com::sun::star::ui;
164 using namespace ::com::sun::star::ui::dialogs;
165 using namespace ::com::sun::star::container;
166 
167 namespace desktop
168 {
169 
170 static oslSignalHandler pSignalHandler = nullptr;
171 
172 namespace {
173 
174 #if HAVE_FEATURE_EXTENSIONS
175 
176 // Remove any existing UserInstallation's extensions cache data remaining from
177 // old installations.  This addresses at least two problems:
178 //
179 // For one, apparently due to the old share/prereg/bundled mechanism (disabled
180 // since 5c47e5f63a79a9e72ec4a100786b1bbf65137ed4 "fdo#51252 Disable copying
181 // share/prereg/bundled to avoid startup crashes"), the user/extensions/bundled
182 // cache could contain corrupted information (like a UNO component registered
183 // twice, which got changed from active to passive registration in one LO
184 // version, but the version of the corresponding bundled extension only
185 // incremented in a later LO version).
186 //
187 // For another, UserInstallations have been seen in the wild where no extensions
188 // were installed per-user (any longer), but user/uno_packages/cache/registry/
189 // com.sun.star.comp.deployment.component.PackageRegistryBackend/*.rdb files
190 // contained data nevertheless.
191 //
192 // When a LO upgrade is detected (i.e., no user/extensions/buildid or one
193 // containing an old build ID), then user/extensions and
194 // user/uno_packages/cache/registry/
195 // com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc are
196 // removed.  That should prevent any problems starting the service manager due
197 // to old junk.  Later on in Desktop::SynchronizeExtensionRepositories, the
198 // removed cache data is recreated.
199 //
200 // Multiple instances of soffice.bin can execute this code in parallel for a
201 // single UserInstallation, as it is called before RequestHandler is set up.
202 // Therefore, any errors here only lead to SAL_WARNs.
203 //
204 // At least in theory, this function could be removed again once no
205 // UserInstallation can be poisoned by old junk any more.
cleanExtensionCache()206 bool cleanExtensionCache() {
207     OUString buildId(
208         "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
209     rtl::Bootstrap::expandMacros(buildId); //TODO: detect failure
210     OUString extDir(
211         "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap")
212         ":UserInstallation}/user/extensions");
213     rtl::Bootstrap::expandMacros(extDir); //TODO: detect failure
214     OUString buildIdFile(extDir + "/buildid");
215     osl::File fr(buildIdFile);
216     osl::FileBase::RC rc = fr.open(osl_File_OpenFlag_Read);
217     switch (rc) {
218     case osl::FileBase::E_None:
219         {
220             rtl::ByteSequence s1;
221             rc = fr.readLine(s1);
222             osl::FileBase::RC rc2 = fr.close();
223             SAL_WARN_IF(
224                 rc2 != osl::FileBase::E_None, "desktop.app",
225                 "cannot close " << fr.getURL() << " after reading: " << +rc2);
226             // readLine returns E_AGAIN for a zero-size file:
227             if (rc != osl::FileBase::E_None && rc != osl::FileBase::E_AGAIN) {
228                 SAL_WARN( "desktop.app", "cannot read from " << fr.getURL() << ": " << +rc);
229                 break;
230             }
231             OUString s2(
232                 reinterpret_cast< char const * >(s1.getConstArray()),
233                 s1.getLength(), RTL_TEXTENCODING_ISO_8859_1);
234                 // using ISO 8859-1 avoids any and all conversion errors; the
235                 // content should only be a subset of ASCII, anyway
236             if (s2 == buildId) {
237                 return false;
238             }
239             break;
240         }
241     case osl::FileBase::E_NOENT:
242         break;
243     default:
244         SAL_WARN( "desktop.app", "cannot open " << fr.getURL() << " for reading: " << +rc);
245         break;
246     }
247     utl::removeTree(extDir);
248     OUString userRcFile(
249         "$UNO_USER_PACKAGES_CACHE/registry/"
250         "com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc");
251     rtl::Bootstrap::expandMacros(userRcFile); //TODO: detect failure
252     rc = osl::File::remove(userRcFile);
253     SAL_WARN_IF(
254         rc != osl::FileBase::E_None && rc != osl::FileBase::E_NOENT, "desktop.app",
255         "cannot remove file " << userRcFile << ": " << +rc);
256     rc = osl::Directory::createPath(extDir);
257     SAL_WARN_IF(
258         rc != osl::FileBase::E_None && rc != osl::FileBase::E_EXIST, "desktop.app",
259         "cannot create path " << extDir << ": " << +rc);
260     osl::File fw(buildIdFile);
261     rc = fw.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
262     if (rc != osl::FileBase::E_None) {
263         SAL_WARN( "desktop.app", "cannot open " << fw.getURL() << " for writing: " << +rc);
264         return true;
265     }
266     OString buf(OUStringToOString(buildId, RTL_TEXTENCODING_UTF8));
267         // using UTF-8 avoids almost all conversion errors (and buildid
268         // containing single surrogate halves should never happen, anyway); the
269         // content should only be a subset of ASCII, anyway
270     sal_uInt64 n = 0;
271     rc = fw.write(buf.getStr(), buf.getLength(), n);
272     SAL_WARN_IF(
273         (rc != osl::FileBase::E_None
274          || n != static_cast< sal_uInt32 >(buf.getLength())),
275         "desktop.app",
276         "cannot write to " << fw.getURL() << ": " << +rc << ", " << n);
277     rc = fw.close();
278     SAL_WARN_IF(
279         rc != osl::FileBase::E_None, "desktop.app",
280         "cannot close " << fw.getURL() << " after writing: " << +rc);
281     return true;
282 }
283 
284 #endif
285 
shouldLaunchQuickstart()286 bool shouldLaunchQuickstart()
287 {
288     bool bQuickstart = Desktop::GetCommandLineArgs().IsQuickstart();
289     if (!bQuickstart)
290     {
291         const SfxPoolItem* pItem=nullptr;
292         SfxItemSet aQLSet(SfxGetpApp()->GetPool(), svl::Items<SID_ATTR_QUICKLAUNCHER, SID_ATTR_QUICKLAUNCHER>{});
293         SfxGetpApp()->GetOptions(aQLSet);
294         SfxItemState eState = aQLSet.GetItemState(SID_ATTR_QUICKLAUNCHER, false, &pItem);
295         if (SfxItemState::SET == eState)
296             bQuickstart = static_cast<const SfxBoolItem*>(pItem)->GetValue();
297     }
298     return bQuickstart;
299 }
300 
SetRestartState()301 void SetRestartState() {
302     try {
303         std::shared_ptr< comphelper::ConfigurationChanges > batch(
304             comphelper::ConfigurationChanges::create());
305         officecfg::Setup::Office::OfficeRestartInProgress::set(true, batch);
306         batch->commit();
307     } catch (css::uno::Exception) {
308         TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
309     }
310 }
311 
DoRestartActionsIfNecessary(bool quickstart)312 void DoRestartActionsIfNecessary(bool quickstart) {
313     if (quickstart) {
314         try {
315             if (officecfg::Setup::Office::OfficeRestartInProgress::get()) {
316                 std::shared_ptr< comphelper::ConfigurationChanges > batch(
317                     comphelper::ConfigurationChanges::create());
318                 officecfg::Setup::Office::OfficeRestartInProgress::set(
319                     false, batch);
320                 batch->commit();
321                 css::office::Quickstart::createStart(
322                     comphelper::getProcessComponentContext(),
323                     shouldLaunchQuickstart());
324             }
325         } catch (css::uno::Exception &) {
326             TOOLS_WARN_EXCEPTION("desktop.app", "ignoring");
327         }
328     }
329 }
330 
RemoveIconCacheDirectory()331 void RemoveIconCacheDirectory()
332 {
333     // See getIconCacheUrl in vcl/source/image/ImplImageTree.cxx
334     OUString sUrl = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER
335         "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/cache";
336     rtl::Bootstrap::expandMacros(sUrl);
337     utl::UCBContentHelper::Kill(sUrl);
338 }
339 
340 }
341 
342 namespace {
343 
344 
MakeStartupErrorMessage(OUString const & aErrorMessage)345 OUString MakeStartupErrorMessage(OUString const & aErrorMessage)
346 {
347     return DpResId(STR_BOOTSTRAP_ERR_CANNOT_START) + "\n" + aErrorMessage;
348 }
349 
MakeStartupConfigAccessErrorMessage(OUString const & aInternalErrMsg)350 OUString MakeStartupConfigAccessErrorMessage( OUString const & aInternalErrMsg )
351 {
352     OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_CFG_DATAACCESS);
353     if ( !aInternalErrMsg.isEmpty() )
354     {
355         aDiagnosticMessage += "\n\n"
356                            + DpResId(STR_INTERNAL_ERRMSG)
357                            + aInternalErrMsg;
358     }
359     return aDiagnosticMessage;
360 }
361 
362 
363 // shows a simple error box with the given message ... but exits from these process !
364 // Fatal errors can't be solved by the process ... nor any recovery can help.
365 // Mostly the installation was damaged and must be repaired manually .. or by calling
366 // setup again.
367 // On the other side we must make sure that no further actions will be possible within
368 // the current office process ! No pipe requests, no menu/toolbar/shortcut actions
369 // are allowed. Otherwise we will force a "crash inside a crash".
370 // That's why we have to use a special native message box here which does not use yield :-)
371 
FatalError(const OUString & sMessage)372 void FatalError(const OUString& sMessage)
373 {
374     OUString sProductKey = ::utl::Bootstrap::getProductKey();
375     if ( sProductKey.isEmpty())
376     {
377         osl_getExecutableFile( &sProductKey.pData );
378 
379         ::sal_uInt32 nLastIndex = sProductKey.lastIndexOf('/');
380         if ( nLastIndex > 0 )
381             sProductKey = sProductKey.copy( nLastIndex+1 );
382     }
383 
384     OUString sTitle = sProductKey + " - Fatal Error";
385     Application::ShowNativeErrorBox (sTitle, sMessage);
386     std::cerr << sTitle << ": " << sMessage << std::endl;
387     _exit(EXITHELPER_FATAL_ERROR);
388 }
389 
390 struct theCommandLineArgs : public rtl::Static< CommandLineArgs, theCommandLineArgs > {};
391 
392 }
393 
GetCommandLineArgs()394 CommandLineArgs& Desktop::GetCommandLineArgs()
395 {
396     return theCommandLineArgs::get();
397 }
398 
399 namespace
400 {
401     struct OOOVendor
402         : public rtl::Static< OUString, OOOVendor > {};
403 }
404 
ReplaceStringHookProc(const OUString & rStr)405 OUString ReplaceStringHookProc( const OUString& rStr )
406 {
407     const static OUString sBuildId(utl::Bootstrap::getBuildIdData("development"));
408     static OUString sBrandName, sVersion, sAboutBoxVersion, sAboutBoxVersionSuffix, sExtension;
409 
410     static std::once_flag aInitOnce;
411     std::call_once(aInitOnce, []
412     {
413         sBrandName = utl::ConfigManager::getProductName();
414         sVersion = utl::ConfigManager::getProductVersion();
415         sAboutBoxVersion = utl::ConfigManager::getAboutBoxProductVersion();
416         sAboutBoxVersionSuffix = utl::ConfigManager::getAboutBoxProductVersionSuffix();
417         sExtension = utl::ConfigManager::getProductExtension();
418     } );
419 
420     OUString sRet(rStr);
421     if (sRet.indexOf("%PRODUCT") != -1 || sRet.indexOf("%ABOUTBOX") != -1)
422     {
423         sRet = sRet.replaceAll( "%PRODUCTNAME", sBrandName );
424         sRet = sRet.replaceAll( "%PRODUCTVERSION", sVersion );
425         sRet = sRet.replaceAll( "%BUILDID", sBuildId );
426         sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSIONSUFFIX", sAboutBoxVersionSuffix );
427         sRet = sRet.replaceAll( "%ABOUTBOXPRODUCTVERSION", sAboutBoxVersion );
428         sRet = sRet.replaceAll( "%PRODUCTEXTENSION", sExtension );
429     }
430 
431     if ( sRet.indexOf( "%OOOVENDOR" ) != -1 )
432     {
433         OUString sOOOVendor = OOOVendor::get();
434 
435         if ( sOOOVendor.isEmpty() )
436         {
437             sOOOVendor = utl::ConfigManager::getVendor();
438         }
439 
440         sRet = sRet.replaceAll( "%OOOVENDOR", sOOOVendor );
441     }
442 
443     return sRet;
444 }
445 
Desktop()446 Desktop::Desktop()
447     : m_bCleanedExtensionCache(false)
448     , m_bServicesRegistered(false)
449     , m_aBootstrapError(BE_OK)
450     , m_aBootstrapStatus(BS_OK)
451 {
452     m_firstRunTimer.SetTimeout(3000); // 3 sec.
453     m_firstRunTimer.SetInvokeHandler(LINK(this, Desktop, AsyncInitFirstRun));
454     m_firstRunTimer.SetDebugName( "desktop::Desktop m_firstRunTimer" );
455 }
456 
~Desktop()457 Desktop::~Desktop()
458 {
459 }
460 
Init()461 void Desktop::Init()
462 {
463     SetBootstrapStatus(BS_OK);
464 
465 #if HAVE_FEATURE_EXTENSIONS
466     m_bCleanedExtensionCache = cleanExtensionCache();
467 #endif
468 
469     // We need to have service factory before going further, but see fdo#37195.
470     // Doing this will mmap common.rdb, making it not overwritable on windows,
471     // so this can't happen before the synchronization above. Lets rework this
472     // so that the above is called *from* CreateApplicationServiceManager or
473     // something to enforce this gotcha
474     try
475     {
476         InitApplicationServiceManager();
477     }
478     catch (css::uno::Exception & e)
479     {
480         SetBootstrapError( BE_UNO_SERVICEMANAGER, e.Message );
481     }
482 
483     // Check whether safe mode is enabled
484     const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
485     // Check if we are restarting from safe mode - in that case we don't want to enter it again
486     if (sfx2::SafeMode::hasRestartFlag())
487         sfx2::SafeMode::removeRestartFlag();
488     else if (rCmdLineArgs.IsSafeMode() || sfx2::SafeMode::hasFlag())
489         Application::EnableSafeMode();
490 
491     // When we are in SafeMode we need to do changes before the configuration
492     // gets read (langselect::prepareLocale() by UNO API -> Components::Components)
493     // This may prepare SafeMode or restore from it by moving data in
494     // the UserConfiguration directory
495     comphelper::BackupFileHelper::reactOnSafeMode(Application::IsSafeModeEnabled());
496 
497     if ( m_aBootstrapError == BE_OK )
498     {
499         try
500         {
501             if (!langselect::prepareLocale())
502             {
503                 SetBootstrapError( BE_LANGUAGE_MISSING, OUString() );
504             }
505         }
506         catch (css::uno::Exception & e)
507         {
508             SetBootstrapError( BE_OFFICECONFIG_BROKEN, e.Message );
509         }
510 
511         // test code for ProfileSafeMode to allow testing the fail
512         // of loading the office configuration initially. To use,
513         // either set to true and compile, or set a breakpoint
514         // in debugger and change the local bool
515         static bool bTryHardOfficeconfigBroken(false); // loplugin:constvars:ignore
516 
517         if (bTryHardOfficeconfigBroken)
518         {
519             SetBootstrapError(BE_OFFICECONFIG_BROKEN, OUString());
520         }
521     }
522 
523     if ( true )
524     {
525         // start ipc thread only for non-remote offices
526         RequestHandler::Status aStatus = RequestHandler::Enable(true);
527         if ( aStatus == RequestHandler::IPC_STATUS_PIPE_ERROR )
528         {
529 #if defined ANDROID
530             // Ignore crack pipe errors on Android
531 #else
532             // Keep using this oddly named BE_PATHINFO_MISSING value
533             // for pipe-related errors on other platforms. Of course
534             // this crack with two (if not more) levels of our own
535             // error codes hiding the actual system error code is
536             // broken, but that is done all over the code, let's leave
537             // reengineering that to another year.
538             SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
539 #endif
540         }
541         else if ( aStatus == RequestHandler::IPC_STATUS_BOOTSTRAP_ERROR )
542         {
543             SetBootstrapError( BE_PATHINFO_MISSING, OUString() );
544         }
545         else if ( aStatus == RequestHandler::IPC_STATUS_2ND_OFFICE )
546         {
547             // 2nd office startup should terminate after sending cmdlineargs through pipe
548             SetBootstrapStatus(BS_TERMINATE);
549         }
550         else if ( !rCmdLineArgs.GetUnknown().isEmpty()
551                   || rCmdLineArgs.IsHelp() || rCmdLineArgs.IsVersion() )
552         {
553             // disable IPC thread in an instance that is just showing a help message
554             RequestHandler::Disable();
555         }
556         pSignalHandler = osl_addSignalHandler(SalMainPipeExchangeSignal_impl, nullptr);
557     }
558 }
559 
InitFinished()560 void Desktop::InitFinished()
561 {
562     CloseSplashScreen();
563 }
564 
DeInit()565 void Desktop::DeInit()
566 {
567     try {
568         // instead of removing of the configManager just let it commit all the changes
569         utl::ConfigManager::storeConfigItems();
570         FlushConfiguration();
571 
572         // close splashscreen if it's still open
573         CloseSplashScreen();
574         Reference< XComponent >(
575             comphelper::getProcessComponentContext(), UNO_QUERY_THROW )->
576             dispose();
577         // nobody should get a destroyed service factory...
578         ::comphelper::setProcessServiceFactory( nullptr );
579 
580         // clear lockfile
581         m_xLockfile.reset();
582 
583         RequestHandler::Disable();
584         if( pSignalHandler )
585             osl_removeSignalHandler( pSignalHandler );
586     } catch (const RuntimeException&) {
587         // someone threw an exception during shutdown
588         // this will leave some garbage behind...
589     }
590 }
591 
QueryExit()592 bool Desktop::QueryExit()
593 {
594     try
595     {
596         utl::ConfigManager::storeConfigItems();
597     }
598     catch ( const RuntimeException& )
599     {
600     }
601 
602     const sal_Char SUSPEND_QUICKSTARTVETO[] = "SuspendQuickstartVeto";
603 
604     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
605     Reference< XPropertySet > xPropertySet(xDesktop, UNO_QUERY_THROW);
606     xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(true) );
607 
608     bool bExit = xDesktop->terminate();
609 
610     if ( !bExit )
611     {
612         xPropertySet->setPropertyValue( SUSPEND_QUICKSTARTVETO, Any(false) );
613     }
614     else if (!Application::IsEventTestingModeEnabled())
615     {
616         FlushConfiguration();
617         try
618         {
619             // it is no problem to call RequestHandler::Disable() more than once
620             // it also looks to be threadsafe
621             RequestHandler::Disable();
622         }
623         catch ( const RuntimeException& )
624         {
625         }
626 
627         m_xLockfile.reset();
628 
629     }
630 
631     return bExit;
632 }
633 
HandleBootstrapPathErrors(::utl::Bootstrap::Status aBootstrapStatus,const OUString & aDiagnosticMessage)634 void Desktop::HandleBootstrapPathErrors( ::utl::Bootstrap::Status aBootstrapStatus, const OUString& aDiagnosticMessage )
635 {
636     if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
637     {
638         OUString        aProductKey;
639         OUString        aTemp;
640 
641         osl_getExecutableFile( &aProductKey.pData );
642         sal_uInt32     lastIndex = aProductKey.lastIndexOf('/');
643         if ( lastIndex > 0 )
644             aProductKey = aProductKey.copy( lastIndex+1 );
645 
646         aTemp = ::utl::Bootstrap::getProductKey( aProductKey );
647         if ( !aTemp.isEmpty() )
648             aProductKey = aTemp;
649 
650         OUString const aMessage(aDiagnosticMessage + "\n");
651 
652         std::unique_ptr<weld::MessageDialog> xBootstrapFailedBox(Application::CreateMessageDialog(nullptr,
653                                                                  VclMessageType::Warning, VclButtonsType::Ok, aMessage));
654         xBootstrapFailedBox->set_title(aProductKey);
655         xBootstrapFailedBox->run();
656     }
657 }
658 
659 // Create an error message depending on bootstrap failure code and an optional file url
CreateErrorMsgString(utl::Bootstrap::FailureCode nFailureCode,const OUString & aFileURL)660 OUString    Desktop::CreateErrorMsgString(
661     utl::Bootstrap::FailureCode nFailureCode,
662     const OUString& aFileURL )
663 {
664     OUString        aMsg;
665     OUString        aFilePath;
666     bool            bFileInfo = true;
667 
668     switch ( nFailureCode )
669     {
670         /// the shared installation directory could not be located
671         case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
672         {
673             aMsg = DpResId(STR_BOOTSTRAP_ERR_PATH_INVALID);
674             bFileInfo = false;
675         }
676         break;
677 
678         /// the bootstrap INI file could not be found or read
679         case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
680         {
681             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
682         }
683         break;
684 
685         /// the bootstrap INI is missing a required entry
686         /// the bootstrap INI contains invalid data
687          case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
688          case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
689         {
690             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_CORRUPT);
691         }
692         break;
693 
694         /// the version locator INI file could not be found or read
695         case ::utl::Bootstrap::MISSING_VERSION_FILE:
696         {
697             aMsg = DpResId(STR_BOOTSTRAP_ERR_FILE_MISSING);
698         }
699         break;
700 
701         /// the version locator INI has no entry for this version
702         case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
703         {
704             aMsg = DpResId(STR_BOOTSTRAP_ERR_NO_SUPPORT);
705         }
706         break;
707 
708         /// the user installation directory does not exist
709         case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
710         {
711             aMsg = DpResId(STR_BOOTSTRAP_ERR_DIR_MISSING);
712         }
713         break;
714 
715         /// some bootstrap data was invalid in unexpected ways
716         case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
717         {
718             aMsg = DpResId(STR_BOOTSTRAP_ERR_INTERNAL);
719             bFileInfo = false;
720         }
721         break;
722 
723         case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
724         {
725             // This needs to be improved, see #i67575#:
726             aMsg = "Invalid version file entry";
727             bFileInfo = false;
728         }
729         break;
730 
731         case ::utl::Bootstrap::NO_FAILURE:
732         {
733             OSL_ASSERT(false);
734         }
735         break;
736     }
737 
738     if ( bFileInfo )
739     {
740         OUString aMsgString( aMsg );
741 
742         osl::File::getSystemPathFromFileURL( aFileURL, aFilePath );
743 
744         aMsgString = aMsgString.replaceFirst( "$1", aFilePath );
745         aMsg = aMsgString;
746     }
747 
748     return MakeStartupErrorMessage( aMsg );
749 }
750 
HandleBootstrapErrors(BootstrapError aBootstrapError,OUString const & aErrorMessage)751 void Desktop::HandleBootstrapErrors(
752     BootstrapError aBootstrapError, OUString const & aErrorMessage )
753 {
754     if ( aBootstrapError == BE_PATHINFO_MISSING )
755     {
756         OUString                    aErrorMsg;
757         OUString                    aBuffer;
758         utl::Bootstrap::Status        aBootstrapStatus;
759         utl::Bootstrap::FailureCode    nFailureCode;
760 
761         aBootstrapStatus = ::utl::Bootstrap::checkBootstrapStatus( aBuffer, nFailureCode );
762         if ( aBootstrapStatus != ::utl::Bootstrap::DATA_OK )
763         {
764             switch ( nFailureCode )
765             {
766                 case ::utl::Bootstrap::MISSING_INSTALL_DIRECTORY:
767                 case ::utl::Bootstrap::INVALID_BOOTSTRAP_DATA:
768                 {
769                     aErrorMsg = CreateErrorMsgString( nFailureCode, OUString() );
770                 }
771                 break;
772 
773                 /// the bootstrap INI file could not be found or read
774                 /// the bootstrap INI is missing a required entry
775                 /// the bootstrap INI contains invalid data
776                 case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY:
777                 case ::utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY:
778                 case ::utl::Bootstrap::MISSING_BOOTSTRAP_FILE:
779                 {
780                     OUString aBootstrapFileURL;
781 
782                     utl::Bootstrap::locateBootstrapFile( aBootstrapFileURL );
783                     aErrorMsg = CreateErrorMsgString( nFailureCode, aBootstrapFileURL );
784                 }
785                 break;
786 
787                 /// the version locator INI file could not be found or read
788                 /// the version locator INI has no entry for this version
789                 /// the version locator INI entry is not a valid directory URL
790                  case ::utl::Bootstrap::INVALID_VERSION_FILE_ENTRY:
791                  case ::utl::Bootstrap::MISSING_VERSION_FILE_ENTRY:
792                  case ::utl::Bootstrap::MISSING_VERSION_FILE:
793                 {
794                     OUString aVersionFileURL;
795 
796                     utl::Bootstrap::locateVersionFile( aVersionFileURL );
797                     aErrorMsg = CreateErrorMsgString( nFailureCode, aVersionFileURL );
798                 }
799                 break;
800 
801                 /// the user installation directory does not exist
802                  case ::utl::Bootstrap::MISSING_USER_DIRECTORY:
803                 {
804                     OUString aUserInstallationURL;
805 
806                     utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
807                     aErrorMsg = CreateErrorMsgString( nFailureCode, aUserInstallationURL );
808                 }
809                 break;
810 
811                 case ::utl::Bootstrap::NO_FAILURE:
812                 {
813                     OSL_ASSERT(false);
814                 }
815                 break;
816             }
817 
818             HandleBootstrapPathErrors( aBootstrapStatus, aErrorMsg );
819         }
820     }
821     else if ( aBootstrapError == BE_UNO_SERVICEMANAGER || aBootstrapError == BE_UNO_SERVICE_CONFIG_MISSING )
822     {
823         // UNO service manager is not available. VCL needs a UNO service manager to display a message box!!!
824         // Currently we are not able to display a message box with a service manager due to this limitations inside VCL.
825 
826         // When UNO is not properly initialized, all kinds of things can fail
827         // and cause the process to crash. To give the user a hint even if
828         // generating and displaying a message box below crashes, print a
829         // hard-coded message on stderr first:
830         std::cerr
831             << "The application cannot be started.\n"
832                 // STR_BOOTSTRAP_ERR_CANNOT_START
833             << (aBootstrapError == BE_UNO_SERVICEMANAGER
834                 ? "The component manager is not available.\n"
835                     // STR_BOOTSTRAP_ERR_NO_SERVICE
836                 : "The configuration service is not available.\n");
837                     // STR_BOOTSTRAP_ERR_NO_CFG_SERVICE
838         if ( !aErrorMessage.isEmpty() )
839         {
840             std::cerr << "(\"" << aErrorMessage << "\")\n";
841         }
842 
843         // First sentence. We cannot bootstrap office further!
844         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NO_CFG_SERVICE) + "\n";
845         if ( !aErrorMessage.isEmpty() )
846         {
847             aDiagnosticMessage += "(\"" + aErrorMessage + "\")\n";
848         }
849 
850         // Due to the fact the we haven't a backup applicat.rdb file anymore it is not possible to
851         // repair the installation with the setup executable besides the office executable. Now
852         // we have to ask the user to start the setup on CD/installation directory manually!!
853         aDiagnosticMessage += DpResId(STR_ASK_START_SETUP_MANUALLY);
854 
855         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
856     }
857     else if ( aBootstrapError == BE_OFFICECONFIG_BROKEN )
858     {
859         // set flag at BackupFileHelper to be able to know if _exit was called and
860         // actions are executed after this. This method we are in will not return,
861         // but end up in a _exit() call
862         comphelper::BackupFileHelper::setExitWasCalled();
863 
864         // enter safe mode, too
865         sfx2::SafeMode::putFlag();
866 
867         OUString msg(DpResId(STR_CONFIG_ERR_ACCESS_GENERAL));
868         if (!aErrorMessage.isEmpty()) {
869             msg += "\n(\"" + aErrorMessage + "\")";
870         }
871         FatalError(MakeStartupErrorMessage(msg));
872     }
873     else if ( aBootstrapError == BE_USERINSTALL_FAILED )
874     {
875         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_USERINSTALL_FAILED);
876         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
877     }
878     else if ( aBootstrapError == BE_LANGUAGE_MISSING )
879     {
880         OUString aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_LANGUAGE_MISSING);
881         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
882     }
883     else if (( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE ) ||
884              ( aBootstrapError == BE_USERINSTALL_NOWRITEACCESS      ))
885     {
886         OUString aUserInstallationURL;
887         OUString aUserInstallationPath;
888         utl::Bootstrap::locateUserInstallation( aUserInstallationURL );
889         osl::File::getSystemPathFromFileURL( aUserInstallationURL, aUserInstallationPath );
890 
891         OUString aDiagnosticMessage;
892         if ( aBootstrapError == BE_USERINSTALL_NOTENOUGHDISKSPACE )
893             aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOTENOUGHDISKSPACE);
894         else
895             aDiagnosticMessage = DpResId(STR_BOOTSTRAP_ERR_NOACCESSRIGHTS);
896         aDiagnosticMessage += aUserInstallationPath;
897 
898         FatalError(MakeStartupErrorMessage(aDiagnosticMessage));
899     }
900 }
901 
902 
903 namespace {
904 
905 
906 #if HAVE_FEATURE_BREAKPAD
handleCrashReport()907 void handleCrashReport()
908 {
909     static const char SERVICENAME_CRASHREPORT[] = "com.sun.star.comp.svx.CrashReportUI";
910 
911     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
912 
913     Reference< css::frame::XSynchronousDispatch > xRecoveryUI(
914         xContext->getServiceManager()->createInstanceWithContext(SERVICENAME_CRASHREPORT, xContext),
915         css::uno::UNO_QUERY_THROW);
916 
917     Reference< css::util::XURLTransformer > xURLParser =
918         css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
919 
920     css::util::URL aURL;
921     css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
922     bool bRet = false;
923     aRet >>= bRet;
924 }
925 #endif
926 
927 #if !defined ANDROID
handleSafeMode()928 void handleSafeMode()
929 {
930     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
931 
932     Reference< css::frame::XSynchronousDispatch > xSafeModeUI(
933         xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.SafeModeUI", xContext),
934         css::uno::UNO_QUERY_THROW);
935 
936     css::util::URL aURL;
937     css::uno::Any aRet = xSafeModeUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
938     bool bRet = false;
939     aRet >>= bRet;
940 }
941 #endif
942 
943 /** @short  check if recovery must be started or not.
944 
945     @param  bCrashed [boolean ... out!]
946             the office crashed last times.
947             But may be there are no recovery data.
948             Useful to trigger the error report tool without
949             showing the recovery UI.
950 
951     @param  bRecoveryDataExists [boolean ... out!]
952             there exists some recovery data.
953 
954     @param  bSessionDataExists [boolean ... out!]
955             there exists some session data.
956             Because the user may be logged out last time from its
957             unix session...
958 */
impl_checkRecoveryState(bool & bCrashed,bool & bRecoveryDataExists,bool & bSessionDataExists)959 void impl_checkRecoveryState(bool& bCrashed           ,
960                              bool& bRecoveryDataExists,
961                              bool& bSessionDataExists )
962 {
963     bCrashed = officecfg::Office::Recovery::RecoveryInfo::Crashed::get()
964 #if HAVE_FEATURE_BREAKPAD
965         || CrashReporter::crashReportInfoExists();
966 #else
967         ;
968 #endif
969     bool elements = officecfg::Office::Recovery::RecoveryList::get()->
970         hasElements();
971     bool session
972         = officecfg::Office::Recovery::RecoveryInfo::SessionData::get();
973     bRecoveryDataExists = elements && !session;
974     bSessionDataExists = elements && session;
975 }
976 
977 Reference< css::frame::XSynchronousDispatch > g_xRecoveryUI;
978 
979 template <class Ref>
980 struct RefClearGuard
981 {
982     Ref& m_Ref;
RefClearGuarddesktop::__anon11d5a4200511::RefClearGuard983     RefClearGuard(Ref& ref) : m_Ref(ref) {}
~RefClearGuarddesktop::__anon11d5a4200511::RefClearGuard984     ~RefClearGuard() { m_Ref.clear(); }
985 };
986 
987 /*  @short  start the recovery wizard.
988 
989     @param  bEmergencySave
990             differs between EMERGENCY_SAVE and RECOVERY
991 */
impl_callRecoveryUI(bool bEmergencySave,bool bExistsRecoveryData)992 bool impl_callRecoveryUI(bool bEmergencySave     ,
993                          bool bExistsRecoveryData)
994 {
995     static const char COMMAND_EMERGENCYSAVE[] = "vnd.sun.star.autorecovery:/doEmergencySave";
996     static const char COMMAND_RECOVERY[] = "vnd.sun.star.autorecovery:/doAutoRecovery";
997 
998     css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
999 
1000     g_xRecoveryUI.set(
1001         xContext->getServiceManager()->createInstanceWithContext("com.sun.star.comp.svx.RecoveryUI", xContext),
1002         css::uno::UNO_QUERY_THROW);
1003     RefClearGuard<Reference< css::frame::XSynchronousDispatch >> refClearGuard(g_xRecoveryUI);
1004 
1005     Reference< css::util::XURLTransformer > xURLParser =
1006         css::util::URLTransformer::create(xContext);
1007 
1008     css::util::URL aURL;
1009     if (bEmergencySave)
1010         aURL.Complete = COMMAND_EMERGENCYSAVE;
1011     else if (bExistsRecoveryData)
1012         aURL.Complete = COMMAND_RECOVERY;
1013     else
1014         return false;
1015 
1016     xURLParser->parseStrict(aURL);
1017 
1018     css::uno::Any aRet = g_xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
1019     bool bRet = false;
1020     aRet >>= bRet;
1021     return bRet;
1022 }
1023 
impl_bringToFrontRecoveryUI()1024 bool impl_bringToFrontRecoveryUI()
1025 {
1026     Reference< css::frame::XSynchronousDispatch > xRecoveryUI(g_xRecoveryUI);
1027     if (!xRecoveryUI.is())
1028         return false;
1029 
1030     css::util::URL aURL;
1031     aURL.Complete = "vnd.sun.star.autorecovery:/doBringToFront";
1032     Reference< css::util::XURLTransformer > xURLParser =
1033         css::util::URLTransformer::create(::comphelper::getProcessComponentContext());
1034     xURLParser->parseStrict(aURL);
1035 
1036     css::uno::Any aRet = xRecoveryUI->dispatchWithReturnValue(aURL, css::uno::Sequence< css::beans::PropertyValue >());
1037     bool bRet = false;
1038     aRet >>= bRet;
1039     return bRet;
1040 }
1041 
1042 }
1043 
1044 namespace {
1045 
restartOnMac(bool passArguments)1046 void restartOnMac(bool passArguments) {
1047 #if defined MACOSX
1048     RequestHandler::Disable();
1049 #if HAVE_FEATURE_MACOSX_SANDBOX
1050     (void) passArguments; // avoid warnings
1051     OUString aMessage = DpResId(STR_LO_MUST_BE_RESTARTED);
1052 
1053     std::unique_ptr<weld::MessageDialog> xRestartBox(Application::CreateMessageDialog(nullptr,
1054                                                      VclMessageType::Warning, VclButtonsType::Ok, aMessage));
1055     xRestartBox->run();
1056 #else
1057     OUString execUrl;
1058     OSL_VERIFY(osl_getExecutableFile(&execUrl.pData) == osl_Process_E_None);
1059     OUString execPath;
1060     OString execPath8;
1061     if ((osl::FileBase::getSystemPathFromFileURL(execUrl, execPath)
1062          != osl::FileBase::E_None) ||
1063         !execPath.convertToString(
1064             &execPath8, osl_getThreadTextEncoding(),
1065             (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
1066              RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
1067     {
1068         std::abort();
1069     }
1070     std::vector< OString > args;
1071     args.push_back(execPath8);
1072     bool wait = false;
1073     if (passArguments) {
1074         sal_uInt32 n = osl_getCommandArgCount();
1075         for (sal_uInt32 i = 0; i < n; ++i) {
1076             OUString arg;
1077             osl_getCommandArg(i, &arg.pData);
1078             if (arg.match("--accept=")) {
1079                 wait = true;
1080             }
1081             OString arg8;
1082             if (!arg.convertToString(
1083                     &arg8, osl_getThreadTextEncoding(),
1084                     (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR |
1085                      RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
1086             {
1087                 std::abort();
1088             }
1089             args.push_back(arg8);
1090         }
1091     }
1092     std::vector< char const * > argPtrs;
1093     for (auto const& elem : args)
1094     {
1095         argPtrs.push_back(elem.getStr());
1096     }
1097     argPtrs.push_back(nullptr);
1098     execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
1099     if (errno == ENOTSUP) { // happens when multithreaded on macOS < 10.6
1100         pid_t pid = fork();
1101         if (pid == 0) {
1102             execv(execPath8.getStr(), const_cast< char ** >(argPtrs.data()));
1103         } else if (pid > 0) {
1104             // Two simultaneously running soffice processes lead to two dock
1105             // icons, so avoid waiting here unless it must be assumed that the
1106             // process invoking soffice itself wants to wait for soffice to
1107             // finish:
1108             if (!wait) {
1109                 return;
1110             }
1111             int stat;
1112             if (waitpid(pid, &stat, 0) == pid && WIFEXITED(stat)) {
1113                 _exit(WEXITSTATUS(stat));
1114             }
1115         }
1116     }
1117     std::abort();
1118 #endif
1119 #else
1120     (void) passArguments; // avoid warnings
1121 #endif
1122 }
1123 
1124 #if HAVE_FEATURE_UPDATE_MAR
isTimeForUpdateCheck()1125 bool isTimeForUpdateCheck()
1126 {
1127     sal_uInt64 nLastUpdate = officecfg::Office::Update::Update::LastUpdateTime::get();
1128     sal_uInt64 nNow = tools::Time::GetSystemTicks();
1129 
1130     sal_uInt64 n7DayInMS = 1000 * 60 * 60 * 12 * 1; // 12 hours in ms
1131     if (nNow - n7DayInMS >= nLastUpdate)
1132         return true;
1133 
1134     return false;
1135 }
1136 #endif
1137 
1138 }
1139 
Exception(ExceptionCategory nCategory)1140 void Desktop::Exception(ExceptionCategory nCategory)
1141 {
1142     // protect against recursive calls
1143     static bool bInException = false;
1144 
1145 #if HAVE_FEATURE_BREAKPAD
1146     CrashReporter::removeExceptionHandler(); // disallow re-entry
1147 #endif
1148 
1149     SystemWindowFlags nOldMode = Application::GetSystemWindowMode();
1150     Application::SetSystemWindowMode( nOldMode & ~SystemWindowFlags::NOAUTOMODE );
1151     if ( bInException )
1152     {
1153         Application::Abort( OUString() );
1154     }
1155 
1156     bInException = true;
1157     const CommandLineArgs& rArgs = GetCommandLineArgs();
1158 
1159     // save all modified documents ... if it's allowed doing so.
1160     bool bRestart                           = false;
1161     bool bAllowRecoveryAndSessionManagement = (
1162                                                     ( !rArgs.IsNoRestore()                    ) && // some use cases of office must work without recovery
1163                                                     ( !rArgs.IsHeadless()                     ) &&
1164                                                     ( nCategory != ExceptionCategory::UserInterface ) && // recovery can't work without UI ... but UI layer seems to be the reason for this crash
1165                                                     ( Application::IsInExecute()               )    // crashes during startup and shutdown should be ignored (they indicate a corrupted installation...)
1166                                                   );
1167     if ( bAllowRecoveryAndSessionManagement )
1168     {
1169         // Save all open documents so they will be reopened
1170         // the next time the application is started
1171         // returns true if at least one document could be saved...
1172         bRestart = impl_callRecoveryUI(
1173                         true , // force emergency save
1174                         false);
1175     }
1176 
1177     FlushConfiguration();
1178 
1179     switch( nCategory )
1180     {
1181         case ExceptionCategory::ResourceNotLoaded:
1182         {
1183             Application::Abort( OUString() );
1184             break;
1185         }
1186 
1187         default:
1188         {
1189             m_xLockfile.reset();
1190 
1191             if( bRestart )
1192             {
1193                 RequestHandler::Disable();
1194                 if( pSignalHandler )
1195                     osl_removeSignalHandler( pSignalHandler );
1196 
1197                 restartOnMac(false);
1198                 if ( m_rSplashScreen.is() )
1199                     m_rSplashScreen->reset();
1200 
1201                 _exit( EXITHELPER_CRASH_WITH_RESTART );
1202             }
1203             else
1204             {
1205                 Application::Abort( OUString() );
1206             }
1207 
1208             break;
1209         }
1210     }
1211 
1212     OSL_ASSERT(false); // unreachable
1213 }
1214 
AppEvent(const ApplicationEvent & rAppEvent)1215 void Desktop::AppEvent( const ApplicationEvent& rAppEvent )
1216 {
1217     HandleAppEvent( rAppEvent );
1218 }
1219 
1220 struct ExecuteGlobals
1221 {
1222     Reference < css::document::XDocumentEventListener > xGlobalBroadcaster;
1223     bool bRestartRequested;
1224     bool bUseSystemFileDialog;
1225     std::unique_ptr<SvtLanguageOptions> pLanguageOptions;
1226     std::unique_ptr<SvtPathOptions> pPathOptions;
1227 
ExecuteGlobalsdesktop::ExecuteGlobals1228     ExecuteGlobals()
1229     : bRestartRequested( false )
1230     , bUseSystemFileDialog( true )
1231     {}
1232 };
1233 
1234 static ExecuteGlobals* pExecGlobals = nullptr;
1235 
Main()1236 int Desktop::Main()
1237 {
1238     pExecGlobals = new ExecuteGlobals();
1239 
1240     // Remember current context object
1241     css::uno::ContextLayer layer( css::uno::getCurrentContext() );
1242 
1243     if ( m_aBootstrapError != BE_OK )
1244     {
1245         HandleBootstrapErrors( m_aBootstrapError, m_aBootstrapErrorMessage );
1246         return EXIT_FAILURE;
1247     }
1248 
1249     BootstrapStatus eStatus = GetBootstrapStatus();
1250     if (eStatus == BS_TERMINATE) {
1251         return EXIT_SUCCESS;
1252     }
1253 
1254     // Detect desktop environment - need to do this as early as possible
1255     css::uno::setCurrentContext( new DesktopContext( css::uno::getCurrentContext() ) );
1256 
1257     CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
1258 
1259     Translate::SetReadStringHook(ReplaceStringHookProc);
1260 
1261     // Startup screen
1262     OpenSplashScreen();
1263 
1264     SetSplashScreenProgress(10);
1265 
1266     userinstall::Status inst_fin = userinstall::finalize();
1267     if (inst_fin != userinstall::EXISTED && inst_fin != userinstall::CREATED)
1268     {
1269         SAL_WARN( "desktop.app", "userinstall failed");
1270         if ( inst_fin == userinstall::ERROR_NO_SPACE )
1271             HandleBootstrapErrors(
1272                 BE_USERINSTALL_NOTENOUGHDISKSPACE, OUString() );
1273         else if ( inst_fin == userinstall::ERROR_CANT_WRITE )
1274             HandleBootstrapErrors( BE_USERINSTALL_NOWRITEACCESS, OUString() );
1275         else
1276             HandleBootstrapErrors( BE_USERINSTALL_FAILED, OUString() );
1277         return EXIT_FAILURE;
1278     }
1279     // refresh path information
1280     utl::Bootstrap::reloadData();
1281     SetSplashScreenProgress(20);
1282 
1283     Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
1284 
1285     Reference< XRestartManager > xRestartManager( OfficeRestartManager::get(xContext) );
1286 
1287     Reference< XDesktop2 > xDesktop;
1288     try
1289     {
1290         RegisterServices(xContext);
1291 
1292         SetSplashScreenProgress(25);
1293 
1294 #if HAVE_FEATURE_DESKTOP
1295         // check user installation directory for lockfile so we can be sure
1296         // there is no other instance using our data files from a remote host
1297 
1298         bool bMustLockProfile = ( getenv( "SAL_NOLOCK_PROFILE" ) == nullptr );
1299         if ( bMustLockProfile )
1300         {
1301             m_xLockfile.reset(new Lockfile);
1302 
1303             if ( !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsInvisible() &&
1304                  !rCmdLineArgs.IsNoLockcheck() && !m_xLockfile->check( Lockfile_execWarning ))
1305             {
1306                 // Lockfile exists, and user clicked 'no'
1307                 return EXIT_FAILURE;
1308             }
1309         }
1310 
1311         // check if accessibility is enabled but not working and allow to quit
1312         if( Application::GetSettings().GetMiscSettings().GetEnableATToolSupport() )
1313         {
1314             if( !InitAccessBridge() )
1315                 return EXIT_FAILURE;
1316         }
1317 #endif
1318 
1319         // terminate if requested...
1320         if( rCmdLineArgs.IsTerminateAfterInit() )
1321             return EXIT_SUCCESS;
1322 
1323         //  Read the common configuration items for optimization purpose
1324         if ( !InitializeConfiguration() )
1325             return EXIT_FAILURE;
1326 
1327 #if HAVE_FEATURE_UPDATE_MAR
1328         const char* pUpdaterTestEnable = std::getenv("LIBO_UPDATER_TEST_ENABLE");
1329         if (pUpdaterTestEnable || officecfg::Office::Update::Update::Enabled::get())
1330         {
1331             // check if we just updated
1332             const char* pUpdaterRunning = std::getenv("LIBO_UPDATER_TEST_RUNNING");
1333             bool bUpdateRunning = officecfg::Office::Update::Update::UpdateRunning::get() || pUpdaterRunning;
1334             if (bUpdateRunning)
1335             {
1336                 OUString aSeeAlso = officecfg::Office::Update::Update::SeeAlso::get();
1337                 OUString aOldBuildID = officecfg::Office::Update::Update::OldBuildID::get();
1338 
1339                 OUString aBuildID = Updater::getBuildID();
1340                 if (aOldBuildID == aBuildID)
1341                 {
1342                     Updater::log("Old and new Build ID are the same. No Updating took place.");
1343                 }
1344                 else
1345                 {
1346                     if (!aSeeAlso.isEmpty())
1347                     {
1348                         SAL_INFO("desktop.updater", "See also: " << aSeeAlso);
1349                                 Reference< css::system::XSystemShellExecute > xSystemShell(
1350                         SystemShellExecute::create(::comphelper::getProcessComponentContext()) );
1351 
1352                         xSystemShell->execute( aSeeAlso, OUString(), SystemShellExecuteFlags::URIS_ONLY );
1353                     }
1354                 }
1355 
1356                 // reset all the configuration values,
1357                 // all values need to be read before this code
1358                 std::shared_ptr< comphelper::ConfigurationChanges > batch(
1359                         comphelper::ConfigurationChanges::create());
1360                 officecfg::Office::Update::Update::UpdateRunning::set(false, batch);
1361                 officecfg::Office::Update::Update::SeeAlso::set(OUString(), batch);
1362                 officecfg::Office::Update::Update::OldBuildID::set(OUString(), batch);
1363                 batch->commit();
1364 
1365                 Updater::removeUpdateFiles();
1366             }
1367 
1368             osl::DirectoryItem aUpdateFile;
1369             osl::DirectoryItem::get(Updater::getUpdateFileURL(), aUpdateFile);
1370 
1371             const char* pUpdaterTestUpdate = std::getenv("LIBO_UPDATER_TEST_UPDATE");
1372             const char* pForcedUpdateCheck = std::getenv("LIBO_UPDATER_TEST_UPDATE_CHECK");
1373             if (pUpdaterTestUpdate || aUpdateFile.is())
1374             {
1375                 OUString aBuildID("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
1376                 rtl::Bootstrap::expandMacros(aBuildID);
1377                 std::shared_ptr< comphelper::ConfigurationChanges > batch(
1378                         comphelper::ConfigurationChanges::create());
1379                 officecfg::Office::Update::Update::OldBuildID::set(aBuildID, batch);
1380                 officecfg::Office::Update::Update::UpdateRunning::set(true, batch);
1381                 batch->commit();
1382 
1383                 // make sure the change is written to the configuration before we start the update
1384                 css::uno::Reference<css::util::XFlushable> xFlushable(css::configuration::theDefaultProvider::get(xContext), UNO_QUERY);
1385                 xFlushable->flush();
1386                 // avoid the old oosplash staying around
1387                 CloseSplashScreen();
1388                 bool bSuccess = update();
1389                 if (bSuccess)
1390                     return EXIT_SUCCESS;
1391             }
1392             else if (isTimeForUpdateCheck() || pForcedUpdateCheck)
1393             {
1394                 sal_uInt64 nNow = tools::Time::GetSystemTicks();
1395                 Updater::log("Update Check Time: " + OUString::number(nNow));
1396                 std::shared_ptr< comphelper::ConfigurationChanges > batch(
1397                         comphelper::ConfigurationChanges::create());
1398                 officecfg::Office::Update::Update::LastUpdateTime::set(nNow, batch);
1399                 batch->commit();
1400                 m_aUpdateThread = std::thread(update_checker);
1401             }
1402         }
1403 #endif
1404 
1405         SetSplashScreenProgress(30);
1406 
1407         // create title string
1408         OUString aTitle(ReplaceStringHookProc(RID_APPTITLE));
1409 
1410 #ifdef DBG_UTIL
1411         //include buildid in non product builds
1412         aTitle += " [" + utl::Bootstrap::getBuildIdData("development") + "]";
1413 #endif
1414 
1415         SetDisplayName( aTitle );
1416         SetSplashScreenProgress(35);
1417         pExecGlobals->pPathOptions.reset( new SvtPathOptions);
1418         SetSplashScreenProgress(40);
1419 
1420         xDesktop = css::frame::Desktop::create( xContext );
1421 
1422         // create service for loading SFX (still needed in startup)
1423         pExecGlobals->xGlobalBroadcaster = Reference < css::document::XDocumentEventListener >
1424             ( css::frame::theGlobalEventBroadcaster::get(xContext), UNO_SET_THROW );
1425 
1426         /* ensure existence of a default window that messages can be dispatched to
1427            This is for the benefit of testtool which uses PostUserEvent extensively
1428            and else can deadlock while creating this window from another thread while
1429            the main thread is not yet in the event loop.
1430         */
1431         Application::GetDefaultDevice();
1432 
1433 #if HAVE_FEATURE_EXTENSIONS
1434         // Check if bundled or shared extensions were added /removed
1435         // and process those extensions (has to be done before checking
1436         // the extension dependencies!
1437         SynchronizeExtensionRepositories(m_bCleanedExtensionCache, this);
1438         bool bAbort = CheckExtensionDependencies();
1439         if ( bAbort )
1440             return EXIT_FAILURE;
1441 
1442         if (inst_fin == userinstall::CREATED)
1443         {
1444             Migration::migrateSettingsIfNecessary();
1445         }
1446 #endif
1447 
1448         // keep a language options instance...
1449         pExecGlobals->pLanguageOptions.reset( new SvtLanguageOptions(true));
1450 
1451         css::document::DocumentEvent aEvent;
1452         aEvent.EventName = "OnStartApp";
1453         pExecGlobals->xGlobalBroadcaster->documentEventOccured(aEvent);
1454 
1455         SetSplashScreenProgress(50);
1456 
1457         // Backing Component
1458         bool bCrashed            = false;
1459         bool bExistsRecoveryData = false;
1460         bool bExistsSessionData  = false;
1461 
1462         impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
1463 
1464         OUString pidfileName = rCmdLineArgs.GetPidfileName();
1465         if ( !pidfileName.isEmpty() )
1466         {
1467             OUString pidfileURL;
1468 
1469             if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
1470             {
1471                 osl::File pidfile( pidfileURL );
1472                 osl::FileBase::RC rc;
1473 
1474                 osl::File::remove( pidfileURL );
1475                 if ( (rc = pidfile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create ) ) == osl::File::E_None )
1476                 {
1477                     OString pid( OString::number( GETPID() ) );
1478                     sal_uInt64 written = 0;
1479                     if ( pidfile.write(pid.getStr(), pid.getLength(), written) != osl::File::E_None )
1480                     {
1481                         SAL_WARN("desktop.app", "cannot write pidfile " << pidfile.getURL());
1482                     }
1483                     pidfile.close();
1484                 }
1485                 else
1486                 {
1487                     SAL_WARN("desktop.app", "cannot open pidfile " << pidfile.getURL() << rc);
1488                 }
1489             }
1490             else
1491             {
1492                 SAL_WARN("desktop.app", "cannot get pidfile URL from path" << pidfileName);
1493             }
1494         }
1495 
1496         if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
1497         {
1498             // Ensure that we use not the system file dialogs as
1499             // headless mode relies on Application::EnableHeadlessMode()
1500             // which does only work for VCL dialogs!!
1501             SvtMiscOptions aMiscOptions;
1502             pExecGlobals->bUseSystemFileDialog = aMiscOptions.UseSystemFileDialog();
1503             aMiscOptions.SetUseSystemFileDialog( false );
1504         }
1505 
1506         pExecGlobals->bRestartRequested = xRestartManager->isRestartRequested(
1507             true);
1508         if ( !pExecGlobals->bRestartRequested )
1509         {
1510             if ((!rCmdLineArgs.WantsToLoadDocument() && !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsHeadless() && !rCmdLineArgs.IsQuickstart()) &&
1511                 (SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE)) &&
1512                 (!bExistsRecoveryData                                                  ) &&
1513                 (!bExistsSessionData                                                   ) &&
1514                 (!Application::AnyInput( VclInputFlags::APPEVENT )                          ))
1515             {
1516                  ShowBackingComponent(this);
1517             }
1518         }
1519     }
1520     catch ( const css::lang::WrappedTargetException& wte )
1521     {
1522         css::uno::Exception te;
1523         wte.TargetException >>= te;
1524         FatalError( MakeStartupConfigAccessErrorMessage(wte.Message + te.Message) );
1525     }
1526     catch ( const css::uno::Exception& e )
1527     {
1528         FatalError( MakeStartupErrorMessage(e.Message) );
1529     }
1530     SetSplashScreenProgress(55);
1531 
1532     SvtFontSubstConfig().Apply();
1533 
1534     SvtTabAppearanceCfg aAppearanceCfg;
1535     SvtTabAppearanceCfg::SetInitialized();
1536     aAppearanceCfg.SetApplicationDefaults( this );
1537     SvtAccessibilityOptions aOptions;
1538     aOptions.SetVCLSettings();
1539     SetSplashScreenProgress(60);
1540 
1541     if ( !pExecGlobals->bRestartRequested )
1542     {
1543         Application::SetFilterHdl( LINK( this, Desktop, ImplInitFilterHdl ) );
1544 
1545         // Preload function depends on an initialized sfx application!
1546         SetSplashScreenProgress(75);
1547 
1548         // use system window dialogs
1549         Application::SetSystemWindowMode( SystemWindowFlags::DIALOG );
1550 
1551         SetSplashScreenProgress(80);
1552 
1553         if ( !rCmdLineArgs.IsInvisible() &&
1554              !rCmdLineArgs.IsNoQuickstart() )
1555             InitializeQuickstartMode( xContext );
1556 
1557         try
1558         {
1559             if ( xDesktop.is() )
1560                 xDesktop->addTerminateListener( new RequestHandlerController );
1561             SetSplashScreenProgress(100);
1562         }
1563         catch ( const css::uno::Exception& e )
1564         {
1565             FatalError( MakeStartupErrorMessage(e.Message) );
1566         }
1567 
1568         // FIXME: move this somewhere sensible.
1569 #if HAVE_FEATURE_OPENCL
1570         CheckOpenCLCompute(xDesktop);
1571 #endif
1572 
1573         // In headless mode, reap the process started by fire_glxtest_process() early in soffice_main
1574         // (desktop/source/app/sofficemain.cxx), in a code block that needs to be covered by the same
1575         // #if condition as this code block:
1576 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID && HAVE_FEATURE_UI && HAVE_FEATURE_OPENGL
1577         if (rCmdLineArgs.IsHeadless()) {
1578             reap_glxtest_process();
1579         }
1580 #endif
1581 
1582         // Release solar mutex just before we wait for our client to connect
1583         {
1584             SolarMutexReleaser aReleaser;
1585 
1586             // Post user event to startup first application component window
1587             // We have to send this OpenClients message short before execute() to
1588             // minimize the risk that this message overtakes type detection construction!!
1589             Application::PostUserEvent( LINK( this, Desktop, OpenClients_Impl ) );
1590 
1591             // Post event to enable acceptors
1592             Application::PostUserEvent( LINK( this, Desktop, EnableAcceptors_Impl) );
1593 
1594             // Acquire solar mutex just before we enter our message loop
1595         }
1596 
1597         // call Application::Execute to process messages in vcl message loop
1598 #ifndef IOS
1599         try
1600 #endif
1601         {
1602 #if HAVE_FEATURE_JAVA
1603             // The JavaContext contains an interaction handler which is used when
1604             // the creation of a Java Virtual Machine fails
1605             css::uno::ContextLayer layer2(
1606                 new svt::JavaContext( css::uno::getCurrentContext() ) );
1607 #endif
1608             // check whether the shutdown is caused by restart just before entering the Execute
1609             pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
1610                 xRestartManager->isRestartRequested(true);
1611 
1612             if ( !pExecGlobals->bRestartRequested )
1613             {
1614                 // if this run of the office is triggered by restart, some additional actions should be done
1615                 DoRestartActionsIfNecessary( !rCmdLineArgs.IsInvisible() && !rCmdLineArgs.IsNoQuickstart() );
1616 
1617                 Execute();
1618             }
1619         }
1620 #ifndef IOS
1621         catch(const css::document::CorruptedFilterConfigurationException& exFilterCfg)
1622         {
1623             RequestHandler::SetDowning();
1624             FatalError( MakeStartupErrorMessage(exFilterCfg.Message) );
1625         }
1626         catch(const css::configuration::CorruptedConfigurationException& exAnyCfg)
1627         {
1628             RequestHandler::SetDowning();
1629             FatalError( MakeStartupErrorMessage(exAnyCfg.Message) );
1630         }
1631         catch( const css::uno::Exception& exUNO)
1632         {
1633             RequestHandler::SetDowning();
1634             FatalError( exUNO.Message);
1635         }
1636         catch( const std::exception& exSTD)
1637         {
1638             RequestHandler::SetDowning();
1639             FatalError(o3tl::runtimeToOUString(exSTD.what()));
1640         }
1641         catch( ...)
1642         {
1643             RequestHandler::SetDowning();
1644             FatalError( "Caught Unknown Exception: Aborting!");
1645         }
1646 #endif
1647     }
1648     else
1649     {
1650         if (xDesktop.is())
1651             xDesktop->terminate();
1652     }
1653     // CAUTION: you do not necessarily get here e.g. on the Mac.
1654     // please put all deinitialization code into doShutdown
1655     return doShutdown();
1656 }
1657 
doShutdown()1658 int Desktop::doShutdown()
1659 {
1660     if( ! pExecGlobals )
1661         return EXIT_SUCCESS;
1662 
1663     if (m_aUpdateThread.joinable())
1664         m_aUpdateThread.join();
1665 
1666     pExecGlobals->bRestartRequested = pExecGlobals->bRestartRequested ||
1667         OfficeRestartManager::get(comphelper::getProcessComponentContext())->
1668         isRestartRequested(true);
1669     if ( pExecGlobals->bRestartRequested )
1670         SetRestartState();
1671 
1672     // Restore old value
1673     const CommandLineArgs& rCmdLineArgs = GetCommandLineArgs();
1674     if ( rCmdLineArgs.IsHeadless() || rCmdLineArgs.IsEventTesting() )
1675         SvtMiscOptions().SetUseSystemFileDialog( pExecGlobals->bUseSystemFileDialog );
1676 
1677     OUString pidfileName = rCmdLineArgs.GetPidfileName();
1678     if ( !pidfileName.isEmpty() )
1679     {
1680         OUString pidfileURL;
1681 
1682         if ( osl_getFileURLFromSystemPath(pidfileName.pData, &pidfileURL.pData) == osl_File_E_None )
1683         {
1684             if ( osl::File::remove( pidfileURL ) != osl::FileBase::E_None )
1685             {
1686                 SAL_WARN("desktop.app", "shutdown: cannot remove pidfile " << pidfileURL);
1687             }
1688         }
1689         else
1690         {
1691             SAL_WARN("desktop.app", "shutdown: cannot get pidfile URL from path" << pidfileName);
1692         }
1693     }
1694 
1695     // remove temp directory
1696     RemoveTemporaryDirectory();
1697     flatpak::removeTemporaryHtmlDirectory();
1698 
1699     // flush evtl. configuration changes so that all config files in user
1700     // dir are written
1701     FlushConfiguration();
1702 
1703     if (pExecGlobals->bRestartRequested)
1704     {
1705         // tdf#128523
1706         RemoveIconCacheDirectory();
1707 
1708         // a restart is already requested, usually due to a configuration change
1709         // that needs a restart to get active. If this is the case, do not try
1710         // to use SecureUserConfig to safe this still untested new configuration
1711     }
1712     else
1713     {
1714         // Test if SecureUserConfig is active. If yes and we are at this point, regular shutdown
1715         // is in progress and the currently used configuration was working. Try to secure this
1716         // working configuration for later eventually necessary restores
1717         comphelper::BackupFileHelper aBackupFileHelper;
1718 
1719         aBackupFileHelper.tryPush();
1720         aBackupFileHelper.tryPushExtensionInfo();
1721     }
1722 
1723     // The acceptors in the AcceptorMap must be released (in DeregisterServices)
1724     // with the solar mutex unlocked, to avoid deadlock:
1725     {
1726         SolarMutexReleaser aReleaser;
1727         DeregisterServices();
1728 #if HAVE_FEATURE_SCRIPTING
1729         StarBASIC::DetachAllDocBasicItems();
1730 #endif
1731     }
1732 
1733     // be sure that path/language options gets destroyed before
1734     // UCB is deinitialized
1735     pExecGlobals->pLanguageOptions.reset();
1736     pExecGlobals->pPathOptions.reset();
1737 
1738     comphelper::ThreadPool::getSharedOptimalPool().shutdown();
1739 
1740     bool bRR = pExecGlobals->bRestartRequested;
1741     delete pExecGlobals;
1742     pExecGlobals = nullptr;
1743 
1744     if ( bRR )
1745     {
1746         restartOnMac(true);
1747         if ( m_rSplashScreen.is() )
1748             m_rSplashScreen->reset();
1749 
1750         return EXITHELPER_NORMAL_RESTART;
1751     }
1752     return EXIT_SUCCESS;
1753 }
1754 
IMPL_STATIC_LINK(Desktop,ImplInitFilterHdl,::ConvertData &,rData,bool)1755 IMPL_STATIC_LINK( Desktop, ImplInitFilterHdl, ::ConvertData&, rData, bool )
1756 {
1757     return GraphicFilter::GetGraphicFilter().GetFilterCallback().Call( rData );
1758 }
1759 
InitializeConfiguration()1760 bool Desktop::InitializeConfiguration()
1761 {
1762     try
1763     {
1764         css::configuration::theDefaultProvider::get(
1765             comphelper::getProcessComponentContext() );
1766         return true;
1767     }
1768     catch( css::lang::ServiceNotRegisteredException & e )
1769     {
1770         HandleBootstrapErrors(
1771             Desktop::BE_UNO_SERVICE_CONFIG_MISSING, e.Message );
1772     }
1773     catch( const css::configuration::MissingBootstrapFileException& e )
1774     {
1775         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::MISSING_BOOTSTRAP_FILE,
1776                                                 e.BootstrapFileURL ));
1777         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_USER_INSTALL, aMsg );
1778     }
1779     catch( const css::configuration::InvalidBootstrapFileException& e )
1780     {
1781         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY,
1782                                                 e.BootstrapFileURL ));
1783         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1784     }
1785     catch( const css::configuration::InstallationIncompleteException& )
1786     {
1787         OUString aVersionFileURL;
1788         OUString aMsg;
1789         utl::Bootstrap::PathStatus aPathStatus = utl::Bootstrap::locateVersionFile( aVersionFileURL );
1790         if ( aPathStatus == utl::Bootstrap::PATH_EXISTS )
1791             aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE_ENTRY, aVersionFileURL );
1792         else
1793             aMsg = CreateErrorMsgString( utl::Bootstrap::MISSING_VERSION_FILE, aVersionFileURL );
1794 
1795         HandleBootstrapPathErrors( ::utl::Bootstrap::MISSING_USER_INSTALL, aMsg );
1796     }
1797     catch ( const css::configuration::backend::BackendAccessException& exception)
1798     {
1799         // [cm122549] It is assumed in this case that the message
1800         // coming from InitConfiguration (in fact CreateApplicationConf...)
1801         // is suitable for display directly.
1802         FatalError( MakeStartupErrorMessage( exception.Message ) );
1803     }
1804     catch ( const css::configuration::backend::BackendSetupException& exception)
1805     {
1806         // [cm122549] It is assumed in this case that the message
1807         // coming from InitConfiguration (in fact CreateApplicationConf...)
1808         // is suitable for display directly.
1809         FatalError( MakeStartupErrorMessage( exception.Message ) );
1810     }
1811     catch ( const css::configuration::CannotLoadConfigurationException& )
1812     {
1813         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
1814                                                 OUString() ));
1815         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1816     }
1817     catch( const css::uno::Exception& )
1818     {
1819         OUString aMsg( CreateErrorMsgString( utl::Bootstrap::INVALID_BOOTSTRAP_DATA,
1820                                                 OUString() ));
1821         HandleBootstrapPathErrors( ::utl::Bootstrap::INVALID_BASE_INSTALL, aMsg );
1822     }
1823     return false;
1824 }
1825 
FlushConfiguration()1826 void Desktop::FlushConfiguration()
1827 {
1828     css::uno::Reference< css::util::XFlushable >(
1829         css::configuration::theDefaultProvider::get(
1830             comphelper::getProcessComponentContext()),
1831         css::uno::UNO_QUERY_THROW)->flush();
1832 }
1833 
InitializeQuickstartMode(const Reference<XComponentContext> & rxContext)1834 bool Desktop::InitializeQuickstartMode( const Reference< XComponentContext >& rxContext )
1835 {
1836     try
1837     {
1838         // the shutdown icon sits in the systray and allows the user to keep
1839         // the office instance running for quicker restart
1840         // this will only be activated if --quickstart was specified on cmdline
1841 
1842         bool bQuickstart = shouldLaunchQuickstart();
1843 
1844         // Try to instantiate quickstart service. This service is not mandatory, so
1845         // do nothing if service is not available
1846 
1847         // #i105753# the following if was invented for performance
1848         // unfortunately this broke the Mac behavior which is to always run
1849         // in quickstart mode since Mac applications do not usually quit
1850         // when the last document closes.
1851         // Note that this claim that on macOS we "always run in quickstart mode"
1852         // has nothing to do with (quick) *starting* (i.e. starting automatically
1853         // when the user logs in), though, but with not quitting when no documents
1854         // are open.
1855         #ifndef MACOSX
1856         if ( bQuickstart )
1857         #endif
1858         {
1859             css::office::Quickstart::createStart(rxContext, bQuickstart);
1860         }
1861         return true;
1862     }
1863     catch( const css::uno::Exception& )
1864     {
1865         return false;
1866     }
1867 }
1868 
OverrideSystemSettings(AllSettings & rSettings)1869 void Desktop::OverrideSystemSettings( AllSettings& rSettings )
1870 {
1871     if ( !SvtTabAppearanceCfg::IsInitialized () )
1872         return;
1873 
1874     StyleSettings hStyleSettings   = rSettings.GetStyleSettings();
1875     MouseSettings hMouseSettings = rSettings.GetMouseSettings();
1876 
1877     DragFullOptions nDragFullOptions = hStyleSettings.GetDragFullOptions();
1878 
1879     SvtTabAppearanceCfg aAppearanceCfg;
1880     DragMode nDragMode = aAppearanceCfg.GetDragMode();
1881     switch ( nDragMode )
1882     {
1883     case DragMode::FullWindow:
1884         nDragFullOptions |= DragFullOptions::All;
1885         break;
1886     case DragMode::Frame:
1887         nDragFullOptions &= ~DragFullOptions::All;
1888         break;
1889     case DragMode::SystemDep:
1890     default:
1891         break;
1892     }
1893 
1894     MouseFollowFlags nFollow = hMouseSettings.GetFollow();
1895     hMouseSettings.SetFollow( aAppearanceCfg.IsMenuMouseFollow() ? (nFollow|MouseFollowFlags::Menu) : (nFollow&~MouseFollowFlags::Menu));
1896     rSettings.SetMouseSettings(hMouseSettings);
1897 
1898     SvtMenuOptions aMenuOpt;
1899     hStyleSettings.SetUseImagesInMenus(aMenuOpt.GetMenuIconsState());
1900     hStyleSettings.SetContextMenuShortcuts(aMenuOpt.GetContextMenuShortcuts());
1901     hStyleSettings.SetDragFullOptions( nDragFullOptions );
1902     rSettings.SetStyleSettings ( hStyleSettings );
1903 }
1904 
1905 
1906 class ExitTimer : public Timer
1907 {
1908   public:
ExitTimer()1909     ExitTimer()
1910     {
1911         SetTimeout(500);
1912         Start();
1913     }
Invoke()1914     virtual void Invoke() override
1915     {
1916         _exit(42);
1917     }
1918 };
1919 
IMPL_LINK_NOARG(Desktop,OpenClients_Impl,void *,void)1920 IMPL_LINK_NOARG(Desktop, OpenClients_Impl, void*, void)
1921 {
1922     try {
1923         // #i114963#
1924         // Enable IPC thread before OpenClients
1925         //
1926         // This is because it is possible for another client to connect during the OpenClients() call.
1927         // This can happen on Windows when document is printed (not opened) and another client wants to print (when printing multiple documents).
1928         // 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
1929         //
1930         // ALSO:
1931         //
1932         // Multiple clients may request simultaneous connections.
1933         // When this server closes down it attempts to recreate the pipe (in RequestHandler::Disable()).
1934         // It's possible that the client has a pending connection request.
1935         // When the IPC thread is not running, this connection locks (because maPipe.accept()) is never called
1936         RequestHandler::SetReady(true);
1937         OpenClients();
1938 
1939         CloseSplashScreen();
1940         CheckFirstRun( );
1941 #ifdef _WIN32
1942         // Registers a COM class factory of the service manager with the windows operating system.
1943         Reference< XMultiServiceFactory > xSMgr=  comphelper::getProcessServiceFactory();
1944         xSMgr->createInstance("com.sun.star.bridge.OleApplicationRegistration");
1945         xSMgr->createInstance("com.sun.star.comp.ole.EmbedServer");
1946 #endif
1947         const char *pExitPostStartup = getenv ("OOO_EXIT_POST_STARTUP");
1948         if (pExitPostStartup && *pExitPostStartup)
1949             new ExitTimer();
1950     } catch (const css::uno::Exception &e) {
1951         Application::Abort( "UNO exception during client open: " + e.Message );
1952     }
1953 }
1954 
OpenClients()1955 void Desktop::OpenClients()
1956 {
1957 
1958     const CommandLineArgs& rArgs = GetCommandLineArgs();
1959 
1960     if (!rArgs.IsQuickstart())
1961     {
1962         OUString aHelpModule;
1963         if (rArgs.IsHelpWriter()) {
1964             aHelpModule = "swriter";
1965         } else if (rArgs.IsHelpCalc()) {
1966             aHelpModule = "scalc";
1967         } else if (rArgs.IsHelpDraw()) {
1968             aHelpModule = "sdraw";
1969         } else if (rArgs.IsHelpImpress()) {
1970             aHelpModule = "simpress";
1971         } else if (rArgs.IsHelpBase()) {
1972             aHelpModule = "sdatabase";
1973         } else if (rArgs.IsHelpBasic()) {
1974             aHelpModule = "sbasic";
1975         } else if (rArgs.IsHelpMath()) {
1976             aHelpModule = "smath";
1977         }
1978         if (!aHelpModule.isEmpty()) {
1979             OUString aHelpURL = "vnd.sun.star.help://"
1980                               + aHelpModule
1981                               + "/start?Language="
1982                               + utl::ConfigManager::getUILocale();
1983 #if defined UNX
1984             aHelpURL += "&System=UNX";
1985 #elif defined WNT
1986             aHelpURL += "&System=WIN";
1987 #endif
1988             Application::GetHelp()->Start(aHelpURL, static_cast<const vcl::Window*>(nullptr));
1989             return;
1990         }
1991     }
1992 
1993     // Disable AutoSave feature in case "--norestore" or a similar command line switch is set on the command line.
1994     // The reason behind: AutoSave/EmergencySave/AutoRecovery share the same data.
1995     // But the require that all documents, which are saved as backup should exists inside
1996     // memory. May be this mechanism will be inconsistent if the configuration exists...
1997     // but no document inside memory corresponds to this data.
1998     // Further it's not acceptable to recover such documents without any UI. It can
1999     // need some time, where the user won't see any results and wait for finishing the office startup...
2000     bool bAllowRecoveryAndSessionManagement = ( !rArgs.IsNoRestore() ) && ( !rArgs.IsHeadless()  );
2001 
2002 #if !defined ANDROID
2003     // Enter safe mode if requested
2004     if (Application::IsSafeModeEnabled()) {
2005         handleSafeMode();
2006     }
2007 #endif
2008 
2009 #if HAVE_FEATURE_BREAKPAD
2010     if (officecfg::Office::Common::Misc::CrashReport::get() && CrashReporter::crashReportInfoExists())
2011         handleCrashReport();
2012 #endif
2013 
2014     if ( ! bAllowRecoveryAndSessionManagement )
2015     {
2016         try
2017         {
2018             Reference< XDispatch > xRecovery = css::frame::theAutoRecovery::get( ::comphelper::getProcessComponentContext() );
2019             Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create( ::comphelper::getProcessComponentContext() );
2020 
2021             css::util::URL aCmd;
2022             aCmd.Complete = "vnd.sun.star.autorecovery:/disableRecovery";
2023             xParser->parseStrict(aCmd);
2024 
2025             xRecovery->dispatch(aCmd, css::uno::Sequence< css::beans::PropertyValue >());
2026         }
2027         catch(const css::uno::Exception&)
2028         {
2029             TOOLS_WARN_EXCEPTION( "desktop.app", "Could not disable AutoRecovery.");
2030         }
2031     }
2032     else
2033     {
2034         bool bCrashed            = false;
2035         bool bExistsRecoveryData = false;
2036         bool bExistsSessionData  = false;
2037         bool const bDisableRecovery
2038             = getenv("OOO_DISABLE_RECOVERY") != nullptr
2039               || !officecfg::Office::Recovery::RecoveryInfo::Enabled::get();
2040 
2041         impl_checkRecoveryState(bCrashed, bExistsRecoveryData, bExistsSessionData);
2042 
2043         if ( !bDisableRecovery &&
2044             (
2045                 bExistsRecoveryData || // => crash with files    => recovery
2046                 bCrashed               // => crash without files => error report
2047             )
2048            )
2049         {
2050             try
2051             {
2052                 impl_callRecoveryUI(
2053                     false          , // false => force recovery instead of emergency save
2054                     bExistsRecoveryData);
2055             }
2056             catch(const css::uno::Exception&)
2057             {
2058                 TOOLS_WARN_EXCEPTION( "desktop.app", "Error during recovery");
2059             }
2060         }
2061 
2062         Reference< XSessionManagerListener2 > xSessionListener;
2063         try
2064         {
2065             // specifies whether the UI-interaction on Session shutdown is allowed
2066             bool bUIOnSessionShutdownAllowed = officecfg::Office::Recovery::SessionShutdown::DocumentStoreUIEnabled::get();
2067             xSessionListener = SessionListener::createWithOnQuitFlag(
2068                     ::comphelper::getProcessComponentContext(), bUIOnSessionShutdownAllowed);
2069         }
2070         catch(const css::uno::Exception&)
2071         {
2072             TOOLS_WARN_EXCEPTION( "desktop.app", "Registration of session listener failed");
2073         }
2074 
2075         if ( !bExistsRecoveryData && xSessionListener.is() )
2076         {
2077             // session management
2078             try
2079             {
2080                 xSessionListener->doRestore();
2081             }
2082             catch(const css::uno::Exception&)
2083             {
2084                 TOOLS_WARN_EXCEPTION( "desktop.app", "Error in session management");
2085             }
2086         }
2087     }
2088 
2089     // write this information here to avoid depending on vcl in the crash reporter lib
2090     CrashReporter::addKeyValue("Language", Application::GetSettings().GetLanguageTag().getBcp47(), CrashReporter::Create);
2091 
2092     RequestHandler::EnableRequests();
2093 
2094     ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
2095     aRequest.aOpenList = rArgs.GetOpenList();
2096     aRequest.aViewList = rArgs.GetViewList();
2097     aRequest.aStartList = rArgs.GetStartList();
2098     aRequest.aPrintList = rArgs.GetPrintList();
2099     aRequest.aPrintToList = rArgs.GetPrintToList();
2100     aRequest.aPrinterName = rArgs.GetPrinterName();
2101     aRequest.aForceOpenList = rArgs.GetForceOpenList();
2102     aRequest.aForceNewList = rArgs.GetForceNewList();
2103     aRequest.aConversionList = rArgs.GetConversionList();
2104     aRequest.aConversionParams = rArgs.GetConversionParams();
2105     aRequest.aConversionOut = rArgs.GetConversionOut();
2106     aRequest.aImageConversionType = rArgs.GetImageConversionType();
2107     aRequest.aInFilter = rArgs.GetInFilter();
2108     aRequest.bTextCat = rArgs.IsTextCat();
2109     aRequest.bScriptCat = rArgs.IsScriptCat();
2110 
2111     if ( !aRequest.aOpenList.empty() ||
2112          !aRequest.aViewList.empty() ||
2113          !aRequest.aStartList.empty() ||
2114          !aRequest.aPrintList.empty() ||
2115          !aRequest.aForceOpenList.empty() ||
2116          !aRequest.aForceNewList.empty() ||
2117          ( !aRequest.aPrintToList.empty() && !aRequest.aPrinterName.isEmpty() ) ||
2118          !aRequest.aConversionList.empty() )
2119     {
2120         if ( rArgs.HasModuleParam() )
2121         {
2122             SvtModuleOptions    aOpt;
2123 
2124             // Support command line parameters to start a module (as preselection)
2125             if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2126                 aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::WRITER );
2127             else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2128                 aRequest.aModule = aOpt.GetFactoryName( SvtModuleOptions::EFactory::CALC );
2129             else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2130                 aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::IMPRESS );
2131             else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2132                 aRequest.aModule= aOpt.GetFactoryName( SvtModuleOptions::EFactory::DRAW );
2133         }
2134 
2135         // check for printing disabled
2136         if( ( !(aRequest.aPrintList.empty() && aRequest.aPrintToList.empty()) )
2137             && Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
2138         {
2139             aRequest.aPrintList.clear();
2140             aRequest.aPrintToList.clear();
2141             std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(nullptr,
2142                                                       VclMessageType::Warning, VclButtonsType::Ok,
2143                                                       DpResId(STR_ERR_PRINTDISABLED)));
2144             xBox->run();
2145         }
2146 
2147         // Process request
2148         if ( RequestHandler::ExecuteCmdLineRequests(aRequest, false) )
2149         {
2150             // Don't do anything if we have successfully called terminate at desktop:
2151             return;
2152         }
2153     }
2154 
2155     // no default document if a document was loaded by recovery or by command line or if soffice is used as server
2156     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create( ::comphelper::getProcessComponentContext() );
2157     Reference< XElementAccess > xList( xDesktop->getFrames(), UNO_QUERY_THROW );
2158     if ( xList->hasElements() )
2159         return;
2160 
2161     if ( rArgs.IsQuickstart() || rArgs.IsInvisible() || Application::AnyInput( VclInputFlags::APPEVENT ) )
2162         // soffice was started as tray icon ...
2163         return;
2164 
2165     OpenDefault();
2166 }
2167 
OpenDefault()2168 void Desktop::OpenDefault()
2169 {
2170     OUString        aName;
2171     SvtModuleOptions    aOpt;
2172 
2173     const CommandLineArgs& rArgs = GetCommandLineArgs();
2174     if ( rArgs.IsNoDefault() ) return;
2175     if ( rArgs.HasModuleParam() )
2176     {
2177         // Support new command line parameters to start a module
2178         if ( rArgs.IsWriter() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2179             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
2180         else if ( rArgs.IsCalc() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2181             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
2182         else if ( rArgs.IsImpress() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2183             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
2184         else if ( rArgs.IsBase() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
2185             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
2186         else if ( rArgs.IsDraw() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2187             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
2188         else if ( rArgs.IsMath() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::MATH ) )
2189             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::MATH );
2190         else if ( rArgs.IsGlobal() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2191             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERGLOBAL );
2192         else if ( rArgs.IsWeb() && aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2193             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITERWEB );
2194     }
2195 
2196     if ( aName.isEmpty() )
2197     {
2198         if (aOpt.IsModuleInstalled(SvtModuleOptions::EModule::STARTMODULE))
2199         {
2200             ShowBackingComponent(nullptr);
2201             return;
2202         }
2203 
2204         // Old way to create a default document
2205         if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::WRITER ) )
2206             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::WRITER );
2207         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::CALC ) )
2208             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::CALC );
2209         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::IMPRESS ) )
2210             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::IMPRESS );
2211         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DATABASE ) )
2212             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DATABASE );
2213         else if ( aOpt.IsModuleInstalled( SvtModuleOptions::EModule::DRAW ) )
2214             aName = aOpt.GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory::DRAW );
2215         else
2216             return;
2217     }
2218 
2219     ProcessDocumentsRequest aRequest(rArgs.getCwdUrl());
2220     aRequest.aOpenList.push_back(aName);
2221     RequestHandler::ExecuteCmdLineRequests(aRequest, false);
2222 }
2223 
2224 
GetURL_Impl(const OUString & rName,boost::optional<OUString> const & cwdUrl)2225 OUString GetURL_Impl(
2226     const OUString& rName, boost::optional< OUString > const & cwdUrl )
2227 {
2228     // if rName is a vnd.sun.star.script URL do not attempt to parse it
2229     // as INetURLObj does not handle URLs there
2230     if (rName.startsWith("vnd.sun.star.script"))
2231     {
2232         return rName;
2233     }
2234 
2235     // don't touch file urls, those should already be in internal form
2236     // they won't get better here (#112849#)
2237     if (comphelper::isFileUrl(rName))
2238     {
2239         return rName;
2240     }
2241 
2242     if ( rName.startsWith("service:"))
2243     {
2244         return rName;
2245     }
2246 
2247     // Add path separator to these directory and make given URL (rName) absolute by using of current working directory
2248     // Attention: "setFinalSlash()" is necessary for calling "smartRel2Abs()"!!!
2249     // Otherwise last part will be ignored and wrong result will be returned!!!
2250     // "smartRel2Abs()" interpret given URL as file not as path. So he truncate last element to get the base path ...
2251     // But if we add a separator - he doesn't do it anymore.
2252     INetURLObject aObj;
2253     if (cwdUrl) {
2254         aObj.SetURL(*cwdUrl);
2255         aObj.setFinalSlash();
2256     }
2257 
2258     // Use the provided parameters for smartRel2Abs to support the usage of '%' in system paths.
2259     // Otherwise this char won't get encoded and we are not able to load such files later,
2260     bool bWasAbsolute;
2261     INetURLObject aURL     = aObj.smartRel2Abs( rName, bWasAbsolute, false, INetURLObject::EncodeMechanism::WasEncoded,
2262                                                 RTL_TEXTENCODING_UTF8, true );
2263     OUString      aFileURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE);
2264 
2265     ::osl::FileStatus aStatus( osl_FileStatus_Mask_FileURL );
2266     ::osl::DirectoryItem aItem;
2267     if( ::osl::FileBase::E_None == ::osl::DirectoryItem::get( aFileURL, aItem ) &&
2268         ::osl::FileBase::E_None == aItem.getFileStatus( aStatus ) )
2269             aFileURL = aStatus.getFileURL();
2270 
2271     return aFileURL;
2272 }
2273 
HandleAppEvent(const ApplicationEvent & rAppEvent)2274 void Desktop::HandleAppEvent( const ApplicationEvent& rAppEvent )
2275 {
2276     switch ( rAppEvent.GetEvent() )
2277     {
2278     case ApplicationEvent::Type::Accept:
2279         // every time an accept parameter is used we create an acceptor
2280         // with the corresponding accept-string
2281         createAcceptor(rAppEvent.GetStringData());
2282         break;
2283     case ApplicationEvent::Type::Appear:
2284         if ( !GetCommandLineArgs().IsInvisible() && !impl_bringToFrontRecoveryUI() )
2285         {
2286             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2287 
2288             // find active task - the active task is always a visible task
2289             Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
2290             Reference< css::frame::XFrame > xTask = xDesktop->getActiveFrame();
2291             if ( !xTask.is() )
2292             {
2293                 // get any task if there is no active one
2294                 Reference< css::container::XIndexAccess > xList = xDesktop->getFrames();
2295                 if ( xList->getCount() > 0 )
2296                     xList->getByIndex(0) >>= xTask;
2297             }
2298 
2299             if ( xTask.is() )
2300             {
2301                 Reference< css::awt::XTopWindow > xTop( xTask->getContainerWindow(), UNO_QUERY );
2302                 xTop->toFront();
2303             }
2304             else
2305             {
2306                 // no visible task that could be activated found
2307                 Reference< css::awt::XWindow > xContainerWindow;
2308                 Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
2309                 if (xBackingFrame.is())
2310                     xContainerWindow = xBackingFrame->getContainerWindow();
2311                 if (xContainerWindow.is())
2312                 {
2313                     Reference< XController > xStartModule = StartModule::createWithParentWindow(xContext, xContainerWindow);
2314                     Reference< css::awt::XWindow > xBackingWin(xStartModule, UNO_QUERY);
2315                     // Attention: You MUST(!) call setComponent() before you call attachFrame().
2316                     // Because the backing component set the property "IsBackingMode" of the frame
2317                     // to true inside attachFrame(). But setComponent() reset this state every time ...
2318                     xBackingFrame->setComponent(xBackingWin, xStartModule);
2319                     xStartModule->attachFrame(xBackingFrame);
2320                     xContainerWindow->setVisible(true);
2321 
2322                     VclPtr<vcl::Window> pCompWindow = VCLUnoHelper::GetWindow(xBackingFrame->getComponentWindow());
2323                     if (pCompWindow)
2324                         pCompWindow->Update();
2325                 }
2326             }
2327         }
2328         break;
2329     case ApplicationEvent::Type::Open:
2330         {
2331             const CommandLineArgs& rCmdLine = GetCommandLineArgs();
2332             if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
2333             {
2334                 ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
2335                 std::vector<OUString> const & data(rAppEvent.GetStringsData());
2336                 docsRequest.aOpenList.insert(
2337                     docsRequest.aOpenList.end(), data.begin(), data.end());
2338                 RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
2339             }
2340         }
2341         break;
2342     case ApplicationEvent::Type::OpenHelpUrl:
2343         // start help for a specific URL
2344         Application::GetHelp()->Start(rAppEvent.GetStringData(), static_cast<vcl::Window*>(nullptr));
2345         break;
2346     case ApplicationEvent::Type::Print:
2347         {
2348             const CommandLineArgs& rCmdLine = GetCommandLineArgs();
2349             if ( !rCmdLine.IsInvisible() && !rCmdLine.IsTerminateAfterInit() )
2350             {
2351                 ProcessDocumentsRequest docsRequest(rCmdLine.getCwdUrl());
2352                 std::vector<OUString> const & data(rAppEvent.GetStringsData());
2353                 docsRequest.aPrintList.insert(
2354                     docsRequest.aPrintList.end(), data.begin(), data.end());
2355                 RequestHandler::ExecuteCmdLineRequests(docsRequest, false);
2356             }
2357         }
2358         break;
2359     case ApplicationEvent::Type::PrivateDoShutdown:
2360         {
2361             Desktop* pD = dynamic_cast<Desktop*>(GetpApp());
2362             OSL_ENSURE( pD, "no desktop ?!?" );
2363             if( pD )
2364                 pD->doShutdown();
2365         }
2366         break;
2367     case ApplicationEvent::Type::QuickStart:
2368         if ( !GetCommandLineArgs().IsInvisible()  )
2369         {
2370             // If the office has been started the second time its command line arguments are sent through a pipe
2371             // connection to the first office. We want to reuse the quickstart option for the first office.
2372             // NOTICE: The quickstart service must be initialized inside the "main thread", so we use the
2373             // application events to do this (they are executed inside main thread)!!!
2374             // Don't start quickstart service if the user specified "--invisible" on the command line!
2375             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2376             css::office::Quickstart::createStart(xContext, true/*Quickstart*/);
2377         }
2378         break;
2379     case ApplicationEvent::Type::ShowDialog:
2380         // ignore all errors here. It's clicking a menu entry only ...
2381         // The user will try it again, in case nothing happens .-)
2382         try
2383         {
2384             Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2385 
2386             Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( xContext );
2387 
2388             Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create(xContext);
2389             css::util::URL aCommand;
2390             if( rAppEvent.GetStringData() == "PREFERENCES" )
2391                 aCommand.Complete = ".uno:OptionsTreeDialog";
2392             else if( rAppEvent.GetStringData() == "ABOUT" )
2393                 aCommand.Complete = ".uno:About";
2394             if( !aCommand.Complete.isEmpty() )
2395             {
2396                 xParser->parseStrict(aCommand);
2397 
2398                 css::uno::Reference< css::frame::XDispatch > xDispatch = xDesktop->queryDispatch(aCommand, OUString(), 0);
2399                 if (xDispatch.is())
2400                     xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >());
2401             }
2402         }
2403         catch(const css::uno::Exception&)
2404         {}
2405         break;
2406     case ApplicationEvent::Type::Unaccept:
2407         // try to remove corresponding acceptor
2408         destroyAcceptor(rAppEvent.GetStringData());
2409         break;
2410     default:
2411         SAL_WARN( "desktop.app", "this cannot happen");
2412         break;
2413     }
2414 }
2415 
OpenSplashScreen()2416 void Desktop::OpenSplashScreen()
2417 {
2418     const CommandLineArgs &rCmdLine = GetCommandLineArgs();
2419     // Show intro only if this is normal start (e.g. no server, no quickstart, no printing )
2420     if ( !rCmdLine.IsInvisible() &&
2421          !rCmdLine.IsHeadless() &&
2422          !rCmdLine.IsQuickstart() &&
2423          !rCmdLine.IsMinimized() &&
2424          !rCmdLine.IsNoLogo() &&
2425          !rCmdLine.IsTerminateAfterInit() &&
2426          rCmdLine.GetPrintList().empty() &&
2427          rCmdLine.GetPrintToList().empty() &&
2428          rCmdLine.GetConversionList().empty() )
2429     {
2430         // Determine application name from command line parameters
2431         OUString aAppName;
2432         if ( rCmdLine.IsWriter() )
2433             aAppName = "writer";
2434         else if ( rCmdLine.IsCalc() )
2435             aAppName = "calc";
2436         else if ( rCmdLine.IsDraw() )
2437             aAppName = "draw";
2438         else if ( rCmdLine.IsImpress() )
2439             aAppName = "impress";
2440         else if ( rCmdLine.IsBase() )
2441             aAppName = "base";
2442         else if ( rCmdLine.IsGlobal() )
2443             aAppName = "global";
2444         else if ( rCmdLine.IsMath() )
2445             aAppName = "math";
2446         else if ( rCmdLine.IsWeb() )
2447             aAppName = "web";
2448 
2449         // Which splash to use
2450         OUString aSplashService( "com.sun.star.office.SplashScreen" );
2451         if ( rCmdLine.HasSplashPipe() )
2452             aSplashService = "com.sun.star.office.PipeSplashScreen";
2453 
2454         Sequence< Any > aSeq( 2 );
2455         aSeq[0] <<= true; // bVisible
2456         aSeq[1] <<= aAppName;
2457         css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2458         m_rSplashScreen.set(
2459             xContext->getServiceManager()->createInstanceWithArgumentsAndContext(aSplashService, aSeq, xContext),
2460             UNO_QUERY);
2461 
2462         if(m_rSplashScreen.is())
2463                 m_rSplashScreen->start("SplashScreen", 100);
2464     }
2465 
2466 }
2467 
SetSplashScreenProgress(sal_Int32 iProgress)2468 void Desktop::SetSplashScreenProgress(sal_Int32 iProgress)
2469 {
2470     if(m_rSplashScreen.is())
2471     {
2472         m_rSplashScreen->setValue(iProgress);
2473     }
2474 }
2475 
SetSplashScreenText(const OUString & rText)2476 void Desktop::SetSplashScreenText( const OUString& rText )
2477 {
2478     if( m_rSplashScreen.is() )
2479     {
2480         m_rSplashScreen->setText( rText );
2481     }
2482 }
2483 
CloseSplashScreen()2484 void Desktop::CloseSplashScreen()
2485 {
2486     if(m_rSplashScreen.is())
2487     {
2488         m_rSplashScreen->end();
2489         m_rSplashScreen = nullptr;
2490     }
2491 }
2492 
2493 
IMPL_STATIC_LINK_NOARG(Desktop,AsyncInitFirstRun,Timer *,void)2494 IMPL_STATIC_LINK_NOARG(Desktop, AsyncInitFirstRun, Timer *, void)
2495 {
2496     // does initializations which are necessary for the first run of the office
2497     try
2498     {
2499         Reference< XJobExecutor > xExecutor = theJobExecutor::get( ::comphelper::getProcessComponentContext() );
2500         xExecutor->trigger( "onFirstRunInitialization" );
2501     }
2502     catch(const css::uno::Exception&)
2503     {
2504         TOOLS_WARN_EXCEPTION( "desktop.app", "Desktop::DoFirstRunInitializations: caught an exception while trigger job executor" );
2505     }
2506 }
2507 
ShowBackingComponent(Desktop * progress)2508 void Desktop::ShowBackingComponent(Desktop * progress)
2509 {
2510     if (GetCommandLineArgs().IsNoDefault())
2511     {
2512         return;
2513     }
2514     Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
2515     Reference< XDesktop2 > xDesktop = css::frame::Desktop::create(xContext);
2516     if (progress != nullptr)
2517     {
2518         progress->SetSplashScreenProgress(60);
2519     }
2520     Reference< XFrame > xBackingFrame = xDesktop->findFrame( "_blank", 0);
2521     Reference< css::awt::XWindow > xContainerWindow;
2522 
2523     if (xBackingFrame.is())
2524         xContainerWindow = xBackingFrame->getContainerWindow();
2525     if (xContainerWindow.is())
2526     {
2527         // set the WindowExtendedStyle::Document style. Normally, this is done by the TaskCreator service when a "_blank"
2528         // frame/window is created. Since we do not use the TaskCreator here, we need to mimic its behavior,
2529         // otherwise documents loaded into this frame will later on miss functionality depending on the style.
2530         VclPtr<vcl::Window> pContainerWindow = VCLUnoHelper::GetWindow( xContainerWindow );
2531         SAL_WARN_IF( !pContainerWindow, "desktop.app", "Desktop::Main: no implementation access to the frame's container window!" );
2532         pContainerWindow->SetExtendedStyle( pContainerWindow->GetExtendedStyle() | WindowExtendedStyle::Document );
2533         if (progress != nullptr)
2534         {
2535             progress->SetSplashScreenProgress(75);
2536         }
2537 
2538         Reference< XController > xStartModule = StartModule::createWithParentWindow( xContext, xContainerWindow);
2539         // Attention: You MUST(!) call setComponent() before you call attachFrame().
2540         // Because the backing component set the property "IsBackingMode" of the frame
2541         // to true inside attachFrame(). But setComponent() reset this state everytimes ...
2542         xBackingFrame->setComponent(Reference< XWindow >(xStartModule, UNO_QUERY), xStartModule);
2543         if (progress != nullptr)
2544         {
2545             progress->SetSplashScreenProgress(100);
2546         }
2547         xStartModule->attachFrame(xBackingFrame);
2548         if (progress != nullptr)
2549         {
2550             progress->CloseSplashScreen();
2551         }
2552         xContainerWindow->setVisible(true);
2553     }
2554 }
2555 
2556 
CheckFirstRun()2557 void Desktop::CheckFirstRun( )
2558 {
2559     if (officecfg::Office::Common::Misc::FirstRun::get())
2560     {
2561         // use VCL timer, which won't trigger during shutdown if the
2562         // application exits before timeout
2563         m_firstRunTimer.Start();
2564 
2565 #ifdef _WIN32
2566         // Check if Quickstarter should be started (on Windows only)
2567         WCHAR szValue[8192];
2568         DWORD nValueSize = sizeof(szValue);
2569         HKEY hKey;
2570         if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, L"Software\\LibreOffice", &hKey ) )
2571         {
2572             if ( ERROR_SUCCESS == RegQueryValueExW( hKey, L"RunQuickstartAtFirstStart", nullptr, nullptr, reinterpret_cast<LPBYTE>(szValue), &nValueSize ) )
2573             {
2574                 css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
2575                 css::office::Quickstart::createAutoStart(xContext, true/*Quickstart*/, true/*bAutostart*/);
2576                 RegCloseKey( hKey );
2577             }
2578         }
2579 #endif
2580 
2581         std::shared_ptr< comphelper::ConfigurationChanges > batch(
2582             comphelper::ConfigurationChanges::create());
2583         officecfg::Office::Common::Misc::FirstRun::set(false, batch);
2584         batch->commit();
2585     }
2586 }
2587 
2588 }
2589 
2590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2591