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