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