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