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