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