xref: /core/extensions/source/update/check/updatecheck.cxx (revision c5bd87c78866dffd053c764b2b6d7406c5625f7e)
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 <sal/config.h>
21 
22 #include <string_view>
23 
24 #include <comphelper/scopeguard.hxx>
25 #include <config_folders.h>
26 
27 #include "updatecheck.hxx"
28 
29 #include <cppuhelper/implbase.hxx>
30 #include <com/sun/star/beans/XFastPropertySet.hpp>
31 #include <com/sun/star/deployment/UpdateInformationProvider.hpp>
32 #include <com/sun/star/frame/Desktop.hpp>
33 #include <com/sun/star/office/Quickstart.hpp>
34 #include <com/sun/star/system/SystemShellExecute.hpp>
35 #include <com/sun/star/system/SystemShellExecuteException.hpp>
36 #include <com/sun/star/system/SystemShellExecuteFlags.hpp>
37 #include <com/sun/star/task/XJob.hpp>
38 #include <com/sun/star/task/XJobExecutor.hpp>
39 
40 #include <rtl/bootstrap.hxx>
41 #include <osl/process.h>
42 #include <osl/file.hxx>
43 #include <sal/macros.h>
44 #include <sal/log.hxx>
45 #include <comphelper/diagnose_ex.hxx>
46 
47 #ifdef _WIN32
48 #include <o3tl/safeCoInitUninit.hxx>
49 #include <objbase.h>
50 #endif
51 
52 #include "onlinecheck.hxx"
53 #include "updateprotocol.hxx"
54 #include "updatecheckconfig.hxx"
55 
56 namespace beans = css::beans ;
57 namespace deployment = css::deployment ;
58 namespace lang = css::lang ;
59 namespace c3s = css::system ;
60 namespace task = css::task ;
61 namespace uno = css::uno ;
62 
63 constexpr OUStringLiteral PROPERTY_TITLE = u"BubbleHeading";
64 constexpr OUStringLiteral PROPERTY_TEXT = u"BubbleText";
65 constexpr OUStringLiteral PROPERTY_SHOW_BUBBLE = u"BubbleVisible";
66 constexpr OUStringLiteral PROPERTY_CLICK_HDL = u"MenuClickHDL";
67 constexpr OUString PROPERTY_SHOW_MENUICON = u"MenuIconVisible"_ustr;
68 
69 // Returns the URL of the release note for the given position
getReleaseNote(const UpdateInfo & rInfo,sal_uInt8 pos,bool autoDownloadEnabled)70 OUString getReleaseNote(const UpdateInfo& rInfo, sal_uInt8 pos, bool autoDownloadEnabled)
71 {
72     for (auto const& elem : rInfo.ReleaseNotes)
73     {
74         if( pos == elem.Pos )
75         {
76             if( (pos > 2) || !autoDownloadEnabled || elem.URL2.isEmpty() )
77                 return elem.URL;
78         }
79         else if( (pos == elem.Pos2) && ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled )
80             return elem.URL2;
81     }
82 
83     return OUString();
84 }
85 
86 
87 namespace
88 {
89 
getBuildId()90 OUString getBuildId()
91 {
92     OUString aPathVal(u"${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}"_ustr);
93     rtl::Bootstrap::expandMacros(aPathVal);
94     return aPathVal;
95 }
96 
97 
isObsoleteUpdateInfo(std::u16string_view rBuildId)98 bool isObsoleteUpdateInfo(std::u16string_view rBuildId)
99 {
100     return rBuildId != getBuildId() && !rBuildId.empty();
101 }
102 
103 
getImageFromFileName(const OUString & aFile)104 OUString getImageFromFileName(const OUString& aFile)
105 {
106 #ifndef _WIN32
107     OUString aUnpackPath;
108     if( osl_getExecutableFile(&aUnpackPath.pData) == osl_Process_E_None )
109     {
110         sal_uInt32 lastIndex = aUnpackPath.lastIndexOf('/');
111         if ( lastIndex > 0 )
112         {
113             aUnpackPath = OUString::Concat(aUnpackPath.subView( 0, lastIndex+1 )) +
114                 "unpack_update";
115         }
116 
117         oslFileHandle hOut = nullptr;
118         oslProcess hProcess = nullptr;
119 
120         OUString aSystemPath;
121         osl::File::getSystemPathFromFileURL(aFile, aSystemPath);
122 
123         oslProcessError rc = osl_executeProcess_WithRedirectedIO(
124             aUnpackPath.pData,                                  // [in] Image name
125             &aSystemPath.pData, 1,                              // [in] Arguments
126             osl_Process_WAIT | osl_Process_NORMAL,              // [in] Options
127             nullptr,                                               // [in] Security
128             nullptr,                                               // [in] Working directory
129             nullptr, 0,                                            // [in] Environment variables
130             &hProcess,                                          // [out] Process handle
131             nullptr, &hOut, nullptr                                   // [out] File handles for redirected I/O
132         );
133 
134         if( osl_Process_E_None == rc )
135         {
136             // Create a guard to ensure correct cleanup in its dtor in any case
137             comphelper::ScopeGuard g([hOut, hProcess] () {
138                 osl_closeFile(hOut);
139                 osl_freeProcessHandle(hProcess);
140             });
141 
142             oslProcessInfo aInfo;
143             aInfo.Size = sizeof(oslProcessInfo);
144 
145             if( osl_Process_E_None == osl_getProcessInfo(hProcess, osl_Process_EXITCODE, &aInfo) )
146             {
147                 if( 0 == aInfo.Code )
148                 {
149                     char       szBuffer[4096];
150                     sal_uInt64 nBytesRead = 0;
151                     const sal_uInt64 nBytesToRead = sizeof(szBuffer) - 1;
152 
153                     OUString aImageName;
154                     while( osl_File_E_None == osl_readFile(hOut, szBuffer, nBytesToRead, &nBytesRead) )
155                     {
156                         char *pc = szBuffer + nBytesRead;
157                         do
158                         {
159                             *pc = '\0'; --pc;
160                         }
161                         while( ('\n' == *pc) || ('\r' == *pc) );
162 
163                         aImageName += OUString(szBuffer, pc - szBuffer + 1, osl_getThreadTextEncoding());
164 
165                         if( nBytesRead < nBytesToRead )
166                             break;
167                     }
168 
169                     if( osl::FileBase::E_None == osl::FileBase::getFileURLFromSystemPath(aImageName, aImageName) )
170                         return aImageName;
171                 }
172             }
173         }
174     }
175 #endif
176 
177     return aFile;
178 }
179 
180 
createMenuBarUI(const uno::Reference<uno::XComponentContext> & xContext,const uno::Reference<task::XJob> & xJob)181 uno::Reference< beans::XPropertySet > createMenuBarUI(
182     const uno::Reference< uno::XComponentContext >& xContext,
183     const uno::Reference< task::XJob >& xJob)
184 {
185     if( !xContext.is() )
186         throw uno::RuntimeException(
187             u"UpdateCheckJob: empty component context"_ustr, uno::Reference< uno::XInterface > () );
188 
189     uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
190     if( !xServiceManager.is() )
191         throw uno::RuntimeException(
192             u"UpdateCheckJob: unable to obtain service manager from component context"_ustr, uno::Reference< uno::XInterface > () );
193 
194     uno::Reference< beans::XPropertySet > xMenuBarUI(
195             xServiceManager->createInstanceWithContext( u"com.sun.star.setup.UpdateCheckUI"_ustr, xContext ),
196             uno::UNO_QUERY_THROW);
197 
198     xMenuBarUI->setPropertyValue( PROPERTY_CLICK_HDL, uno::Any( xJob ) );
199 
200     return xMenuBarUI;
201 }
202 
203 
204 typedef sal_Bool (* OnlineCheckFunc) ();
205 
206 class UpdateCheckThread : public WorkerThread
207 {
208 
209 public:
210     UpdateCheckThread( osl::Condition& rCondition,
211         const uno::Reference<uno::XComponentContext>& xContext,
212         rtl::Reference<UpdateCheck> const & controller );
213 
214     virtual void SAL_CALL join() override;
215     virtual void SAL_CALL terminate() override;
216     virtual void cancel() override;
217 
218     void cancelAsSoonAsPossible();
219 
220 protected:
221     virtual ~UpdateCheckThread() override;
222 
223     virtual void SAL_CALL run() override;
224     virtual void SAL_CALL onTerminated() override;
225 
226     /* Wrapper around checkForUpdates */
227     bool runCheck( bool & rbExtensionsChecked );
228 
229 private:
230 
231     /* Used to avoid dialup login windows (on platforms we know how to double this) */
hasInternetConnection()232     static bool hasInternetConnection()
233     {
234 #ifdef _WIN32
235         return WNT_hasInternetConnection();
236 #else
237         return true;
238 #endif
239     }
240 
241     /* Creates a new instance of UpdateInformationProvider and returns this instance */
createProvider()242     uno::Reference<deployment::XUpdateInformationProvider> createProvider()
243     {
244         osl::MutexGuard aGuard(m_aMutex);
245         m_xProvider = deployment::UpdateInformationProvider::create(m_xContext);
246         return m_xProvider;
247     };
248 
249     /* Returns the remembered instance of UpdateInformationProvider if any */
getProvider()250     uno::Reference<deployment::XUpdateInformationProvider> getProvider()
251         { osl::MutexGuard aGuard(m_aMutex); return m_xProvider; };
252 
253     /* Releases the remembered instance of UpdateInformationProvider if any */
clearProvider()254     void clearProvider()
255         { osl::MutexGuard aGuard(m_aMutex); m_xProvider.clear(); };
256 
257     osl::Mutex      m_aMutex;
258 
259 protected:
260     osl::Condition& m_aCondition;
261 
262 private:
263     const uno::Reference<uno::XComponentContext> m_xContext;
264     uno::Reference<deployment::XUpdateInformationProvider> m_xProvider;
265     rtl::Reference<UpdateCheck> m_controller;
266     bool m_cancelAsSoonAsPossible;
267 };
268 
269 
270 class ManualUpdateCheckThread : public UpdateCheckThread
271 {
272 public:
ManualUpdateCheckThread(osl::Condition & rCondition,const uno::Reference<uno::XComponentContext> & xContext)273     ManualUpdateCheckThread( osl::Condition& rCondition, const uno::Reference<uno::XComponentContext>& xContext ) :
274         UpdateCheckThread(rCondition, xContext, {}) {};
275 
276     virtual void SAL_CALL run() override;
277 };
278 
279 
280 class MenuBarButtonJob : public ::cppu::WeakImplHelper< task::XJob >
281 {
282 public:
283     explicit MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck);
284 
285     // XJob
286     virtual uno::Any SAL_CALL execute(const uno::Sequence<beans::NamedValue>&) override;
287 
288 private:
289     rtl::Reference< UpdateCheck > m_aUpdateCheck;
290 };
291 
292 class DownloadThread :  public WorkerThread
293 {
294 public:
295     DownloadThread(
296         osl::Condition& rCondition,
297         const uno::Reference<uno::XComponentContext>& xContext,
298         const rtl::Reference< DownloadInteractionHandler >& rHandler,
299         const OUString& rURL );
300 
301     virtual void SAL_CALL run() override;
302     virtual void cancel() override;
303     virtual void SAL_CALL suspend() override;
304     virtual void SAL_CALL onTerminated() override;
305 
306 protected:
307     virtual ~DownloadThread() override;
308 
309 private:
310     osl::Condition& m_aCondition;
311     const uno::Reference<uno::XComponentContext> m_xContext;
312     const OUString m_aURL;
313     Download m_aDownload;
314 };
315 
316 
317 
UpdateCheckThread(osl::Condition & rCondition,const uno::Reference<uno::XComponentContext> & xContext,rtl::Reference<UpdateCheck> const & controller)318 UpdateCheckThread::UpdateCheckThread( osl::Condition& rCondition,
319                                       const uno::Reference<uno::XComponentContext>& xContext,
320                                       rtl::Reference<UpdateCheck> const & controller ) :
321     m_aCondition(rCondition),
322     m_xContext(xContext),
323     m_controller(controller),
324     m_cancelAsSoonAsPossible(false)
325 {
326     createSuspended();
327 
328     // actually run the thread
329     resume();
330 }
331 
332 
~UpdateCheckThread()333 UpdateCheckThread::~UpdateCheckThread()
334 {
335 }
336 
337 
338 void SAL_CALL
terminate()339 UpdateCheckThread::terminate()
340 {
341     // Cancel potentially hanging http request ..
342     cancel();
343     // .. before terminating
344     osl::Thread::terminate();
345 }
346 
347 
348 void SAL_CALL
join()349 UpdateCheckThread::join()
350 {
351     uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
352 
353     // do not join during an update check until #i73893# is fixed
354     if( ! xProvider.is() )
355     {
356         osl::Thread::join();
357     }
358 }
359 
360 
361 void
cancel()362 UpdateCheckThread::cancel()
363 {
364     uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
365 
366     if( xProvider.is() )
367         xProvider->cancel();
368 }
369 
cancelAsSoonAsPossible()370 void UpdateCheckThread::cancelAsSoonAsPossible() {
371     {
372         osl::MutexGuard g(m_aMutex);
373         m_cancelAsSoonAsPossible = true;
374     }
375     m_aCondition.set();
376 }
377 
378 bool
runCheck(bool & rbExtensionsChecked)379 UpdateCheckThread::runCheck( bool & rbExtensionsChecked )
380 {
381     bool ret = false;
382     UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
383 
384     UpdateInfo aInfo;
385     rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
386 
387     if( checkForUpdates(aInfo, m_xContext, aController->getInteractionHandler(), createProvider()) )
388     {
389         aController->setUpdateInfo(aInfo);
390         eUIState = UpdateCheck::getUIState(aInfo);
391         ret = true;
392     }
393     else
394         aController->setCheckFailedState();
395 
396     // We will only look for extension updates, when there is no 'check for office updates' dialog open
397     // and when there was no office update found
398     if ( ( eUIState != UPDATESTATE_UPDATE_AVAIL ) &&
399          ( eUIState != UPDATESTATE_UPDATE_NO_DOWNLOAD ) &&
400          !aController->isDialogShowing() &&
401          !rbExtensionsChecked )
402     {
403         bool bHasExtensionUpdates = checkForExtensionUpdates( m_xContext );
404         aController->setHasExtensionUpdates( bHasExtensionUpdates );
405         if ( bHasExtensionUpdates )
406             aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL );
407         rbExtensionsChecked = true;
408     }
409 
410     // joining with this thread is safe again
411     clearProvider();
412     return ret;
413 }
414 
415 
416 void SAL_CALL
onTerminated()417 UpdateCheckThread::onTerminated()
418 {
419     delete this;
420 }
421 
422 
423 void SAL_CALL
run()424 UpdateCheckThread::run()
425 {
426     osl_setThreadName("UpdateCheckThread");
427 
428     TimeValue systime;
429     TimeValue nExtCheckTime;
430     osl_getSystemTime( &nExtCheckTime );
431 
432     osl::Condition::Result aResult = osl::Condition::result_timeout;
433     TimeValue tv = { 10, 0 };
434 
435     // Initial wait to avoid doing further time consuming tasks during start-up
436     aResult = m_aCondition.wait(&tv);
437     {
438         osl::MutexGuard g(m_aMutex);
439         if (m_cancelAsSoonAsPossible) {
440             goto done;
441         }
442     }
443 
444     try {
445         bool bExtensionsChecked = false;
446 
447         while( schedule() )
448         {
449             /* Use cases:
450              *  a) manual check requested from auto check thread - "last check" should not be checked (one time)
451              *     a1) manual check was requested in the middle of a running auto check,
452              *         condition is set
453              *     a2) manual check was requested while waiting for a retry,
454              *         condition is set
455              *     a3) manual check was requested while waiting for time to next
456              *         scheduled check elapsing, condition is set
457              *     a4) manual check was requested during initial wait, condition is set
458              *  b) check interval got changed, condition may be set - same sub-cases as a),
459              *     but "last check" should be honored
460              *  c) normal auto check mode, condition not set - "last check" should be honored
461              */
462 
463             // Accessing const members without synchronization
464             rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
465             rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *aController);
466 
467             // FIXME: remember last & offset ?
468             sal_Int64 last   = rModel->getLastChecked();
469             sal_Int64 offset = rModel->getCheckInterval();
470 
471             rModel.clear();
472 
473             // last == 0 means check immediately
474             bool checkNow = last <= 0;
475 
476             // Reset the condition to avoid busy loops
477             if( osl::Condition::result_ok == aResult )
478             {
479                 m_aCondition.reset();
480                 aResult = osl::Condition::result_timeout;
481                 checkNow = aController->isDialogShowing();
482             }
483 
484             if( ! checkNow )
485             {
486                 osl_getSystemTime(&systime);
487 
488                 // Go back to sleep until time has elapsed
489                 sal_Int64 next = last + offset;
490                 if( last + offset > systime.Seconds )
491                 {
492                     // This can not be > 32 Bit for now ..
493                     tv.Seconds = static_cast< sal_Int32 > (next - systime.Seconds);
494                     aResult = m_aCondition.wait(&tv);
495                     {
496                         osl::MutexGuard g(m_aMutex);
497                         if (m_cancelAsSoonAsPossible) {
498                             goto done;
499                         }
500                     }
501                     continue;
502                 }
503             }
504 
505             static sal_uInt8 n = 0;
506 
507             if( ! hasInternetConnection() || ! runCheck( bExtensionsChecked ) )
508             {
509                 // the extension update check should be independent from the office update check
510 
511                 osl_getSystemTime( &systime );
512                 if ( nExtCheckTime.Seconds + offset < systime.Seconds )
513                     bExtensionsChecked = false;
514 
515                 // Increase next by 15, 60, .. minutes
516                 static const sal_Int32 nRetryInterval[] = { 900, 3600, 14400, 86400 };
517 
518                 if( n < std::size(nRetryInterval) )
519                     ++n;
520 
521                 tv.Seconds = nRetryInterval[n-1];
522                 aResult = m_aCondition.wait(&tv);
523                 {
524                     osl::MutexGuard g(m_aMutex);
525                     if (m_cancelAsSoonAsPossible) {
526                         goto done;
527                     }
528                 }
529             }
530             else // reset retry counter
531             {
532                 n = 0;
533                 bExtensionsChecked = false;
534             }
535         }
536     }
537 
538     catch(const uno::Exception&) {
539         // Silently catch all errors
540         TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
541     }
542 
543 done:
544     if (m_controller.is()) {
545         m_controller->notifyUpdateCheckFinished();
546     }
547 }
548 
549 
550 void SAL_CALL
run()551 ManualUpdateCheckThread::run()
552 {
553     try {
554         bool bExtensionsChecked = false;
555         runCheck( bExtensionsChecked );
556         m_aCondition.reset();
557     }
558     catch(const uno::Exception&) {
559         // Silently catch all errors
560         TOOLS_WARN_EXCEPTION("extensions.update", "Caught exception, thread terminated" );
561     }
562 }
563 
564 
MenuBarButtonJob(const rtl::Reference<UpdateCheck> & rUpdateCheck)565 MenuBarButtonJob::MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck) :
566     m_aUpdateCheck(rUpdateCheck)
567 {
568 };
569 
570 
571 uno::Any SAL_CALL
execute(const uno::Sequence<beans::NamedValue> &)572 MenuBarButtonJob::execute(const uno::Sequence<beans::NamedValue>& )
573 {
574     if ( m_aUpdateCheck->shouldShowExtUpdDlg() )
575         m_aUpdateCheck->showExtensionDialog();
576     else
577         m_aUpdateCheck->showDialog();
578 
579     return uno::Any();
580 }
581 
582 
DownloadThread(osl::Condition & rCondition,const uno::Reference<uno::XComponentContext> & xContext,const rtl::Reference<DownloadInteractionHandler> & rHandler,const OUString & rURL)583 DownloadThread::DownloadThread(osl::Condition& rCondition,
584                                const uno::Reference<uno::XComponentContext>& xContext,
585                                const rtl::Reference< DownloadInteractionHandler >& rHandler,
586                                const OUString& rURL) :
587     m_aCondition(rCondition),
588     m_xContext(xContext),
589     m_aURL(rURL),
590     m_aDownload(xContext, rHandler)
591 {
592     createSuspended();
593 }
594 
595 
~DownloadThread()596 DownloadThread::~DownloadThread()
597 {
598 }
599 
600 
601 void SAL_CALL
run()602 DownloadThread::run()
603 {
604     osl_setThreadName("DownloadThread");
605 
606 #ifdef _WIN32
607     int nNbCallCoInitializeExForReinit = 0;
608     // for SystemShellExecute
609     o3tl::safeCoInitializeEx(COINIT_APARTMENTTHREADED, nNbCallCoInitializeExForReinit);
610 #endif
611 
612     while( schedule() )
613     {
614         rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
615 
616         OUString aLocalFile = rModel->getLocalFileName();
617         OUString aDownloadDest = rModel->getDownloadDestination();
618 
619         // release config class for now
620         rModel.clear();
621 
622         static sal_uInt8 n = 0;
623         if( ! m_aDownload.start(m_aURL, aLocalFile, aDownloadDest ) )
624         {
625             // retry every 15s unless the dialog is not visible
626             TimeValue tv(15, 0);
627 
628             if( ! UpdateCheck::get()->isDialogShowing() )
629             {
630                 // Increase next by 1, 5, 15, 60, .. minutes
631                 static const sal_Int16 nRetryInterval[] = { 60, 300, 900, 3600 };
632 
633                 if( n < std::size(nRetryInterval) )
634                     ++n;
635 
636                 tv.Seconds = nRetryInterval[n-1];
637             }
638             m_aCondition.wait(&tv);
639         }
640         else
641         {
642             // reset wait period after successful download
643             n=0;
644         }
645     }
646 #ifdef _WIN32
647     o3tl::safeCoUninitializeReinit(COINIT_MULTITHREADED, nNbCallCoInitializeExForReinit);
648 #endif
649 }
650 
651 
cancel()652 void DownloadThread::cancel()
653 {
654     m_aDownload.stop();
655     resume();
656 
657     rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
658     aController->cancelDownload();
659 }
660 
661 
suspend()662 void SAL_CALL DownloadThread::suspend()
663 {
664     osl::Thread::suspend();
665     m_aDownload.stop();
666 }
667 
668 
onTerminated()669 void SAL_CALL DownloadThread::onTerminated()
670 {
671     delete this;
672 }
673 
674 
675 } // anonymous namespace
676 
UpdateCheck()677 UpdateCheck::UpdateCheck()
678     : m_eState(NOT_INITIALIZED)
679     , m_eUpdateState(UPDATESTATES_COUNT)
680     , m_pThread(nullptr)
681     , m_bHasExtensionUpdate(false)
682     , m_bShowExtUpdDlg(false)
683     , m_updateCheckRunning(false)
684 {
685 }
686 
~UpdateCheck()687 UpdateCheck::~UpdateCheck() {}
688 
689 void
initialize(const uno::Sequence<beans::NamedValue> & rValues,const uno::Reference<uno::XComponentContext> & xContext)690 UpdateCheck::initialize(const uno::Sequence< beans::NamedValue >& rValues,
691                         const uno::Reference<uno::XComponentContext>& xContext)
692 {
693     std::scoped_lock aGuard(m_aMutex);
694 
695     if( NOT_INITIALIZED == m_eState )
696     {
697         NamedValueByNameAccess aNameAccess(rValues);
698         UpdateCheckROModel aModel( aNameAccess );
699         m_xContext = xContext;
700 
701         OUString aUpdateEntryVersion = aModel.getUpdateEntryVersion();
702 
703         aModel.getUpdateEntry(m_aUpdateInfo);
704 
705         bool obsoleteUpdateInfo = isObsoleteUpdateInfo(aUpdateEntryVersion);
706         bool bContinueDownload = false;
707         bool bDownloadAvailable = false;
708 
709         m_bHasExtensionUpdate = checkForPendingUpdates( xContext );
710         m_bShowExtUpdDlg = false;
711 
712         OUString aLocalFileName = aModel.getLocalFileName();
713 
714         if( !aLocalFileName.isEmpty() )
715         {
716             bContinueDownload = true;
717 
718             // Try to get the number of bytes already on disk
719             osl::DirectoryItem aDirectoryItem;
720             if( osl::DirectoryItem::E_None == osl::DirectoryItem::get(aLocalFileName, aDirectoryItem) )
721             {
722                 osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileSize);
723                 if( osl::DirectoryItem::E_None == aDirectoryItem.getFileStatus(aFileStatus) )
724                 {
725                     sal_Int64 nDownloadSize = aModel.getDownloadSize();
726                     sal_Int64 nFileSize = aFileStatus.getFileSize();
727 
728                     if( nDownloadSize > 0 )
729                     {
730                         if ( nDownloadSize <= nFileSize ) // we have already downloaded everything
731                         {
732                             bContinueDownload = false;
733                             bDownloadAvailable = true;
734                             m_aImageName = getImageFromFileName( aLocalFileName );
735                         }
736                         else // Calculate initial percent value.
737                         {
738                             sal_Int32 nPercent = static_cast<sal_Int32>(100 * nFileSize / nDownloadSize);
739                             getUpdateHandler()->setProgress( nPercent );
740                         }
741                     }
742                 }
743             }
744 
745             if ( bContinueDownload )
746             {
747                 bool downloadPaused = aModel.isDownloadPaused();
748 
749                 enableDownload(true, downloadPaused);
750                 setUIState(downloadPaused ? UPDATESTATE_DOWNLOAD_PAUSED : UPDATESTATE_DOWNLOADING);
751             }
752 
753         }
754         if ( !bContinueDownload )
755         {
756             // We do this intentionally only if no download is in progress ..
757             if( obsoleteUpdateInfo )
758             {
759                 // Bring-up release note for position 5 ..
760                 const OUString aURL(getReleaseNote(m_aUpdateInfo, 5));
761                 if( !aURL.isEmpty() )
762                     showReleaseNote(aURL);
763 
764                 // Data is outdated, probably due to installed update
765                 rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( xContext, *this );
766                 aConfig->clearUpdateFound();
767                 aConfig->clearLocalFileName();
768 
769 
770                 m_aUpdateInfo = UpdateInfo();
771                 // Remove outdated release notes
772                 storeReleaseNote( 1, OUString() );
773                 storeReleaseNote( 2, OUString() );
774             }
775             else
776             {
777                 enableAutoCheck(aModel.isAutoCheckEnabled());
778                 if ( bDownloadAvailable )
779                     setUIState( UPDATESTATE_DOWNLOAD_AVAIL );
780                 else
781                 {
782                     setUIState(getUIState(m_aUpdateInfo));
783                 }
784             }
785         }
786     }
787 }
788 
789 
790 void
cancel()791 UpdateCheck::cancel()
792 {
793     std::unique_lock aGuard(m_aMutex);
794 
795     WorkerThread *pThread = m_pThread;
796     UpdateState eUIState = getUIState(m_aUpdateInfo);
797 
798     aGuard.unlock();
799 
800     if( nullptr != pThread )
801         pThread->cancel();
802 
803     setUIState(eUIState);
804 }
805 
806 
807 void
download()808 UpdateCheck::download()
809 {
810     std::unique_lock aGuard(m_aMutex);
811     UpdateInfo aInfo(m_aUpdateInfo);
812     State eState = m_eState;
813     aGuard.unlock();
814 
815     if (aInfo.Sources.empty())
816     {
817         SAL_WARN("extensions.update", "download called without source");
818         return;
819     }
820 
821     if( aInfo.Sources[0].IsDirect )
822     {
823         // Ignore second click of a double click
824         if( DOWNLOADING != eState )
825         {
826             shutdownThread(true);
827 
828             {
829                 std::scoped_lock aGuard2(m_aMutex);
830                 enableDownload(true);
831             }
832             setUIState(UPDATESTATE_DOWNLOADING);
833         }
834     }
835     else
836     {
837         showReleaseNote(aInfo.Sources[0].URL); // Display in browser
838     }
839 }
840 
841 
842 void
pause()843 UpdateCheck::pause()
844 {
845     std::unique_lock aGuard(m_aMutex);
846 
847     if( nullptr != m_pThread )
848         m_pThread->suspend();
849 
850     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
851     aGuard.unlock();
852 
853     rModel->storeDownloadPaused(true);
854     setUIState(UPDATESTATE_DOWNLOAD_PAUSED);
855 }
856 
857 
858 void
resume()859 UpdateCheck::resume()
860 {
861     std::unique_lock aGuard(m_aMutex);
862 
863     if( nullptr != m_pThread )
864         m_pThread->resume();
865 
866     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
867     aGuard.unlock();
868 
869     rModel->storeDownloadPaused(false);
870     setUIState(UPDATESTATE_DOWNLOADING);
871 }
872 
873 
874 void
closeAfterFailure()875 UpdateCheck::closeAfterFailure()
876 {
877     std::unique_lock aGuard(m_aMutex);
878 
879     if ( ( m_eState == DISABLED ) || ( m_eState == CHECK_SCHEDULED ) )
880     {
881         const UpdateState eUIState = getUIState( m_aUpdateInfo );
882         aGuard.unlock();
883         setUIState( eUIState, true );
884     }
885 }
886 
notifyUpdateCheckFinished()887 void UpdateCheck::notifyUpdateCheckFinished() {
888     std::scoped_lock l(m_aMutex);
889     m_updateCheckRunning = false;
890     m_updateCheckFinished.notify_all();
891 }
892 
waitForUpdateCheckFinished()893 void UpdateCheck::waitForUpdateCheckFinished() {
894     UpdateCheckThread * thread;
895     {
896         std::scoped_lock l(m_aMutex);
897         thread = dynamic_cast<UpdateCheckThread *>(m_pThread);
898     }
899     if (thread != nullptr) {
900         thread->cancelAsSoonAsPossible();
901     }
902     for (;;) {
903         std::unique_lock lock(m_aMutex);
904         if (!m_updateCheckRunning) {
905             return;
906         }
907         m_updateCheckFinished.wait(lock);
908     }
909 }
910 
911 void
shutdownThread(bool join)912 UpdateCheck::shutdownThread(bool join)
913 {
914     std::unique_lock aGuard(m_aMutex);
915 
916     // copy thread object pointer to stack
917     osl::Thread *pThread = m_pThread;
918     m_pThread = nullptr;
919     aGuard.unlock();
920 
921     if( nullptr != pThread )
922     {
923         pThread->terminate();
924         if( join )
925         {
926             m_aCondition.set();
927             pThread->join();
928             m_aCondition.reset();
929         }
930     }
931 }
932 
933 
934 void
enableAutoCheck(bool enable)935 UpdateCheck::enableAutoCheck(bool enable)
936 {
937     if( enable )
938     {
939         m_updateCheckRunning = true;
940         m_pThread = new UpdateCheckThread(m_aCondition, m_xContext, this);
941     }
942 
943     m_eState = enable ? CHECK_SCHEDULED : DISABLED;
944 }
945 
946 
947 void
enableDownload(bool enable,bool paused)948 UpdateCheck::enableDownload(bool enable, bool paused)
949 {
950     OSL_ASSERT(nullptr == m_pThread);
951 
952     if( enable )
953     {
954         m_pThread = new DownloadThread(m_aCondition, m_xContext, this, m_aUpdateInfo.Sources[0].URL );
955         State eState = DISABLED;
956         if( !paused )
957         {
958             eState = DOWNLOADING;
959             m_pThread->resume();
960         }
961         else
962             eState = DOWNLOAD_PAUSED;
963 
964         m_eState = eState;
965     }
966     else {
967         enableAutoCheck(UpdateCheckConfig::get(m_xContext)->isAutoCheckEnabled());
968     }
969 
970 }
971 
972 
973 bool
downloadTargetExists(const OUString & rFileName)974 UpdateCheck::downloadTargetExists(const OUString& rFileName)
975 {
976     std::unique_lock aGuard(m_aMutex);
977 
978     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
979     UpdateState eUIState = UPDATESTATE_DOWNLOADING;
980 
981     bool cont = false;
982 
983     if( aUpdateHandler->isVisible() )
984     {
985         cont = aUpdateHandler->showOverwriteWarning();
986         if( cont )
987         {
988             if( osl_File_E_None != osl_removeFile(rFileName.pData) )
989             {
990                 // FIXME: error message
991                 cont = false;
992             }
993         }
994         else
995             eUIState = getUIState(m_aUpdateInfo);
996     }
997     else
998     {
999         m_aImageName = getImageFromFileName(rFileName);
1000         eUIState = UPDATESTATE_DOWNLOAD_AVAIL;
1001     }
1002 
1003     if( !cont )
1004     {
1005         shutdownThread(false);
1006         enableDownload(false);
1007 
1008         aGuard.unlock();
1009         setUIState(eUIState);
1010     }
1011 
1012     return cont;
1013 }
1014 
1015 
checkDownloadDestination(const OUString & rFileName)1016 bool UpdateCheck::checkDownloadDestination( const OUString& rFileName )
1017 {
1018     std::scoped_lock aGuard(m_aMutex);
1019 
1020     rtl::Reference< UpdateHandler > aUpdateHandler( getUpdateHandler() );
1021 
1022     bool bReload = false;
1023 
1024     if( aUpdateHandler->isVisible() )
1025     {
1026         bReload = aUpdateHandler->showOverwriteWarning( rFileName );
1027     }
1028 
1029     return bReload;
1030 }
1031 
1032 
1033 void
downloadStalled(const OUString & rErrorMessage)1034 UpdateCheck::downloadStalled(const OUString& rErrorMessage)
1035 {
1036     std::unique_lock aGuard(m_aMutex);
1037     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
1038     aGuard.unlock();
1039 
1040     aUpdateHandler->setErrorMessage(rErrorMessage);
1041     setUIState(UPDATESTATE_ERROR_DOWNLOADING);
1042 }
1043 
1044 
1045 void
downloadProgressAt(sal_Int8 nPercent)1046 UpdateCheck::downloadProgressAt(sal_Int8 nPercent)
1047 {
1048     std::unique_lock aGuard(m_aMutex);
1049     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
1050     aGuard.unlock();
1051 
1052     aUpdateHandler->setProgress(nPercent);
1053     setUIState(UPDATESTATE_DOWNLOADING);
1054 }
1055 
1056 
1057 void
downloadStarted(const OUString & rLocalFileName,sal_Int64 nFileSize)1058 UpdateCheck::downloadStarted(const OUString& rLocalFileName, sal_Int64 nFileSize)
1059 {
1060     if ( nFileSize > 0 )
1061     {
1062         std::scoped_lock aGuard(m_aMutex);
1063 
1064         rtl::Reference< UpdateCheckConfig > aModel(UpdateCheckConfig::get(m_xContext));
1065         aModel->storeLocalFileName(rLocalFileName, nFileSize);
1066 
1067         // Bring-up release note for position 1 ..
1068         const OUString aURL(getReleaseNote(m_aUpdateInfo, 1, aModel->isAutoDownloadEnabled()));
1069         if( !aURL.isEmpty() )
1070             showReleaseNote(aURL);
1071     }
1072 }
1073 
1074 
1075 void
downloadFinished(const OUString & rLocalFileName)1076 UpdateCheck::downloadFinished(const OUString& rLocalFileName)
1077 {
1078     std::unique_lock aGuard(m_aMutex);
1079 
1080     // no more retries
1081     m_pThread->terminate();
1082 
1083     m_aImageName = getImageFromFileName(rLocalFileName);
1084     UpdateInfo aUpdateInfo(m_aUpdateInfo);
1085 
1086     aGuard.unlock();
1087     setUIState(UPDATESTATE_DOWNLOAD_AVAIL);
1088 
1089     // Bring-up release note for position 2 ..
1090     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
1091     const OUString aURL(getReleaseNote(aUpdateInfo, 2, rModel->isAutoDownloadEnabled()));
1092     if( !aURL.isEmpty() )
1093         showReleaseNote(aURL);
1094 }
1095 
1096 
1097 void
cancelDownload()1098 UpdateCheck::cancelDownload()
1099 {
1100     shutdownThread(true);
1101 
1102     std::scoped_lock aGuard(m_aMutex);
1103     enableDownload(false);
1104 
1105     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
1106 
1107     OUString aLocalFile(rModel->getLocalFileName());
1108     rModel->clearLocalFileName();
1109     rModel->storeDownloadPaused(false);
1110 
1111     if( isObsoleteUpdateInfo(rModel->getUpdateEntryVersion()) )
1112     {
1113         rModel->clearUpdateFound(); // This wasn't done during init yet ..
1114         m_aUpdateInfo = UpdateInfo();
1115     }
1116 
1117     /*oslFileError rc =*/ osl_removeFile(aLocalFile.pData);
1118     // FIXME: error handling ..
1119 
1120 }
1121 
1122 
1123 void
showDialog(bool forceCheck)1124 UpdateCheck::showDialog(bool forceCheck)
1125 {
1126     std::unique_lock aGuard(m_aMutex);
1127 
1128     bool update_found = !m_aUpdateInfo.BuildId.isEmpty();
1129     bool bSetUIState = ! m_aUpdateHandler.is();
1130 
1131     UpdateState eDialogState = UPDATESTATES_COUNT;
1132 
1133     switch( m_eState )
1134     {
1135     case DISABLED:
1136     case CHECK_SCHEDULED:
1137         if( forceCheck || ! update_found ) // Run check when forced or if we did not find an update yet
1138         {
1139             eDialogState = UPDATESTATE_CHECKING;
1140             bSetUIState = true;
1141         }
1142         else if(m_aUpdateInfo.Sources[0].IsDirect)
1143             eDialogState = UPDATESTATE_UPDATE_AVAIL;
1144         else
1145             eDialogState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1146         break;
1147 
1148     case DOWNLOADING:
1149         eDialogState = UPDATESTATE_DOWNLOADING;
1150         break;
1151 
1152     case DOWNLOAD_PAUSED:
1153         eDialogState = UPDATESTATE_DOWNLOAD_PAUSED;
1154         break;
1155 
1156     case NOT_INITIALIZED:
1157         OSL_ASSERT( false );
1158         break;
1159     }
1160 
1161     if( bSetUIState )
1162     {
1163         aGuard.unlock();
1164         setUIState(eDialogState, true); // suppress bubble as Dialog will be visible soon
1165         aGuard.lock();
1166     }
1167 
1168     getUpdateHandler()->setVisible();
1169 
1170     // Run check in separate thread ..
1171     if( UPDATESTATE_CHECKING == eDialogState )
1172     {
1173         if( DISABLED == m_eState )
1174         {
1175             // destructs itself when done, not cancellable for now ..
1176             new ManualUpdateCheckThread(m_aCondition, m_xContext);
1177         }
1178 
1179         m_aCondition.set();
1180     }
1181 }
1182 
1183 void
setUpdateInfo(const UpdateInfo & aInfo)1184 UpdateCheck::setUpdateInfo(const UpdateInfo& aInfo)
1185 {
1186     std::unique_lock aGuard(m_aMutex);
1187 
1188     bool bSuppressBubble = aInfo.BuildId == m_aUpdateInfo.BuildId;
1189     m_aUpdateInfo = aInfo;
1190 
1191     OSL_ASSERT(DISABLED == m_eState || CHECK_SCHEDULED == m_eState);
1192 
1193     // Ignore leading non direct download if we get direct ones
1194     std::vector< DownloadSource >::iterator iter = std::find_if(m_aUpdateInfo.Sources.begin(), m_aUpdateInfo.Sources.end(),
1195         [](const DownloadSource& rSource) { return rSource.IsDirect; });
1196 
1197     if( (iter != m_aUpdateInfo.Sources.begin()) &&
1198         (iter != m_aUpdateInfo.Sources.end()) &&
1199         iter->IsDirect )
1200     {
1201         m_aUpdateInfo.Sources.erase(m_aUpdateInfo.Sources.begin(), --iter);
1202     }
1203 
1204     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *this);
1205     OSL_ASSERT( rModel.is() );
1206 
1207     // Decide whether to use alternate release note pos ..
1208     bool autoDownloadEnabled = rModel->isAutoDownloadEnabled();
1209 
1210     for (auto & elem : m_aUpdateInfo.ReleaseNotes)
1211     {
1212         if( ((1 == elem.Pos) || (2 == elem.Pos)) && autoDownloadEnabled && !elem.URL2.isEmpty())
1213         {
1214             elem.URL = elem.URL2;
1215             elem.URL2.clear();
1216             elem.Pos = elem.Pos2;
1217             elem.Pos2 = 0;
1218         }
1219     }
1220 
1221     // do not move below store/clear ..
1222     rModel->updateLastChecked();
1223 
1224     UpdateState eUIState;
1225     if( !m_aUpdateInfo.Sources.empty() )
1226     {
1227         rModel->storeUpdateFound(aInfo, getBuildId());
1228 
1229         if( m_aUpdateInfo.Sources[0].IsDirect )
1230         {
1231             eUIState = UPDATESTATE_UPDATE_AVAIL;
1232 
1233             if( rModel->isAutoDownloadEnabled() )
1234             {
1235                 shutdownThread(false);
1236                 eUIState = UPDATESTATE_DOWNLOADING;
1237                 enableDownload(true);
1238             }
1239         }
1240         else
1241             eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1242     }
1243     else
1244     {
1245         eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
1246         rModel->clearUpdateFound();
1247     }
1248 
1249     aGuard.unlock();
1250     setUIState(eUIState, bSuppressBubble);
1251 }
1252 
hasOfficeUpdate() const1253 bool UpdateCheck::hasOfficeUpdate() const
1254 {
1255     std::unique_lock aGuard(m_aMutex);
1256     return m_aUpdateInfo.BuildId.getLength() > 0;
1257 }
1258 
1259 void
setCheckFailedState()1260 UpdateCheck::setCheckFailedState()
1261 {
1262     setUIState(UPDATESTATE_ERROR_CHECKING);
1263 }
1264 
1265 
handleMenuBarUI(const rtl::Reference<UpdateHandler> & rUpdateHandler,const uno::Reference<beans::XPropertySet> & xMenuBarUI,UpdateState & eState,bool suppressBubble)1266 void UpdateCheck::handleMenuBarUI( const rtl::Reference< UpdateHandler >& rUpdateHandler,
1267                                    const uno::Reference<beans::XPropertySet>& xMenuBarUI,
1268                                    UpdateState& eState,
1269                                    bool suppressBubble )
1270 {
1271     if ( ( UPDATESTATE_NO_UPDATE_AVAIL == eState ) && m_bHasExtensionUpdate )
1272         eState = UPDATESTATE_EXT_UPD_AVAIL;
1273 
1274     if ( UPDATESTATE_EXT_UPD_AVAIL == eState )
1275         m_bShowExtUpdDlg = true;
1276     else
1277         m_bShowExtUpdDlg = false;
1278 
1279     if( xMenuBarUI.is() )
1280     {
1281         if( UPDATESTATE_NO_UPDATE_AVAIL == eState )
1282         {
1283             xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(false) );
1284         }
1285         else
1286         {
1287             xMenuBarUI->setPropertyValue( PROPERTY_TITLE, uno::Any(rUpdateHandler->getBubbleTitle(eState)) );
1288             xMenuBarUI->setPropertyValue( PROPERTY_TEXT, uno::Any(rUpdateHandler->getBubbleText(eState)) );
1289 
1290             if( ! suppressBubble && ( ! rUpdateHandler->isVisible() || rUpdateHandler->isMinimized() ) )
1291                 xMenuBarUI->setPropertyValue( PROPERTY_SHOW_BUBBLE, uno::Any( true ) );
1292 
1293             if( UPDATESTATE_CHECKING != eState )
1294                 xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::Any(true) );
1295         }
1296     }
1297 }
1298 
1299 
setUIState(UpdateState eState,bool suppressBubble)1300 void UpdateCheck::setUIState(UpdateState eState, bool suppressBubble)
1301 {
1302     std::unique_lock aGuard(m_aMutex);
1303 
1304     if( ! m_xMenuBarUI.is() &&
1305         (DISABLED != m_eState) &&
1306         ( m_bHasExtensionUpdate || (UPDATESTATE_NO_UPDATE_AVAIL != eState)) &&
1307         (UPDATESTATE_CHECKING != eState) &&
1308         (UPDATESTATE_ERROR_CHECKING != eState)
1309     )
1310     {
1311         m_xMenuBarUI = createMenuBarUI(m_xContext, new MenuBarButtonJob(this));
1312     }
1313 
1314     // Show bubble only when the status has changed
1315     if ( eState == m_eUpdateState )
1316         suppressBubble = true;
1317     else
1318         m_eUpdateState = eState;
1319 
1320     rtl::Reference<UpdateHandler> aUpdateHandler(getUpdateHandler());
1321     OSL_ASSERT( aUpdateHandler.is() );
1322 
1323     UpdateInfo aUpdateInfo(m_aUpdateInfo);
1324     OUString aImageName(m_aImageName);
1325     uno::Reference<beans::XPropertySet> xMenuBarUI(m_xMenuBarUI);
1326 
1327     aGuard.unlock();
1328 
1329     handleMenuBarUI(aUpdateHandler, xMenuBarUI, eState, suppressBubble);
1330 
1331     if( (UPDATESTATE_UPDATE_AVAIL == eState)
1332      || (UPDATESTATE_DOWNLOAD_PAUSED == eState)
1333      || (UPDATESTATE_DOWNLOADING == eState) )
1334     {
1335         uno::Reference< uno::XComponentContext > xContext(m_xContext);
1336 
1337         OUString aDownloadDestination =
1338             UpdateCheckConfig::get(xContext, this)->getDownloadDestination();
1339 
1340         osl_getSystemPathFromFileURL(aDownloadDestination.pData, &aDownloadDestination.pData);
1341 
1342         aUpdateHandler->setDownloadPath(aDownloadDestination);
1343     }
1344     else if( UPDATESTATE_DOWNLOAD_AVAIL == eState )
1345     {
1346         aUpdateHandler->setDownloadFile(aImageName);
1347     }
1348 
1349     aUpdateHandler->setDescription(aUpdateInfo.Description);
1350     aUpdateHandler->setNextVersion(aUpdateInfo.Version);
1351     aUpdateHandler->setState(eState);
1352 }
1353 
1354 
1355 UpdateState
getUIState(const UpdateInfo & rInfo)1356 UpdateCheck::getUIState(const UpdateInfo& rInfo)
1357 {
1358     UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
1359 
1360     if( !rInfo.BuildId.isEmpty() )
1361     {
1362         if( rInfo.Sources[0].IsDirect )
1363             eUIState = UPDATESTATE_UPDATE_AVAIL;
1364         else
1365             eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1366     }
1367 
1368     return eUIState;
1369 }
1370 
1371 
1372 void
showReleaseNote(const OUString & rURL) const1373 UpdateCheck::showReleaseNote(const OUString& rURL) const
1374 {
1375     const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
1376         c3s::SystemShellExecute::create( m_xContext ) );
1377 
1378     try {
1379         xShellExecute->execute(rURL, OUString(), c3s::SystemShellExecuteFlags::URIS_ONLY);
1380     } catch(const c3s::SystemShellExecuteException&) {
1381     }
1382 }
1383 
1384 
1385 bool
storeReleaseNote(sal_Int8 nNum,const OUString & rURL)1386 UpdateCheck::storeReleaseNote(sal_Int8 nNum, const OUString &rURL)
1387 {
1388     osl::FileBase::RC rc;
1389     OUString aTargetDir( UpdateCheckConfig::getAllUsersDirectory() + "/sun" );
1390 
1391     osl::Directory::createPath( aTargetDir );
1392 
1393     OUString aFileName = "releasenote" +
1394                               OUString::number( nNum ) +
1395                               ".url";
1396 
1397     OUString aFilePath;
1398     rc = osl::FileBase::getAbsoluteFileURL( aTargetDir, aFileName, aFilePath );
1399     if ( rc != osl::FileBase::E_None ) return false;
1400 
1401     osl::File::remove( aFilePath );
1402 
1403     // don't store empty release notes, but delete old ones
1404     if ( rURL.isEmpty() )
1405         return true;
1406 
1407     osl::File aFile( aFilePath );
1408     rc = aFile.open( osl_File_OpenFlag_Write | osl_File_OpenFlag_Create );
1409     if ( rc != osl::FileBase::E_None ) return false;
1410 
1411     OString aLineBuf("[InternetShortcut]\r\n"_ostr);
1412     sal_uInt64 nWritten = 0;
1413 
1414     OUString aURL( rURL );
1415 #ifdef _WIN32
1416     rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
1417     if ( rc != osl::FileBase::E_None ) return false;
1418     aURL = "URL=" + rURL;
1419 #endif
1420     aLineBuf = OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 );
1421     rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
1422     if ( rc != osl::FileBase::E_None ) return false;
1423 
1424     aFile.close();
1425     return true;
1426 }
1427 
1428 
showExtensionDialog()1429 void UpdateCheck::showExtensionDialog()
1430 {
1431     uno::Reference< uno::XInterface > xService;
1432 
1433     if( ! m_xContext.is() )
1434         throw uno::RuntimeException(
1435             u"UpdateCheck::showExtensionDialog(): empty component context"_ustr, uno::Reference< uno::XInterface > () );
1436 
1437     uno::Reference< lang::XMultiComponentFactory > xServiceManager( m_xContext->getServiceManager() );
1438     if( !xServiceManager.is() )
1439         throw uno::RuntimeException(
1440             u"UpdateCheck::showExtensionDialog(): unable to obtain service manager from component context"_ustr, uno::Reference< uno::XInterface > () );
1441 
1442     xService = xServiceManager->createInstanceWithContext( u"com.sun.star.deployment.ui.PackageManagerDialog"_ustr, m_xContext );
1443     uno::Reference< task::XJobExecutor > xExecutable( xService, uno::UNO_QUERY );
1444     if ( xExecutable.is() )
1445         xExecutable->trigger( u"SHOW_UPDATE_DIALOG"_ustr );
1446 }
1447 
1448 
1449 rtl::Reference<UpdateHandler>
getUpdateHandler()1450 UpdateCheck::getUpdateHandler()
1451 {
1452     std::scoped_lock aGuard(m_aMutex);
1453 
1454     if( ! m_aUpdateHandler.is() )
1455         m_aUpdateHandler = new UpdateHandler(m_xContext, this);
1456 
1457     return m_aUpdateHandler;
1458 }
1459 
1460 
1461 uno::Reference< task::XInteractionHandler >
getInteractionHandler() const1462 UpdateCheck::getInteractionHandler() const
1463 {
1464     std::scoped_lock aGuard(m_aMutex);
1465 
1466     uno::Reference< task::XInteractionHandler > xHandler;
1467 
1468     if( m_aUpdateHandler.is() && m_aUpdateHandler->isVisible() )
1469         xHandler = m_aUpdateHandler.get();
1470 
1471     return xHandler;
1472 }
1473 
1474 
1475 bool
isDialogShowing() const1476 UpdateCheck::isDialogShowing() const
1477 {
1478     std::scoped_lock aGuard(m_aMutex);
1479     return m_aUpdateHandler.is() && m_aUpdateHandler->isVisible();
1480 };
1481 
1482 
1483 void
autoCheckStatusChanged(bool enabled)1484 UpdateCheck::autoCheckStatusChanged(bool enabled)
1485 {
1486     std::unique_lock aGuard(m_aMutex);
1487 
1488     if( (CHECK_SCHEDULED == m_eState) && !enabled )
1489         shutdownThread(false);
1490 
1491     if( (DISABLED == m_eState) || (CHECK_SCHEDULED == m_eState) )
1492     {
1493         enableAutoCheck(enabled);
1494         UpdateState eState = getUIState(m_aUpdateInfo);
1495         aGuard.unlock();
1496         setUIState(eState);
1497     }
1498 };
1499 
1500 
1501 void
autoCheckIntervalChanged()1502 UpdateCheck::autoCheckIntervalChanged()
1503 {
1504     // just wake-up
1505     m_aCondition.set();
1506 };
1507 
1508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1509