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