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