summaryrefslogtreecommitdiffstats
path: root/extensions/source/update/check/updatecheck.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'extensions/source/update/check/updatecheck.cxx')
-rw-r--r--extensions/source/update/check/updatecheck.cxx1617
1 files changed, 1617 insertions, 0 deletions
diff --git a/extensions/source/update/check/updatecheck.cxx b/extensions/source/update/check/updatecheck.cxx
new file mode 100644
index 000000000..8669f890e
--- /dev/null
+++ b/extensions/source/update/check/updatecheck.cxx
@@ -0,0 +1,1617 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <comphelper/scopeguard.hxx>
+#include <config_folders.h>
+
+#include "updatecheck.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/office/Quickstart.hpp>
+#include <com/sun/star/system/SystemShellExecute.hpp>
+#include <com/sun/star/system/SystemShellExecuteException.hpp>
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/task/XJob.hpp>
+#include <com/sun/star/task/XJobExecutor.hpp>
+
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <sal/macros.h>
+#include <sal/log.hxx>
+#include <tools/diagnose_ex.h>
+
+#ifdef _WIN32
+#include <o3tl/safeCoInitUninit.hxx>
+#include <objbase.h>
+#endif
+
+#include "onlinecheck.hxx"
+#include "updateprotocol.hxx"
+#include "updatecheckconfig.hxx"
+
+namespace beans = com::sun::star::beans ;
+namespace deployment = com::sun::star::deployment ;
+namespace frame = com::sun::star::frame ;
+namespace lang = com::sun::star::lang ;
+namespace c3s = com::sun::star::system ;
+namespace task = com::sun::star::task ;
+namespace uno = com::sun::star::uno ;
+
+constexpr OUStringLiteral PROPERTY_TITLE = u"BubbleHeading";
+constexpr OUStringLiteral PROPERTY_TEXT = u"BubbleText";
+constexpr OUStringLiteral PROPERTY_SHOW_BUBBLE = u"BubbleVisible";
+constexpr OUStringLiteral PROPERTY_CLICK_HDL = u"MenuClickHDL";
+constexpr OUStringLiteral PROPERTY_SHOW_MENUICON = u"MenuIconVisible";
+
+// Returns the URL of the release note for the given position
+OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled)
+{
+ for (auto const& elem : rInfo.ReleaseNotes)
+ {
+ if( pos == elem.Pos )
+ {
+ if( (pos > 2) || !autoDownloadEnabled || elem.URL2.isEmpty() )
+ return elem.URL;
+ }
+ else if( (pos == elem.Pos2) && ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled )
+ return elem.URL2;
+ }
+
+ return OUString();
+}
+
+
+namespace
+{
+
+OUString getBuildId()
+{
+ OUString aPathVal("${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}");
+ rtl::Bootstrap::expandMacros(aPathVal);
+ return aPathVal;
+}
+
+
+#if (defined LINUX || defined __sun)
+OUString getBaseInstallation()
+{
+ OUString aPathVal("$BRAND_BASE_DIR");
+ rtl::Bootstrap::expandMacros(aPathVal);
+ return aPathVal;
+}
+#endif
+
+
+bool isObsoleteUpdateInfo(std::u16string_view rBuildId)
+{
+ return rBuildId != getBuildId() && !rBuildId.empty();
+}
+
+
+OUString getImageFromFileName(const OUString& aFile)
+{
+#ifndef _WIN32
+ OUString aUnpackPath;
+ if( osl_getExecutableFile(&aUnpackPath.pData) == osl_Process_E_None )
+ {
+ sal_uInt32 lastIndex = aUnpackPath.lastIndexOf('/');
+ if ( lastIndex > 0 )
+ {
+ aUnpackPath = OUString::Concat(aUnpackPath.subView( 0, lastIndex+1 )) +
+ "unpack_update";
+ }
+
+ oslFileHandle hOut = nullptr;
+ oslProcess hProcess = nullptr;
+
+ OUString aSystemPath;
+ osl::File::getSystemPathFromFileURL(aFile, aSystemPath);
+
+ oslProcessError rc = osl_executeProcess_WithRedirectedIO(
+ aUnpackPath.pData, // [in] Image name
+ &aSystemPath.pData, 1, // [in] Arguments
+ osl_Process_WAIT | osl_Process_NORMAL, // [in] Options
+ nullptr, // [in] Security
+ nullptr, // [in] Working directory
+ nullptr, 0, // [in] Environment variables
+ &hProcess, // [out] Process handle
+ nullptr, &hOut, nullptr // [out] File handles for redirected I/O
+ );
+
+ if( osl_Process_E_None == rc )
+ {
+ // Create a guard to ensure correct cleanup in its dtor in any case
+ comphelper::ScopeGuard g([hOut, hProcess] () {
+ osl_closeFile(hOut);
+ osl_freeProcessHandle(hProcess);
+ });
+
+ oslProcessInfo aInfo;
+ aInfo.Size = sizeof(oslProcessInfo);
+
+ if( osl_Process_E_None == osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo) )
+ {
+ if( 0 == aInfo.Code )
+ {
+ char szBuffer[4096];
+ sal_uInt64 nBytesRead = 0;
+ const sal_uInt64 nBytesToRead = sizeof(szBuffer) - 1;
+
+ OUString aImageName;
+ while( osl_File_E_None == osl_readFile(hOut, szBuffer, nBytesToRead, &nBytesRead) )
+ {
+ char *pc = szBuffer + nBytesRead;
+ do
+ {
+ *pc = '\0'; --pc;
+ }
+ while( ('\n' == *pc) || ('\r' == *pc) );
+
+ aImageName += OUString(szBuffer, pc - szBuffer + 1, osl_getThreadTextEncoding());
+
+ if( nBytesRead < nBytesToRead )
+ break;
+ }
+
+ if( osl::FileBase::E_None == osl::FileBase::getFileURLFromSystemPath(aImageName, aImageName) )
+ return aImageName;
+ }
+ }
+ }
+ }
+#endif
+
+ return aFile;
+}
+
+
+uno::Reference< beans::XPropertySet > createMenuBarUI(
+ const uno::Reference< uno::XComponentContext >& xContext,
+ const uno::Reference< task::XJob >& xJob)
+{
+ if( !xContext.is() )
+ throw uno::RuntimeException(
+ "UpdateCheckJob: empty component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException(
+ "UpdateCheckJob: unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< beans::XPropertySet > xMenuBarUI(
+ xServiceManager->createInstanceWithContext( "com.sun.star.setup.UpdateCheckUI", xContext ),
+ uno::UNO_QUERY_THROW);
+
+ xMenuBarUI->setPropertyValue( PROPERTY_CLICK_HDL, uno::Any( xJob ) );
+
+ return xMenuBarUI;
+}
+
+
+typedef sal_Bool (* OnlineCheckFunc) ();
+
+class UpdateCheckThread : public WorkerThread
+{
+
+public:
+ UpdateCheckThread( osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ rtl::Reference<UpdateCheck> const & controller );
+
+ virtual void SAL_CALL join() override;
+ virtual void SAL_CALL terminate() override;
+ virtual void cancel() override;
+
+ void cancelAsSoonAsPossible();
+
+protected:
+ virtual ~UpdateCheckThread() override;
+
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+ /* Wrapper around checkForUpdates */
+ bool runCheck( bool & rbExtensionsChecked );
+
+private:
+
+ /* Used to avoid dialup login windows (on platforms we know how to double this) */
+ static bool hasInternetConnection()
+ {
+#ifdef _WIN32
+ return WNT_hasInternetConnection();
+#else
+ return true;
+#endif
+ }
+
+ /* Creates a new instance of UpdateInformationProvider and returns this instance */
+ uno::Reference<deployment::XUpdateInformationProvider> createProvider()
+ {
+ osl::MutexGuard aGuard(m_aMutex);
+ m_xProvider = deployment::UpdateInformationProvider::create(m_xContext);
+ return m_xProvider;
+ };
+
+ /* Returns the remembered instance of UpdateInformationProvider if any */
+ uno::Reference<deployment::XUpdateInformationProvider> getProvider()
+ { osl::MutexGuard aGuard(m_aMutex); return m_xProvider; };
+
+ /* Releases the remembered instance of UpdateInformationProvider if any */
+ void clearProvider()
+ { osl::MutexGuard aGuard(m_aMutex); m_xProvider.clear(); };
+
+ osl::Mutex m_aMutex;
+
+protected:
+ osl::Condition& m_aCondition;
+
+private:
+ const uno::Reference<uno::XComponentContext> m_xContext;
+ uno::Reference<deployment::XUpdateInformationProvider> m_xProvider;
+ rtl::Reference<UpdateCheck> m_controller;
+ bool m_cancelAsSoonAsPossible;
+};
+
+
+class ManualUpdateCheckThread : public UpdateCheckThread
+{
+public:
+ ManualUpdateCheckThread( osl::Condition& rCondition, const uno::Reference<uno::XComponentContext>& xContext ) :
+ UpdateCheckThread(rCondition, xContext, {}) {};
+
+ virtual void SAL_CALL run() override;
+};
+
+
+class MenuBarButtonJob : public ::cppu::WeakImplHelper< task::XJob >
+{
+public:
+ explicit MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck);
+
+ // XJob
+ virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
+
+private:
+ rtl::Reference< UpdateCheck > m_aUpdateCheck;
+};
+
+class DownloadThread : public WorkerThread
+{
+public:
+ DownloadThread(
+ osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const rtl::Reference< DownloadInteractionHandler >& rHandler,
+ const OUString& rURL );
+
+ virtual void SAL_CALL run() override;
+ virtual void cancel() override;
+ virtual void SAL_CALL suspend() override;
+ virtual void SAL_CALL onTerminated() override;
+
+protected:
+ virtual ~DownloadThread() override;
+
+private:
+ osl::Condition& m_aCondition;
+ const uno::Reference<uno::XComponentContext> m_xContext;
+ const OUString m_aURL;
+ Download m_aDownload;
+};
+
+
+class ShutdownThread : public osl::Thread
+{
+public:
+ explicit ShutdownThread(const uno::Reference<uno::XComponentContext>& xContext);
+
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+protected:
+ virtual ~ShutdownThread() override;
+
+private:
+ osl::Condition m_aCondition;
+ const uno::Reference<uno::XComponentContext> m_xContext;
+};
+
+
+UpdateCheckThread::UpdateCheckThread( osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ rtl::Reference<UpdateCheck> const & controller ) :
+ m_aCondition(rCondition),
+ m_xContext(xContext),
+ m_controller(controller),
+ m_cancelAsSoonAsPossible(false)
+{
+ createSuspended();
+
+ // actually run the thread
+ resume();
+}
+
+
+UpdateCheckThread::~UpdateCheckThread()
+{
+}
+
+
+void SAL_CALL
+UpdateCheckThread::terminate()
+{
+ // Cancel potentially hanging http request ..
+ cancel();
+ // .. before terminating
+ osl::Thread::terminate();
+}
+
+
+void SAL_CALL
+UpdateCheckThread::join()
+{
+ uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
+
+ // do not join during an update check until #i73893# is fixed
+ if( ! xProvider.is() )
+ {
+ osl::Thread::join();
+ }
+}
+
+
+void
+UpdateCheckThread::cancel()
+{
+ uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
+
+ if( xProvider.is() )
+ xProvider->cancel();
+}
+
+void UpdateCheckThread::cancelAsSoonAsPossible() {
+ {
+ osl::MutexGuard g(m_aMutex);
+ m_cancelAsSoonAsPossible = true;
+ }
+ m_aCondition.set();
+}
+
+bool
+UpdateCheckThread::runCheck( bool & rbExtensionsChecked )
+{
+ bool ret = false;
+ UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+
+ UpdateInfo aInfo;
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+
+ if( checkForUpdates(aInfo, m_xContext, aController->getInteractionHandler(), createProvider()) )
+ {
+ aController->setUpdateInfo(aInfo);
+ eUIState = UpdateCheck::getUIState(aInfo);
+ ret = true;
+ }
+ else
+ aController->setCheckFailedState();
+
+ // We will only look for extension updates, when there is no 'check for office updates' dialog open
+ // and when there was no office update found
+ if ( ( eUIState != UPDATESTATE_UPDATE_AVAIL ) &&
+ ( eUIState != UPDATESTATE_UPDATE_NO_DOWNLOAD ) &&
+ !aController->isDialogShowing() &&
+ !rbExtensionsChecked )
+ {
+ bool bHasExtensionUpdates = checkForExtensionUpdates( m_xContext );
+ aController->setHasExtensionUpdates( bHasExtensionUpdates );
+ if ( bHasExtensionUpdates )
+ aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL );
+ rbExtensionsChecked = true;
+ }
+
+ // joining with this thread is safe again
+ clearProvider();
+ return ret;
+}
+
+
+void SAL_CALL
+UpdateCheckThread::onTerminated()
+{
+ delete this;
+}
+
+
+void SAL_CALL
+UpdateCheckThread::run()
+{
+ osl_setThreadName("UpdateCheckThread");
+
+ TimeValue systime;
+ TimeValue nExtCheckTime;
+ osl_getSystemTime( &nExtCheckTime );
+
+ osl::Condition::Result aResult = osl::Condition::result_timeout;
+ TimeValue tv = { 10, 0 };
+
+ // Initial wait to avoid doing further time consuming tasks during start-up
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+
+ try {
+ bool bExtensionsChecked = false;
+
+ while( schedule() )
+ {
+ /* Use cases:
+ * a) manual check requested from auto check thread - "last check" should not be checked (one time)
+ * a1) manual check was requested in the middle of a running auto check,
+ * condition is set
+ * a2) manual check was requested while waiting for a retry,
+ * condition is set
+ * a3) manual check was requested while waiting for time to next
+ * scheduled check elapsing, condition is set
+ * a4) manual check was requested during initial wait, condition is set
+ * b) check interval got changed, condition may be set - same sub-cases as a),
+ * but "last check" should be honored
+ * c) normal auto check mode, condition not set - "last check" should be honored
+ */
+
+ // Accessing const members without synchronization
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *aController);
+
+ // FIXME: remember last & offset ?
+ sal_Int64 last = rModel->getLastChecked();
+ sal_Int64 offset = rModel->getCheckInterval();
+
+ rModel.clear();
+
+ // last == 0 means check immediately
+ bool checkNow = last <= 0;
+
+ // Reset the condition to avoid busy loops
+ if( osl::Condition::result_ok == aResult )
+ {
+ m_aCondition.reset();
+ aResult = osl::Condition::result_timeout;
+ checkNow = aController->isDialogShowing();
+ }
+
+ if( ! checkNow )
+ {
+ osl_getSystemTime(&systime);
+
+ // Go back to sleep until time has elapsed
+ sal_Int64 next = last + offset;
+ if( last + offset > systime.Seconds )
+ {
+ // This can not be > 32 Bit for now ..
+ tv.Seconds = static_cast< sal_Int32 > (next - systime.Seconds);
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+ continue;
+ }
+ }
+
+ static sal_uInt8 n = 0;
+
+ if( ! hasInternetConnection() || ! runCheck( bExtensionsChecked ) )
+ {
+ // the extension update check should be independent from the office update check
+
+ osl_getSystemTime( &systime );
+ if ( nExtCheckTime.Seconds + offset < systime.Seconds )
+ bExtensionsChecked = false;
+
+ // Increase next by 15, 60, .. minutes
+ static const sal_Int32 nRetryInterval[] = { 900, 3600, 14400, 86400 };
+
+ if( n < SAL_N_ELEMENTS(nRetryInterval) )
+ ++n;
+
+ tv.Seconds = nRetryInterval[n-1];
+ aResult = m_aCondition.wait(&tv);
+ {
+ osl::MutexGuard g(m_aMutex);
+ if (m_cancelAsSoonAsPossible) {
+ goto done;
+ }
+ }
+ }
+ else // reset retry counter
+ {
+ n = 0;
+ bExtensionsChecked = false;
+ }
+ }
+ }
+
+ catch(const uno::Exception&) {
+ // Silently catch all errors
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
+ }
+
+done:
+ if (m_controller.is()) {
+ m_controller->notifyUpdateCheckFinished();
+ }
+}
+
+
+void SAL_CALL
+ManualUpdateCheckThread::run()
+{
+ try {
+ bool bExtensionsChecked = false;
+ runCheck( bExtensionsChecked );
+ m_aCondition.reset();
+ }
+ catch(const uno::Exception&) {
+ // Silently catch all errors
+ TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
+ }
+}
+
+
+MenuBarButtonJob::MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck) :
+ m_aUpdateCheck(rUpdateCheck)
+{
+};
+
+
+uno::Any SAL_CALL
+MenuBarButtonJob::execute(const uno::Sequence<beans::NamedValue>& )
+{
+ if ( m_aUpdateCheck->shouldShowExtUpdDlg() )
+ m_aUpdateCheck->showExtensionDialog();
+ else
+ m_aUpdateCheck->showDialog();
+
+ return uno::Any();
+}
+
+
+DownloadThread::DownloadThread(osl::Condition& rCondition,
+ const uno::Reference<uno::XComponentContext>& xContext,
+ const rtl::Reference< DownloadInteractionHandler >& rHandler,
+ const OUString& rURL) :
+ m_aCondition(rCondition),
+ m_xContext(xContext),
+ m_aURL(rURL),
+ m_aDownload(xContext, rHandler)
+{
+ createSuspended();
+}
+
+
+DownloadThread::~DownloadThread()
+{
+}
+
+
+void SAL_CALL
+DownloadThread::run()
+{
+ osl_setThreadName("DownloadThread");
+
+#ifdef _WIN32
+ int nNbCallCoInitializeExForReinit = 0;
+ // for SystemShellExecute
+ o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
+#endif
+
+ while( schedule() )
+ {
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+
+ OUString aLocalFile = rModel->getLocalFileName();
+ OUString aDownloadDest = rModel->getDownloadDestination();
+
+ // release config class for now
+ rModel.clear();
+
+ static sal_uInt8 n = 0;
+ if( ! m_aDownload.start(m_aURL, aLocalFile, aDownloadDest ) )
+ {
+ // retry every 15s unless the dialog is not visible
+ TimeValue tv;
+ tv.Seconds = 15;
+
+ if( ! UpdateCheck::get()->isDialogShowing() )
+ {
+ // Increase next by 1, 5, 15, 60, .. minutes
+ static const sal_Int16 nRetryInterval[] = { 60, 300, 900, 3600 };
+
+ if( n < SAL_N_ELEMENTS(nRetryInterval) )
+ ++n;
+
+ tv.Seconds = nRetryInterval[n-1];
+ }
+ m_aCondition.wait(&tv);
+ }
+ else
+ {
+ // reset wait period after successful download
+ n=0;
+ }
+ }
+#ifdef _WIN32
+ o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
+#endif
+}
+
+
+void DownloadThread::cancel()
+{
+ m_aDownload.stop();
+ resume();
+
+ rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
+ aController->cancelDownload();
+}
+
+
+void SAL_CALL DownloadThread::suspend()
+{
+ osl::Thread::suspend();
+ m_aDownload.stop();
+}
+
+
+void SAL_CALL DownloadThread::onTerminated()
+{
+ delete this;
+}
+
+
+ShutdownThread::ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext) :
+ m_xContext( xContext )
+{
+ create();
+}
+
+
+ShutdownThread::~ShutdownThread()
+{
+}
+
+
+void SAL_CALL
+ShutdownThread::run()
+{
+ osl_setThreadName("ShutdownThread");
+
+ TimeValue tv = { 0, 250 };
+
+ m_aCondition.wait(&tv);
+
+ // Tell QuickStarter not to veto ..
+ uno::Reference< css::beans::XFastPropertySet > xQuickStarter = css::office::Quickstart::createDefault(m_xContext);
+
+ xQuickStarter->setFastPropertyValue(0, uno::Any(false));
+
+ // Shutdown the office
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create(m_xContext);
+
+ xDesktop->terminate();
+}
+
+
+void SAL_CALL ShutdownThread::onTerminated()
+{
+ delete this;
+}
+
+
+} // anonymous namespace
+
+UpdateCheck::UpdateCheck()
+ : m_eState(NOT_INITIALIZED)
+ , m_eUpdateState(UPDATESTATES_COUNT)
+ , m_pThread(nullptr)
+ , m_bHasExtensionUpdate(false)
+ , m_bShowExtUpdDlg(false)
+ , m_updateCheckRunning(false)
+{
+}
+
+UpdateCheck::~UpdateCheck() {}
+
+void
+UpdateCheck::initialize(const uno::Sequence< beans::NamedValue >& rValues,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if( NOT_INITIALIZED == m_eState )
+ {
+ NamedValueByNameAccess aNameAccess(rValues);
+ UpdateCheckROModel aModel( aNameAccess );
+ m_xContext = xContext;
+
+ OUString aUpdateEntryVersion = aModel.getUpdateEntryVersion();
+
+ aModel.getUpdateEntry(m_aUpdateInfo);
+
+ bool obsoleteUpdateInfo = isObsoleteUpdateInfo(aUpdateEntryVersion);
+ bool bContinueDownload = false;
+ bool bDownloadAvailable = false;
+
+ m_bHasExtensionUpdate = checkForPendingUpdates( xContext );
+ m_bShowExtUpdDlg = false;
+
+ OUString aLocalFileName = aModel.getLocalFileName();
+
+ if( !aLocalFileName.isEmpty() )
+ {
+ bContinueDownload = true;
+
+ // Try to get the number of bytes already on disk
+ osl::DirectoryItem aDirectoryItem;
+ if( osl::DirectoryItem::E_None == osl::DirectoryItem::get(aLocalFileName, aDirectoryItem) )
+ {
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileSize);
+ if( osl::DirectoryItem::E_None == aDirectoryItem.getFileStatus(aFileStatus) )
+ {
+ sal_Int64 nDownloadSize = aModel.getDownloadSize();
+ sal_Int64 nFileSize = aFileStatus.getFileSize();
+
+ if( nDownloadSize > 0 )
+ {
+ if ( nDownloadSize <= nFileSize ) // we have already downloaded everything
+ {
+ bContinueDownload = false;
+ bDownloadAvailable = true;
+ m_aImageName = getImageFromFileName( aLocalFileName );
+ }
+ else // Calculate initial percent value.
+ {
+ sal_Int32 nPercent = static_cast<sal_Int32>(100 * nFileSize / nDownloadSize);
+ getUpdateHandler()->setProgress( nPercent );
+ }
+ }
+ }
+ }
+
+ if ( bContinueDownload )
+ {
+ bool downloadPaused = aModel.isDownloadPaused();
+
+ enableDownload(true, downloadPaused);
+ // coverity[lock_order : FALSE] - incorrect report of lock order error with std::recursive_mutex
+ setUIState(downloadPaused ? UPDATESTATE_DOWNLOAD_PAUSED : UPDATESTATE_DOWNLOADING);
+ }
+
+ }
+ if ( !bContinueDownload )
+ {
+ // We do this intentionally only if no download is in progress ..
+ if( obsoleteUpdateInfo )
+ {
+ // Bring-up release note for position 5 ..
+ const OUString aURL(getReleaseNote(m_aUpdateInfo, 5));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+
+ // Data is outdated, probably due to installed update
+ rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( xContext, *this );
+ aConfig->clearUpdateFound();
+ aConfig->clearLocalFileName();
+
+
+ m_aUpdateInfo = UpdateInfo();
+ // Remove outdated release notes
+ storeReleaseNote( 1, OUString() );
+ storeReleaseNote( 2, OUString() );
+ }
+ else
+ {
+ enableAutoCheck(aModel.isAutoCheckEnabled());
+ if ( bDownloadAvailable )
+ setUIState( UPDATESTATE_DOWNLOAD_AVAIL );
+ else
+ {
+ // coverity[lock_order : FALSE] - incorrect report of lock order error with std::recursive_mutex
+ setUIState(getUIState(m_aUpdateInfo));
+ }
+ }
+ }
+ }
+}
+
+
+void
+UpdateCheck::cancel()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ WorkerThread *pThread = m_pThread;
+ UpdateState eUIState = getUIState(m_aUpdateInfo);
+
+ aGuard.unlock();
+
+ if( nullptr != pThread )
+ pThread->cancel();
+
+ setUIState(eUIState);
+}
+
+
+void
+UpdateCheck::download()
+{
+ std::unique_lock aGuard(m_aMutex);
+ UpdateInfo aInfo(m_aUpdateInfo);
+ State eState = m_eState;
+ aGuard.unlock();
+
+ if (aInfo.Sources.empty())
+ {
+ SAL_WARN("extensions.update", "download called without source");
+ return;
+ }
+
+ if( aInfo.Sources[0].IsDirect )
+ {
+ // Ignore second click of a double click
+ if( DOWNLOADING != eState )
+ {
+ shutdownThread(true);
+
+ {
+ std::scoped_lock aGuard2(m_aMutex);
+ enableDownload(true);
+ }
+ setUIState(UPDATESTATE_DOWNLOADING);
+ }
+ }
+ else
+ {
+ showReleaseNote(aInfo.Sources[0].URL); // Display in browser
+ }
+}
+
+
+void
+UpdateCheck::install()
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ const uno::Reference< c3s::XSystemShellExecute > xShellExecute = c3s::SystemShellExecute::create( m_xContext );
+
+ try {
+ // Construct install command ??
+
+ // Store release note for position 3 and 4
+ OUString aURL(getReleaseNote(m_aUpdateInfo, 3));
+ storeReleaseNote(1, aURL);
+
+ aURL = getReleaseNote(m_aUpdateInfo, 4);
+ storeReleaseNote(2, aURL);
+
+ OUString aInstallImage(m_aImageName);
+ osl::FileBase::getSystemPathFromFileURL(aInstallImage, aInstallImage);
+
+ sal_Int32 nFlags;
+#if (defined LINUX || defined __sun)
+ nFlags = 42;
+ OUString aParameter = getBaseInstallation();
+ if( !aParameter.isEmpty() )
+ osl::FileBase::getSystemPathFromFileURL(aParameter, aParameter);
+
+ aParameter += " &";
+#else
+ nFlags = c3s::SystemShellExecuteFlags::DEFAULTS;
+ OUString const aParameter;
+#endif
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
+ rModel->clearLocalFileName();
+
+ xShellExecute->execute(aInstallImage, aParameter, nFlags);
+ new ShutdownThread( m_xContext );
+ } catch(const uno::Exception&) {
+ m_aUpdateHandler->setErrorMessage( m_aUpdateHandler->getDefaultInstErrMsg() );
+ }
+}
+
+
+void
+UpdateCheck::pause()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( nullptr != m_pThread )
+ m_pThread->suspend();
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+ aGuard.unlock();
+
+ rModel->storeDownloadPaused(true);
+ setUIState(UPDATESTATE_DOWNLOAD_PAUSED);
+}
+
+
+void
+UpdateCheck::resume()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( nullptr != m_pThread )
+ m_pThread->resume();
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+ aGuard.unlock();
+
+ rModel->storeDownloadPaused(false);
+ setUIState(UPDATESTATE_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::closeAfterFailure()
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if ( ( m_eState == DISABLED ) || ( m_eState == CHECK_SCHEDULED ) )
+ {
+ const UpdateState eUIState = getUIState( m_aUpdateInfo );
+ aGuard.unlock();
+ setUIState( eUIState, true );
+ }
+}
+
+void UpdateCheck::notifyUpdateCheckFinished() {
+ std::scoped_lock l(m_aMutex);
+ m_updateCheckRunning = false;
+ m_updateCheckFinished.notify_all();
+}
+
+void UpdateCheck::waitForUpdateCheckFinished() {
+ UpdateCheckThread * thread;
+ {
+ std::scoped_lock l(m_aMutex);
+ thread = dynamic_cast<UpdateCheckThread *>(m_pThread);
+ }
+ if (thread != nullptr) {
+ thread->cancelAsSoonAsPossible();
+ }
+ for (;;) {
+ std::unique_lock lock(m_aMutex);
+ if (!m_updateCheckRunning) {
+ return;
+ }
+ m_updateCheckFinished.wait(lock);
+ }
+}
+
+void
+UpdateCheck::shutdownThread(bool join)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ // copy thread object pointer to stack
+ osl::Thread *pThread = m_pThread;
+ m_pThread = nullptr;
+ aGuard.unlock();
+
+ if( nullptr != pThread )
+ {
+ pThread->terminate();
+ if( join )
+ {
+ m_aCondition.set();
+ pThread->join();
+ m_aCondition.reset();
+ }
+ }
+}
+
+
+void
+UpdateCheck::enableAutoCheck(bool enable)
+{
+ if( enable )
+ {
+ m_updateCheckRunning = true;
+ m_pThread = new UpdateCheckThread(m_aCondition, m_xContext, this);
+ }
+
+ m_eState = enable ? CHECK_SCHEDULED : DISABLED;
+}
+
+
+void
+UpdateCheck::enableDownload(bool enable, bool paused)
+{
+ OSL_ASSERT(nullptr == m_pThread);
+
+ if( enable )
+ {
+ m_pThread = new DownloadThread(m_aCondition, m_xContext, this, m_aUpdateInfo.Sources[0].URL );
+ State eState = DISABLED;
+ if( !paused )
+ {
+ eState = DOWNLOADING;
+ m_pThread->resume();
+ }
+ else
+ eState = DOWNLOAD_PAUSED;
+
+ m_eState = eState;
+ }
+ else {
+ enableAutoCheck(UpdateCheckConfig::get(m_xContext)->isAutoCheckEnabled());
+ }
+
+}
+
+
+bool
+UpdateCheck::downloadTargetExists(const OUString& rFileName)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ UpdateState eUIState = UPDATESTATE_DOWNLOADING;
+
+ bool cont = false;
+
+ if( aUpdateHandler->isVisible() )
+ {
+ cont = aUpdateHandler->showOverwriteWarning();
+ if( cont )
+ {
+ if( osl_File_E_None != osl_removeFile(rFileName.pData) )
+ {
+ // FIXME: error message
+ cont = false;
+ }
+ }
+ else
+ eUIState = getUIState(m_aUpdateInfo);
+ }
+ else
+ {
+ m_aImageName = getImageFromFileName(rFileName);
+ eUIState = UPDATESTATE_DOWNLOAD_AVAIL;
+ }
+
+ if( !cont )
+ {
+ shutdownThread(false);
+ enableDownload(false);
+
+ aGuard.unlock();
+ setUIState(eUIState);
+ }
+
+ return cont;
+}
+
+
+bool UpdateCheck::checkDownloadDestination( const OUString& rFileName )
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateHandler > aUpdateHandler( getUpdateHandler() );
+
+ bool bReload = false;
+
+ if( aUpdateHandler->isVisible() )
+ {
+ bReload = aUpdateHandler->showOverwriteWarning( rFileName );
+ }
+
+ return bReload;
+}
+
+
+void
+UpdateCheck::downloadStalled(const OUString& rErrorMessage)
+{
+ std::unique_lock aGuard(m_aMutex);
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ aGuard.unlock();
+
+ aUpdateHandler->setErrorMessage(rErrorMessage);
+ setUIState(UPDATESTATE_ERROR_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::downloadProgressAt(sal_Int8 nPercent)
+{
+ std::unique_lock aGuard(m_aMutex);
+ rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
+ aGuard.unlock();
+
+ aUpdateHandler->setProgress(nPercent);
+ setUIState(UPDATESTATE_DOWNLOADING);
+}
+
+
+void
+UpdateCheck::downloadStarted(const OUString& rLocalFileName, sal_Int64 nFileSize)
+{
+ if ( nFileSize > 0 )
+ {
+ std::scoped_lock aGuard(m_aMutex);
+
+ rtl::Reference< UpdateCheckConfig > aModel(UpdateCheckConfig::get(m_xContext));
+ aModel->storeLocalFileName(rLocalFileName, nFileSize);
+
+ // Bring-up release note for position 1 ..
+ const OUString aURL(getReleaseNote(m_aUpdateInfo, 1, aModel->isAutoDownloadEnabled()));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+ }
+}
+
+
+void
+UpdateCheck::downloadFinished(const OUString& rLocalFileName)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ // no more retries
+ m_pThread->terminate();
+
+ m_aImageName = getImageFromFileName(rLocalFileName);
+ UpdateInfo aUpdateInfo(m_aUpdateInfo);
+
+ aGuard.unlock();
+ setUIState(UPDATESTATE_DOWNLOAD_AVAIL);
+
+ // Bring-up release note for position 2 ..
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
+ const OUString aURL(getReleaseNote(aUpdateInfo, 2, rModel->isAutoDownloadEnabled()));
+ if( !aURL.isEmpty() )
+ showReleaseNote(aURL);
+}
+
+
+void
+UpdateCheck::cancelDownload()
+{
+ shutdownThread(true);
+
+ std::scoped_lock aGuard(m_aMutex);
+ enableDownload(false);
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
+
+ OUString aLocalFile(rModel->getLocalFileName());
+ rModel->clearLocalFileName();
+ rModel->storeDownloadPaused(false);
+
+ if( isObsoleteUpdateInfo(rModel->getUpdateEntryVersion()) )
+ {
+ rModel->clearUpdateFound(); // This wasn't done during init yet ..
+ m_aUpdateInfo = UpdateInfo();
+ }
+
+ /*oslFileError rc =*/ osl_removeFile(aLocalFile.pData);
+ // FIXME: error handling ..
+
+}
+
+
+void
+UpdateCheck::showDialog(bool forceCheck)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ bool update_found = !m_aUpdateInfo.BuildId.isEmpty();
+ bool bSetUIState = ! m_aUpdateHandler.is();
+
+ UpdateState eDialogState = UPDATESTATES_COUNT;
+
+ switch( m_eState )
+ {
+ case DISABLED:
+ case CHECK_SCHEDULED:
+ if( forceCheck || ! update_found ) // Run check when forced or if we did not find an update yet
+ {
+ eDialogState = UPDATESTATE_CHECKING;
+ bSetUIState = true;
+ }
+ else if(m_aUpdateInfo.Sources[0].IsDirect)
+ eDialogState = UPDATESTATE_UPDATE_AVAIL;
+ else
+ eDialogState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ break;
+
+ case DOWNLOADING:
+ eDialogState = UPDATESTATE_DOWNLOADING;
+ break;
+
+ case DOWNLOAD_PAUSED:
+ eDialogState = UPDATESTATE_DOWNLOAD_PAUSED;
+ break;
+
+ case NOT_INITIALIZED:
+ OSL_ASSERT( false );
+ break;
+ }
+
+ if( bSetUIState )
+ {
+ aGuard.unlock();
+ setUIState(eDialogState, true); // suppress bubble as Dialog will be visible soon
+ aGuard.lock();
+ }
+
+ getUpdateHandler()->setVisible();
+
+ // Run check in separate thread ..
+ if( UPDATESTATE_CHECKING == eDialogState )
+ {
+ if( DISABLED == m_eState )
+ {
+ // destructs itself when done, not cancellable for now ..
+ new ManualUpdateCheckThread(m_aCondition, m_xContext);
+ }
+
+ m_aCondition.set();
+ }
+}
+
+
+void
+UpdateCheck::setUpdateInfo(const UpdateInfo& aInfo)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ bool bSuppressBubble = aInfo.BuildId == m_aUpdateInfo.BuildId;
+ m_aUpdateInfo = aInfo;
+
+ OSL_ASSERT(DISABLED == m_eState || CHECK_SCHEDULED == m_eState);
+
+ // Ignore leading non direct download if we get direct ones
+ std::vector< DownloadSource >::iterator iter = std::find_if(m_aUpdateInfo.Sources.begin(), m_aUpdateInfo.Sources.end(),
+ [](const DownloadSource& rSource) { return rSource.IsDirect; });
+
+ if( (iter != m_aUpdateInfo.Sources.begin()) &&
+ (iter != m_aUpdateInfo.Sources.end()) &&
+ iter->IsDirect )
+ {
+ m_aUpdateInfo.Sources.erase(m_aUpdateInfo.Sources.begin(), --iter);
+ }
+
+ rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *this);
+ OSL_ASSERT( rModel.is() );
+
+ // Decide whether to use alternate release note pos ..
+ bool autoDownloadEnabled = rModel->isAutoDownloadEnabled();
+
+ for (auto & elem : m_aUpdateInfo.ReleaseNotes)
+ {
+ if( ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled && !elem.URL2.isEmpty())
+ {
+ elem.URL = elem.URL2;
+ elem.URL2.clear();
+ elem.Pos = elem.Pos2;
+ elem.Pos2 = 0;
+ }
+ }
+
+ // do not move below store/clear ..
+ rModel->updateLastChecked();
+
+ UpdateState eUIState;
+ if( !m_aUpdateInfo.Sources.empty() )
+ {
+ rModel->storeUpdateFound(aInfo, getBuildId());
+
+ if( m_aUpdateInfo.Sources[0].IsDirect )
+ {
+ eUIState = UPDATESTATE_UPDATE_AVAIL;
+
+ if( rModel->isAutoDownloadEnabled() )
+ {
+ shutdownThread(false);
+ eUIState = UPDATESTATE_DOWNLOADING;
+ enableDownload(true);
+ }
+ }
+ else
+ eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ }
+ else
+ {
+ eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+ rModel->clearUpdateFound();
+ }
+
+ aGuard.unlock();
+ setUIState(eUIState, bSuppressBubble);
+}
+
+
+void
+UpdateCheck::setCheckFailedState()
+{
+ setUIState(UPDATESTATE_ERROR_CHECKING);
+}
+
+
+void UpdateCheck::handleMenuBarUI( const rtl::Reference< UpdateHandler >& rUpdateHandler,
+ UpdateState& eState,
+ bool suppressBubble )
+{
+ uno::Reference<beans::XPropertySet> xMenuBarUI( m_xMenuBarUI );
+
+ if ( ( UPDATESTATE_NO_UPDATE_AVAIL == eState ) && m_bHasExtensionUpdate )
+ eState = UPDATESTATE_EXT_UPD_AVAIL;
+
+ if ( UPDATESTATE_EXT_UPD_AVAIL == eState )
+ m_bShowExtUpdDlg = true;
+ else
+ m_bShowExtUpdDlg = false;
+
+ if( xMenuBarUI.is() )
+ {
+ if( UPDATESTATE_NO_UPDATE_AVAIL == eState )
+ {
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(false) );
+ }
+ else
+ {
+ xMenuBarUI->setPropertyValue( PROPERTY_TITLE, uno::Any(rUpdateHandler->getBubbleTitle(eState)) );
+ xMenuBarUI->setPropertyValue( PROPERTY_TEXT, uno::Any(rUpdateHandler->getBubbleText(eState)) );
+
+ if( ! suppressBubble && ( ! rUpdateHandler->isVisible() || rUpdateHandler->isMinimized() ) )
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_BUBBLE, uno::Any( true ) );
+
+ if( UPDATESTATE_CHECKING != eState )
+ xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(true) );
+ }
+ }
+}
+
+
+void UpdateCheck::setUIState(UpdateState eState, bool suppressBubble)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( ! m_xMenuBarUI.is() &&
+ (DISABLED != m_eState) &&
+ ( m_bHasExtensionUpdate || (UPDATESTATE_NO_UPDATE_AVAIL != eState)) &&
+ (UPDATESTATE_CHECKING != eState) &&
+ (UPDATESTATE_ERROR_CHECKING != eState)
+ )
+ {
+ m_xMenuBarUI = createMenuBarUI(m_xContext, new MenuBarButtonJob(this));
+ }
+
+ // Show bubble only when the status has changed
+ if ( eState == m_eUpdateState )
+ suppressBubble = true;
+ else
+ m_eUpdateState = eState;
+
+ rtl::Reference<UpdateHandler> aUpdateHandler(getUpdateHandler());
+ OSL_ASSERT( aUpdateHandler.is() );
+
+ UpdateInfo aUpdateInfo(m_aUpdateInfo);
+ OUString aImageName(m_aImageName);
+
+ aGuard.unlock();
+
+ handleMenuBarUI( aUpdateHandler, eState, suppressBubble );
+
+ if( (UPDATESTATE_UPDATE_AVAIL == eState)
+ || (UPDATESTATE_DOWNLOAD_PAUSED == eState)
+ || (UPDATESTATE_DOWNLOADING == eState) )
+ {
+ uno::Reference< uno::XComponentContext > xContext(m_xContext);
+
+ OUString aDownloadDestination =
+ UpdateCheckConfig::get(xContext, this)->getDownloadDestination();
+
+ osl_getSystemPathFromFileURL(aDownloadDestination.pData, &aDownloadDestination.pData);
+
+ aUpdateHandler->setDownloadPath(aDownloadDestination);
+ }
+ else if( UPDATESTATE_DOWNLOAD_AVAIL == eState )
+ {
+ aUpdateHandler->setDownloadFile(aImageName);
+ }
+
+ aUpdateHandler->setDescription(aUpdateInfo.Description);
+ aUpdateHandler->setNextVersion(aUpdateInfo.Version);
+ aUpdateHandler->setState(eState);
+}
+
+
+UpdateState
+UpdateCheck::getUIState(const UpdateInfo& rInfo)
+{
+ UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
+
+ if( !rInfo.BuildId.isEmpty() )
+ {
+ if( rInfo.Sources[0].IsDirect )
+ eUIState = UPDATESTATE_UPDATE_AVAIL;
+ else
+ eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
+ }
+
+ return eUIState;
+}
+
+
+void
+UpdateCheck::showReleaseNote(const OUString& rURL) const
+{
+ const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
+ c3s::SystemShellExecute::create( m_xContext ) );
+
+ try {
+ xShellExecute->execute(rURL, OUString(), c3s::SystemShellExecuteFlags::URIS_ONLY);
+ } catch(const c3s::SystemShellExecuteException&) {
+ }
+}
+
+
+bool
+UpdateCheck::storeReleaseNote(sal_Int8 nNum, const OUString &rURL)
+{
+ osl::FileBase::RC rc;
+ OUString aTargetDir( UpdateCheckConfig::getAllUsersDirectory() + "/sun" );
+
+ osl::Directory::createPath( aTargetDir );
+
+ OUString aFileName = "releasenote" +
+ OUString::number( nNum ) +
+ ".url";
+
+ OUString aFilePath;
+ rc = osl::FileBase::getAbsoluteFileURL( aTargetDir, aFileName, aFilePath );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ osl::File::remove( aFilePath );
+
+ // don't store empty release notes, but delete old ones
+ if ( rURL.isEmpty() )
+ return true;
+
+ osl::File aFile( aFilePath );
+ rc = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ OString aLineBuf("[InternetShortcut]\r\n");
+ sal_uInt64 nWritten = 0;
+
+ OUString aURL( rURL );
+#ifdef _WIN32
+ rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
+ if ( rc != osl::FileBase::E_None ) return false;
+ aURL = "URL=" + rURL;
+#endif
+ aLineBuf = OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 );
+ rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
+ if ( rc != osl::FileBase::E_None ) return false;
+
+ aFile.close();
+ return true;
+}
+
+
+void UpdateCheck::showExtensionDialog()
+{
+ uno::Reference< uno::XInterface > xService;
+
+ if( ! m_xContext.is() )
+ throw uno::RuntimeException(
+ "UpdateCheck::showExtensionDialog(): empty component context", uno::Reference< uno::XInterface > () );
+
+ uno::Reference< lang::XMultiComponentFactory > xServiceManager( m_xContext->getServiceManager() );
+ if( !xServiceManager.is() )
+ throw uno::RuntimeException(
+ "UpdateCheck::showExtensionDialog(): unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
+
+ xService = xServiceManager->createInstanceWithContext( "com.sun.star.deployment.ui.PackageManagerDialog", m_xContext );
+ uno::Reference< task::XJobExecutor > xExecutable( xService, uno::UNO_QUERY );
+ if ( xExecutable.is() )
+ xExecutable->trigger( "SHOW_UPDATE_DIALOG" );
+}
+
+
+rtl::Reference<UpdateHandler>
+UpdateCheck::getUpdateHandler()
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ if( ! m_aUpdateHandler.is() )
+ m_aUpdateHandler = new UpdateHandler(m_xContext, this);
+
+ return m_aUpdateHandler;
+}
+
+
+uno::Reference< task::XInteractionHandler >
+UpdateCheck::getInteractionHandler() const
+{
+ std::scoped_lock aGuard(m_aMutex);
+
+ uno::Reference< task::XInteractionHandler > xHandler;
+
+ if( m_aUpdateHandler.is() && m_aUpdateHandler->isVisible() )
+ xHandler = m_aUpdateHandler.get();
+
+ return xHandler;
+}
+
+
+bool
+UpdateCheck::isDialogShowing() const
+{
+ std::scoped_lock aGuard(m_aMutex);
+ return m_aUpdateHandler.is() && m_aUpdateHandler->isVisible();
+};
+
+
+void
+UpdateCheck::autoCheckStatusChanged(bool enabled)
+{
+ std::unique_lock aGuard(m_aMutex);
+
+ if( (CHECK_SCHEDULED == m_eState) && !enabled )
+ shutdownThread(false);
+
+ if( (DISABLED == m_eState) || (CHECK_SCHEDULED == m_eState) )
+ {
+ enableAutoCheck(enabled);
+ UpdateState eState = getUIState(m_aUpdateInfo);
+ aGuard.unlock();
+ setUIState(eState);
+ }
+};
+
+
+void
+UpdateCheck::autoCheckIntervalChanged()
+{
+ // just wake-up
+ m_aCondition.set();
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */