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