xref: /trunk/main/extensions/source/update/check/updatecheck.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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                         // strip trailing CR/LF, but never read/write before the
185                         // start of the buffer (e.g. when nBytesRead == 0)
186                         sal_Char *pc = szBuffer + nBytesRead;
187                         while( pc > szBuffer && ( '\n' == *(pc-1) || '\r' == *(pc-1) ) )
188                             --pc;
189                         *pc = '\0';
190 
191                         aImageName += rtl::OUString(szBuffer, pc - szBuffer, 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 
323 private:
324     rtl::Reference< UpdateCheck > m_aUpdateCheck;
325 };
326 
327 class DownloadThread :  public WorkerThread
328 {
329 public:
330     DownloadThread(
331         osl::Condition& rCondition,
332         const uno::Reference<uno::XComponentContext>& xContext,
333         const rtl::Reference< DownloadInteractionHandler >& rHandler,
334         const rtl::OUString& rURL );
335 
336     virtual void SAL_CALL run();
337     virtual void SAL_CALL cancel();
338     virtual void SAL_CALL suspend();
339     virtual void SAL_CALL onTerminated();
340 
341 protected:
342     ~DownloadThread();
343 
344 private:
345     osl::Condition& m_aCondition;
346     const uno::Reference<uno::XComponentContext> m_xContext;
347     const rtl::OUString m_aURL;
348     Download m_aDownload;
349 };
350 
351 //------------------------------------------------------------------------------
352 class ShutdownThread :  public osl::Thread
353 {
354 public:
355     ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext );
356 
357     virtual void SAL_CALL run();
358     virtual void SAL_CALL onTerminated();
359 
360 protected:
361     ~ShutdownThread();
362 
363 private:
364     osl::Condition m_aCondition;
365     const uno::Reference<uno::XComponentContext> m_xContext;
366 };
367 
368 //------------------------------------------------------------------------------
369 
UpdateCheckThread(osl::Condition & rCondition,const uno::Reference<uno::XComponentContext> & xContext)370 UpdateCheckThread::UpdateCheckThread( osl::Condition& rCondition,
371                                       const uno::Reference<uno::XComponentContext>& xContext ) :
372     m_aCondition(rCondition),
373     m_pHasInternetConnection(NULL),
374     m_xContext(xContext)
375 {
376 
377 #ifdef WNT
378     rtl::OUString aPath;
379     if( osl_getExecutableFile(&aPath.pData) == osl_Process_E_None )
380     {
381         sal_uInt32 lastIndex = aPath.lastIndexOf('/');
382         if ( lastIndex > 0 )
383         {
384             aPath = aPath.copy( 0, lastIndex+1 );
385             aPath  += UNISTRING( "onlinecheck" );
386         }
387 
388         if ( m_aModule.load(aPath) )
389         {
390             m_pHasInternetConnection =
391                 reinterpret_cast < OnlineCheckFunc > (
392                     m_aModule.getFunctionSymbol( UNISTRING("hasInternetConnection")));
393         }
394     }
395 #endif
396 
397     createSuspended();
398 
399     // actually run the thread
400     resume();
401 }
402 
403 //------------------------------------------------------------------------------
404 
~UpdateCheckThread()405 UpdateCheckThread::~UpdateCheckThread()
406 {
407 }
408 
409 //------------------------------------------------------------------------------
410 
411 
412 void SAL_CALL
terminate()413 UpdateCheckThread::terminate()
414 {
415     // Cancel potentially hanging http request ..
416     cancel();
417     // .. before terminating
418     osl::Thread::terminate();
419 }
420 
421 //------------------------------------------------------------------------------
422 
423 void SAL_CALL
join()424 UpdateCheckThread::join()
425 {
426     uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
427 
428     // do not join during an update check until #i73893# is fixed
429     if( ! xProvider.is() )
430     {
431         osl::Thread::join();
432     }
433 }
434 
435 //------------------------------------------------------------------------------
436 
437 void SAL_CALL
cancel()438 UpdateCheckThread::cancel()
439 {
440     uno::Reference< deployment::XUpdateInformationProvider > xProvider(getProvider());
441 
442     if( xProvider.is() )
443         xProvider->cancel();
444 }
445 
446 //------------------------------------------------------------------------------
447 
448 bool
runCheck(bool & rbExtensionsChecked)449 UpdateCheckThread::runCheck( bool & rbExtensionsChecked )
450 {
451     bool ret = false;
452     UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
453 
454     UpdateInfo aInfo;
455     rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
456 
457     if( checkForUpdates(aInfo, m_xContext, aController->getInteractionHandler(), createProvider()) )
458     {
459         aController->setUpdateInfo(aInfo);
460         eUIState = aController->getUIState(aInfo);
461         ret = true;
462     }
463     else
464         aController->setCheckFailedState();
465 
466     // We will only look for extension updates, when there is no 'check for office updates' dialog open
467     // and when there was no office update found
468     if ( ( eUIState != UPDATESTATE_UPDATE_AVAIL ) &&
469          ( eUIState != UPDATESTATE_UPDATE_NO_DOWNLOAD ) &&
470          !aController->isDialogShowing() &&
471          !rbExtensionsChecked )
472     {
473         bool bHasExtensionUpdates = checkForExtensionUpdates( m_xContext );
474         aController->setHasExtensionUpdates( bHasExtensionUpdates );
475         if ( bHasExtensionUpdates )
476             aController->setUIState( UPDATESTATE_EXT_UPD_AVAIL );
477         rbExtensionsChecked = true;
478     }
479 
480     // joining with this thread is safe again
481     clearProvider();
482     return ret;
483 }
484 
485 //------------------------------------------------------------------------------
486 
487 void SAL_CALL
onTerminated()488 UpdateCheckThread::onTerminated()
489 {
490     delete this;
491 }
492 
493 //------------------------------------------------------------------------------
494 
495 void SAL_CALL
run()496 UpdateCheckThread::run()
497 {
498     bool bExtensionsChecked = false;
499     TimeValue systime;
500     TimeValue nExtCheckTime;
501     osl_getSystemTime( &nExtCheckTime );
502 
503     osl::Condition::Result aResult = osl::Condition::result_timeout;
504     TimeValue tv = { 10, 0 };
505 
506     // Initial wait to avoid doing further time consuming tasks during start-up
507     aResult = m_aCondition.wait(&tv);
508 
509     try {
510 
511         while( sal_True == schedule() )
512         {
513             /* Use cases:
514              *  a) manual check requested from auto check thread - "last check" should not be checked (one time)
515              *     a1) manual check was requested in the middle of a running auto check,
516              *         condition is set
517              *     a2) manual check was requested while waiting for a retry,
518              *         condition is set
519              *     a3) manual check was requested while waiting for time to next
520              *         scheduled check elapsing, condition is set
521              *     a4) manual check was requested during initial wait, condition is set
522              *  b) check interval got changed, condition may be set - same sub-cases as a),
523              *     but "last check" should be honored
524              *  c) normal auto check mode, condition not set - "last check" should be honored
525              */
526 
527             // Accessing const members without synchronization
528             rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
529             rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *aController);
530 
531             // FIXME: remember last & offset ?
532             sal_Int64 last   = rModel->getLastChecked();
533             sal_Int64 offset = rModel->getCheckInterval();
534 
535             rModel.clear();
536 
537             // last == 0 means check immediately
538             bool checkNow = ! (last > 0);
539 
540             // Reset the condition to avoid busy loops
541             if( osl::Condition::result_ok == aResult )
542             {
543                 m_aCondition.reset();
544                 aResult = osl::Condition::result_timeout;
545                 checkNow = aController->isDialogShowing();
546             }
547 
548             if( ! checkNow )
549             {
550                 osl_getSystemTime(&systime);
551 
552                 // Go back to sleep until time has elapsed
553                 sal_Int64 next = last + offset;
554                 if( last + offset > systime.Seconds )
555                 {
556                     // This can not be > 32 Bit for now ..
557                     tv.Seconds = static_cast< sal_Int32 > (next - systime.Seconds);
558                     aResult = m_aCondition.wait(&tv);
559                     continue;
560                 }
561             }
562 
563             static sal_uInt8 n = 0;
564 
565             if( ! hasInternetConnection() || ! runCheck( bExtensionsChecked ) )
566             {
567                 // the extension update check should be independent from the office update check
568                 //
569                 osl_getSystemTime( &systime );
570                 if ( nExtCheckTime.Seconds + offset < systime.Seconds )
571                     bExtensionsChecked = false;
572 
573                 // Increase next by 15, 60, .. minutes
574                 static const sal_Int32 nRetryInterval[] = { 900, 3600, 14400, 86400 };
575 
576                 if( n < sizeof(nRetryInterval) / sizeof(sal_Int32) )
577                     ++n;
578 
579                 tv.Seconds = nRetryInterval[n-1];
580                 aResult = m_aCondition.wait(&tv);
581             }
582             else // reset retry counter
583             {
584                 n = 0;
585                 bExtensionsChecked = false;
586             }
587         }
588     }
589 
590     catch(const uno::Exception& e) {
591         // Silently catch all errors
592         OSL_TRACE( "Caught exception: %s\n thread terminated.\n",
593             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr() );
594     }
595 }
596 
597 //------------------------------------------------------------------------------
598 
599 void SAL_CALL
run()600 ManualUpdateCheckThread::run()
601 {
602     bool bExtensionsChecked = false;
603 
604     try {
605         runCheck( bExtensionsChecked );
606         m_aCondition.reset();
607     }
608     catch(const uno::Exception& e) {
609         // Silently catch all errors
610         OSL_TRACE( "Caught exception: %s\n thread terminated.\n",
611             rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr() );
612     }
613 }
614 
615 //------------------------------------------------------------------------------
616 
MenuBarButtonJob(const rtl::Reference<UpdateCheck> & rUpdateCheck)617 MenuBarButtonJob::MenuBarButtonJob(const rtl::Reference< UpdateCheck >& rUpdateCheck) :
618     m_aUpdateCheck(rUpdateCheck)
619 {
620 };
621 
622 //------------------------------------------------------------------------------
623 
624 uno::Any SAL_CALL
execute(const uno::Sequence<beans::NamedValue> &)625 MenuBarButtonJob::execute(const uno::Sequence<beans::NamedValue>& )
626 {
627     if ( m_aUpdateCheck->shouldShowExtUpdDlg() )
628         m_aUpdateCheck->showExtensionDialog();
629     else
630         m_aUpdateCheck->showDialog();
631 
632     return uno::Any();
633 }
634 
635 //------------------------------------------------------------------------------
636 
DownloadThread(osl::Condition & rCondition,const uno::Reference<uno::XComponentContext> & xContext,const rtl::Reference<DownloadInteractionHandler> & rHandler,const rtl::OUString & rURL)637 DownloadThread::DownloadThread(osl::Condition& rCondition,
638                                const uno::Reference<uno::XComponentContext>& xContext,
639                                const rtl::Reference< DownloadInteractionHandler >& rHandler,
640                                const rtl::OUString& rURL) :
641     m_aCondition(rCondition),
642     m_xContext(xContext),
643     m_aURL(rURL),
644     m_aDownload(xContext, rHandler)
645 {
646     createSuspended();
647 }
648 
649 //------------------------------------------------------------------------------
650 
~DownloadThread()651 DownloadThread::~DownloadThread()
652 {
653 }
654 
655 //------------------------------------------------------------------------------
656 
657 void SAL_CALL
run()658 DownloadThread::run()
659 {
660 #ifdef WNT
661     CoUninitialize();
662     CoInitialize( NULL );
663 #endif
664 
665     while( schedule() )
666     {
667         rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
668 
669         rtl::OUString aLocalFile = rModel->getLocalFileName();
670         rtl::OUString aDownloadDest = rModel->getDownloadDestination();
671 
672         // release config class for now
673         rModel.clear();
674 
675         static sal_uInt8 n = 0;
676         if( ! m_aDownload.start(m_aURL, aLocalFile, aDownloadDest ) )
677         {
678             // retry every 15s unless the dialog is not visible
679             TimeValue tv;
680             tv.Seconds = 15;
681 
682             if( ! UpdateCheck::get()->isDialogShowing() )
683             {
684                 // Increase next by 1, 5, 15, 60, .. minutes
685                 static const sal_Int16 nRetryInterval[] = { 60, 300, 900, 3600 };
686 
687                 if( n < sizeof(nRetryInterval) / sizeof(sal_Int16) )
688                     ++n;
689 
690                 tv.Seconds = nRetryInterval[n-1];
691             }
692             m_aCondition.wait(&tv);
693         }
694         else
695         {
696             // reset wait period after successful download
697             n=0;
698         }
699     }
700 }
701 
702 //------------------------------------------------------------------------------
703 
cancel()704 void SAL_CALL DownloadThread::cancel()
705 {
706     m_aDownload.stop();
707     resume();
708 
709     rtl::Reference< UpdateCheck > aController(UpdateCheck::get());
710     aController->cancelDownload();
711 }
712 
713 //------------------------------------------------------------------------------
714 
suspend()715 void SAL_CALL DownloadThread::suspend()
716 {
717     osl::Thread::suspend();
718     m_aDownload.stop();
719 }
720 
721 //------------------------------------------------------------------------------
722 
onTerminated()723 void SAL_CALL DownloadThread::onTerminated()
724 {
725     delete this;
726 }
727 
728 //------------------------------------------------------------------------------
ShutdownThread(const uno::Reference<uno::XComponentContext> & xContext)729 ShutdownThread::ShutdownThread( const uno::Reference<uno::XComponentContext>& xContext) :
730     m_xContext( xContext )
731 {
732     create();
733 }
734 
735 //------------------------------------------------------------------------------
~ShutdownThread()736 ShutdownThread::~ShutdownThread()
737 {
738 }
739 
740 //------------------------------------------------------------------------------
741 void SAL_CALL
run()742 ShutdownThread::run()
743 {
744     TimeValue tv = { 0, 250 };
745 
746     m_aCondition.wait(&tv);
747 
748     // Tell QuickStarter not to veto ..
749     uno::Reference< beans::XFastPropertySet > xQuickStarter(
750         UpdateCheck::createService(UNISTRING("com.sun.star.office.Quickstart"), m_xContext),
751         uno::UNO_QUERY
752     );
753 
754     if (xQuickStarter.is())
755         xQuickStarter->setFastPropertyValue(0, uno::makeAny(false));
756 
757     // Shutdown the office
758     uno::Reference< frame::XDesktop > xDesktop(
759         UpdateCheck::createService(UNISTRING("com.sun.star.frame.Desktop"), m_xContext),
760         uno::UNO_QUERY);
761 
762     if( xDesktop.is() )
763         xDesktop->terminate();
764 }
765 
766 //------------------------------------------------------------------------------
onTerminated()767 void SAL_CALL ShutdownThread::onTerminated()
768 {
769     delete this;
770 }
771 
772 //------------------------------------------------------------------------------
773 
774 } // anonymous namespace
775 
776 
777 //------------------------------------------------------------------------------
778 
779 
780 void
initialize(const uno::Sequence<beans::NamedValue> & rValues,const uno::Reference<uno::XComponentContext> & xContext)781 UpdateCheck::initialize(const uno::Sequence< beans::NamedValue >& rValues,
782                         const uno::Reference<uno::XComponentContext>& xContext)
783 {
784     osl::MutexGuard aGuard(m_aMutex);
785 
786     if( NOT_INITIALIZED == m_eState )
787     {
788         NamedValueByNameAccess aNameAccess(rValues);
789         UpdateCheckROModel aModel( aNameAccess );
790         m_xContext = xContext;
791 
792         rtl::OUString aUpdateEntryVersion = aModel.getUpdateEntryVersion();
793 
794         aModel.getUpdateEntry(m_aUpdateInfo);
795 
796         bool obsoleteUpdateInfo = isObsoleteUpdateInfo(aUpdateEntryVersion);
797         bool bContinueDownload = false;
798         bool bDownloadAvailable = false;
799 
800         m_bHasExtensionUpdate = checkForPendingUpdates( xContext );
801         m_bShowExtUpdDlg = false;
802 
803         rtl::OUString aLocalFileName = aModel.getLocalFileName();
804 
805         if( aLocalFileName.getLength() > 0 )
806         {
807             bContinueDownload = true;
808 
809             // Try to get the number of bytes already on disk
810             osl::DirectoryItem aDirectoryItem;
811             if( osl::DirectoryItem::E_None == osl::DirectoryItem::get(aLocalFileName, aDirectoryItem) )
812             {
813                 osl::FileStatus aFileStatus(FileStatusMask_FileSize);
814                 if( osl::DirectoryItem::E_None == aDirectoryItem.getFileStatus(aFileStatus) )
815                 {
816                     sal_Int64 nDownloadSize = aModel.getDownloadSize();
817                     sal_Int64 nFileSize = aFileStatus.getFileSize();
818 
819                     if( nDownloadSize > 0 )
820                     {
821                         if ( nDownloadSize <= nFileSize ) // we have already downloaded everything
822                         {
823                             bContinueDownload = false;
824                             bDownloadAvailable = true;
825                             m_aImageName = getImageFromFileName( aLocalFileName );
826                         }
827                         else // Calculate initial percent value.
828                         {
829                             sal_Int32 nPercent = (sal_Int32) (100 * nFileSize / nDownloadSize);
830                             getUpdateHandler()->setProgress( nPercent );
831                         }
832                     }
833                 }
834             }
835 
836             if ( bContinueDownload )
837             {
838                 bool downloadPaused = aModel.isDownloadPaused();
839 
840                 enableDownload(true, downloadPaused);
841                 setUIState(downloadPaused ? UPDATESTATE_DOWNLOAD_PAUSED : UPDATESTATE_DOWNLOADING);
842             }
843 
844         }
845         if ( !bContinueDownload )
846         {
847             // We do this intentionally only if no download is in progress ..
848             if( obsoleteUpdateInfo )
849             {
850                 // Bring-up release note for position 5 ..
851                 const rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 5));
852                 if( aURL.getLength() > 0 )
853                     showReleaseNote(aURL);
854 
855                 // Data is outdated, probably due to installed update
856                 rtl::Reference< UpdateCheckConfig > aConfig = UpdateCheckConfig::get( xContext, *this );
857                 aConfig->clearUpdateFound();
858                 aConfig->clearLocalFileName();
859 
860 
861                 m_aUpdateInfo = UpdateInfo();
862                 // Remove outdated release notes
863                 storeReleaseNote( 1, rtl::OUString() );
864                 storeReleaseNote( 2, rtl::OUString() );
865             }
866             else
867             {
868                 enableAutoCheck(aModel.isAutoCheckEnabled());
869                 if ( bDownloadAvailable )
870                     setUIState( UPDATESTATE_DOWNLOAD_AVAIL );
871                 else
872                     setUIState(getUIState(m_aUpdateInfo));
873             }
874         }
875     }
876 }
877 
878 //------------------------------------------------------------------------------
879 
880 void
cancel()881 UpdateCheck::cancel()
882 {
883     osl::ClearableMutexGuard aGuard(m_aMutex);
884 
885     WorkerThread *pThread = m_pThread;
886     UpdateState eUIState = getUIState(m_aUpdateInfo);
887 
888     aGuard.clear();
889 
890     if( NULL != pThread )
891         pThread->cancel();
892 
893     setUIState(eUIState);
894 }
895 
896 //------------------------------------------------------------------------------
897 
898 void
download()899 UpdateCheck::download()
900 {
901     osl::ClearableMutexGuard aGuard(m_aMutex);
902     UpdateInfo aInfo(m_aUpdateInfo);
903     State eState = m_eState;
904     aGuard.clear();
905 
906     if( aInfo.Sources[0].IsDirect )
907     {
908         // Ignore second click of a double click
909         if( DOWNLOADING != eState )
910         {
911             shutdownThread(true);
912 
913             osl::ClearableMutexGuard aGuard2(m_aMutex);
914             enableDownload(true);
915             aGuard2.clear();
916             setUIState(UPDATESTATE_DOWNLOADING);
917         }
918     }
919     else
920     {
921         showReleaseNote(aInfo.Sources[0].URL); // Display in browser
922     }
923 }
924 
925 //------------------------------------------------------------------------------
926 
927 void
install()928 UpdateCheck::install()
929 {
930     osl::MutexGuard aGuard(m_aMutex);
931 
932     const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
933         c3s::SystemShellExecute::create( m_xContext ) );
934 
935     try {
936         // Construct install command ??
937 
938         // Store release note for position 3 and 4
939         rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 3));
940         storeReleaseNote(1, aURL);
941 
942         aURL = getReleaseNote(m_aUpdateInfo, 4);
943         storeReleaseNote(2, aURL);
944 
945         if( xShellExecute.is() )
946         {
947             rtl::OUString aInstallImage(m_aImageName);
948             osl::FileBase::getSystemPathFromFileURL(aInstallImage, aInstallImage);
949 
950             rtl::OUString aParameter;
951             sal_Int32 nFlags = c3s::SystemShellExecuteFlags::DEFAULTS;
952 #if ( defined LINUX || defined SOLARIS )
953             nFlags = 42;
954             aParameter = getBaseInstallation();
955             if( aParameter.getLength() > 0 )
956                 osl::FileBase::getSystemPathFromFileURL(aParameter, aParameter);
957 
958             aParameter += UNISTRING(" &");
959 #endif
960 
961             rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
962             rModel->clearLocalFileName();
963 
964             xShellExecute->execute(aInstallImage, aParameter, nFlags);
965             ShutdownThread *pShutdownThread = new ShutdownThread( m_xContext );
966             (void) pShutdownThread;
967         }
968     } catch(uno::Exception&) {
969         m_aUpdateHandler->setErrorMessage( m_aUpdateHandler->getDefaultInstErrMsg() );
970     }
971 }
972 
973 //------------------------------------------------------------------------------
974 
975 void
pause()976 UpdateCheck::pause()
977 {
978     osl::ClearableMutexGuard aGuard(m_aMutex);
979 
980     if( NULL != m_pThread )
981         m_pThread->suspend();
982 
983     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
984     aGuard.clear();
985 
986     rModel->storeDownloadPaused(true);
987     setUIState(UPDATESTATE_DOWNLOAD_PAUSED);
988 }
989 
990 //------------------------------------------------------------------------------
991 
992 void
resume()993 UpdateCheck::resume()
994 {
995     osl::ClearableMutexGuard aGuard(m_aMutex);
996 
997     if( NULL != m_pThread )
998         m_pThread->resume();
999 
1000     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
1001     aGuard.clear();
1002 
1003     rModel->storeDownloadPaused(false);
1004     setUIState(UPDATESTATE_DOWNLOADING);
1005 }
1006 
1007 //------------------------------------------------------------------------------
1008 
1009 void
closeAfterFailure()1010 UpdateCheck::closeAfterFailure()
1011 {
1012     osl::ClearableMutexGuard aGuard(m_aMutex);
1013 
1014     if ( ( m_eState == DISABLED ) || ( m_eState == CHECK_SCHEDULED ) )
1015     {
1016         const UpdateState eUIState = getUIState( m_aUpdateInfo );
1017         aGuard.clear();
1018         setUIState( eUIState, true );
1019     }
1020 }
1021 
1022 //------------------------------------------------------------------------------
1023 
1024 void
shutdownThread(bool join)1025 UpdateCheck::shutdownThread(bool join)
1026 {
1027     osl::ClearableMutexGuard aGuard(m_aMutex);
1028 
1029     // copy thread object pointer to stack
1030     osl::Thread *pThread = m_pThread;
1031     m_pThread = NULL;
1032     aGuard.clear();
1033 
1034     if( NULL != pThread )
1035     {
1036         pThread->terminate();
1037         if( join )
1038         {
1039             m_aCondition.set();
1040             pThread->join();
1041             m_aCondition.reset();
1042         }
1043     }
1044 }
1045 
1046 //------------------------------------------------------------------------------
1047 
1048 void
enableAutoCheck(bool enable)1049 UpdateCheck::enableAutoCheck(bool enable)
1050 {
1051     if( enable )
1052         m_pThread = new UpdateCheckThread(m_aCondition, m_xContext);
1053 
1054     m_eState = enable ? CHECK_SCHEDULED : DISABLED;
1055 }
1056 
1057 //------------------------------------------------------------------------------
1058 
1059 void
enableDownload(bool enable,bool paused)1060 UpdateCheck::enableDownload(bool enable, bool paused)
1061 {
1062     OSL_ASSERT(NULL == m_pThread);
1063 
1064     State eState = DISABLED;
1065     if( enable )
1066     {
1067         m_pThread = new DownloadThread(m_aCondition, m_xContext, this, m_aUpdateInfo.Sources[0].URL );
1068         if( !paused )
1069         {
1070             eState = DOWNLOADING;
1071             m_pThread->resume();
1072         }
1073         else
1074             eState = DOWNLOAD_PAUSED;
1075 
1076         m_eState = eState;
1077     }
1078     else {
1079         enableAutoCheck(UpdateCheckConfig::get(m_xContext)->isAutoCheckEnabled());
1080     }
1081 
1082 }
1083 
1084 //------------------------------------------------------------------------------
1085 
1086 bool
downloadTargetExists(const rtl::OUString & rFileName)1087 UpdateCheck::downloadTargetExists(const rtl::OUString& rFileName)
1088 {
1089     osl::ClearableMutexGuard aGuard(m_aMutex);
1090 
1091     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
1092     UpdateState eUIState = UPDATESTATE_DOWNLOADING;
1093 
1094     bool cont = false;
1095 
1096     if( aUpdateHandler->isVisible() )
1097     {
1098         cont = aUpdateHandler->showOverwriteWarning();
1099         if( cont )
1100         {
1101             if( osl_File_E_None != osl_removeFile(rFileName.pData) )
1102             {
1103                 // FIXME: error message
1104                 cont = false;
1105             }
1106         }
1107         else
1108             eUIState = getUIState(m_aUpdateInfo);
1109     }
1110     else
1111     {
1112         m_aImageName = getImageFromFileName(rFileName);
1113         eUIState = UPDATESTATE_DOWNLOAD_AVAIL;
1114     }
1115 
1116     if( !cont )
1117     {
1118         shutdownThread(false);
1119         enableDownload(false);
1120 
1121         aGuard.clear();
1122         setUIState(eUIState);
1123     }
1124 
1125     return cont;
1126 }
1127 
1128 //------------------------------------------------------------------------------
checkDownloadDestination(const rtl::OUString & rFileName)1129 bool UpdateCheck::checkDownloadDestination( const rtl::OUString& rFileName )
1130 {
1131     osl::ClearableMutexGuard aGuard(m_aMutex);
1132 
1133     rtl::Reference< UpdateHandler > aUpdateHandler( getUpdateHandler() );
1134 
1135     bool bReload = false;
1136 
1137     if( aUpdateHandler->isVisible() )
1138     {
1139         bReload = aUpdateHandler->showOverwriteWarning( rFileName );
1140     }
1141 
1142     return bReload;
1143 }
1144 
1145 //------------------------------------------------------------------------------
1146 
1147 void
downloadStalled(const rtl::OUString & rErrorMessage)1148 UpdateCheck::downloadStalled(const rtl::OUString& rErrorMessage)
1149 {
1150     osl::ClearableMutexGuard aGuard(m_aMutex);
1151     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
1152     aGuard.clear();
1153 
1154     aUpdateHandler->setErrorMessage(rErrorMessage);
1155     setUIState(UPDATESTATE_ERROR_DOWNLOADING);
1156 }
1157 
1158 //------------------------------------------------------------------------------
1159 
1160 void
downloadProgressAt(sal_Int8 nPercent)1161 UpdateCheck::downloadProgressAt(sal_Int8 nPercent)
1162 {
1163     osl::ClearableMutexGuard aGuard(m_aMutex);
1164     rtl::Reference< UpdateHandler > aUpdateHandler(getUpdateHandler());
1165     aGuard.clear();
1166 
1167     aUpdateHandler->setProgress(nPercent);
1168     setUIState(UPDATESTATE_DOWNLOADING);
1169 }
1170 
1171 //------------------------------------------------------------------------------
1172 
1173 void
downloadStarted(const rtl::OUString & rLocalFileName,sal_Int64 nFileSize)1174 UpdateCheck::downloadStarted(const rtl::OUString& rLocalFileName, sal_Int64 nFileSize)
1175 {
1176     if ( nFileSize > 0 )
1177     {
1178         osl::MutexGuard aGuard(m_aMutex);
1179 
1180         rtl::Reference< UpdateCheckConfig > aModel(UpdateCheckConfig::get(m_xContext));
1181         aModel->storeLocalFileName(rLocalFileName, nFileSize);
1182 
1183         // Bring-up release note for position 1 ..
1184         const rtl::OUString aURL(getReleaseNote(m_aUpdateInfo, 1, aModel->isAutoDownloadEnabled()));
1185         if( aURL.getLength() > 0 )
1186             showReleaseNote(aURL);
1187     }
1188 }
1189 
1190 //------------------------------------------------------------------------------
1191 
1192 void
downloadFinished(const rtl::OUString & rLocalFileName)1193 UpdateCheck::downloadFinished(const rtl::OUString& rLocalFileName)
1194 {
1195     osl::ClearableMutexGuard aGuard(m_aMutex);
1196 
1197     // no more retries
1198     m_pThread->terminate();
1199 
1200     m_aImageName = getImageFromFileName(rLocalFileName);
1201     UpdateInfo aUpdateInfo(m_aUpdateInfo);
1202 
1203     aGuard.clear();
1204     setUIState(UPDATESTATE_DOWNLOAD_AVAIL);
1205 
1206     // Bring-up release note for position 2 ..
1207     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get( m_xContext );
1208     const rtl::OUString aURL(getReleaseNote(aUpdateInfo, 2, rModel->isAutoDownloadEnabled()));
1209     if( aURL.getLength() > 0 )
1210         showReleaseNote(aURL);
1211 }
1212 
1213 //------------------------------------------------------------------------------
1214 
1215 void
cancelDownload()1216 UpdateCheck::cancelDownload()
1217 {
1218     shutdownThread(true);
1219 
1220     osl::MutexGuard aGuard(m_aMutex);
1221     enableDownload(false);
1222 
1223     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext);
1224 
1225     rtl::OUString aLocalFile(rModel->getLocalFileName());
1226     rModel->clearLocalFileName();
1227     rModel->storeDownloadPaused(false);
1228 
1229     if( isObsoleteUpdateInfo(rModel->getUpdateEntryVersion()) )
1230     {
1231         rModel->clearUpdateFound(); // This wasn't done during init yet ..
1232         m_aUpdateInfo = UpdateInfo();
1233     }
1234 
1235     /*oslFileError rc =*/ osl_removeFile(aLocalFile.pData);
1236     // FIXME: error handling ..
1237 
1238 }
1239 
1240 //------------------------------------------------------------------------------
1241 
1242 void
showDialog(bool forceCheck)1243 UpdateCheck::showDialog(bool forceCheck)
1244 {
1245     osl::ResettableMutexGuard aGuard(m_aMutex);
1246 
1247     bool update_found = m_aUpdateInfo.BuildId.getLength() > 0;
1248     bool bSetUIState = ! m_aUpdateHandler.is();
1249 
1250     UpdateState eDialogState = UPDATESTATES_COUNT;
1251 
1252     switch( m_eState )
1253     {
1254     case DISABLED:
1255     case CHECK_SCHEDULED:
1256         if( forceCheck || ! update_found ) // Run check when forced or if we did not find an update yet
1257         {
1258             eDialogState = UPDATESTATE_CHECKING;
1259             bSetUIState = true;
1260         }
1261         else if(m_aUpdateInfo.Sources[0].IsDirect)
1262             eDialogState = UPDATESTATE_UPDATE_AVAIL;
1263         else
1264             eDialogState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1265         break;
1266 
1267     case DOWNLOADING:
1268         eDialogState = UPDATESTATE_DOWNLOADING;
1269         break;
1270 
1271     case DOWNLOAD_PAUSED:
1272         eDialogState = UPDATESTATE_DOWNLOAD_PAUSED;
1273         break;
1274 
1275     case NOT_INITIALIZED:
1276         OSL_ASSERT( false );
1277         break;
1278     }
1279 
1280     if( bSetUIState )
1281     {
1282         aGuard.clear();
1283         setUIState(eDialogState, true); // suppress bubble as Dialog will be visible soon
1284         aGuard.reset();
1285     }
1286 
1287     getUpdateHandler()->setVisible(true);
1288 
1289     // Run check in separate thread ..
1290     if( UPDATESTATE_CHECKING == eDialogState )
1291     {
1292         if( DISABLED == m_eState )
1293         {
1294             // destructs itself when done, not cancellable for now ..
1295             new ManualUpdateCheckThread(m_aCondition, m_xContext);
1296         }
1297 
1298         m_aCondition.set();
1299     }
1300 }
1301 
1302 //------------------------------------------------------------------------------
1303 
1304 void
setUpdateInfo(const UpdateInfo & aInfo)1305 UpdateCheck::setUpdateInfo(const UpdateInfo& aInfo)
1306 {
1307     osl::ClearableMutexGuard aGuard(m_aMutex);
1308 
1309     bool bSuppressBubble = (sal_True == aInfo.BuildId.equals(m_aUpdateInfo.BuildId));
1310     m_aUpdateInfo = aInfo;
1311 
1312     OSL_ASSERT(DISABLED == m_eState || CHECK_SCHEDULED == m_eState);
1313 
1314     // Ignore leading non direct download if we get direct ones
1315     std::vector< DownloadSource >::iterator iter = m_aUpdateInfo.Sources.begin();
1316     while( iter != m_aUpdateInfo.Sources.end() )
1317     {
1318         if( iter->IsDirect )
1319             break;
1320 
1321         ++iter;
1322     }
1323 
1324     if( (iter != m_aUpdateInfo.Sources.begin()) &&
1325         (iter != m_aUpdateInfo.Sources.end()) &&
1326         iter->IsDirect )
1327     {
1328         m_aUpdateInfo.Sources.erase(m_aUpdateInfo.Sources.begin(), --iter);
1329     }
1330 
1331     rtl::Reference< UpdateCheckConfig > rModel = UpdateCheckConfig::get(m_xContext, *this);
1332     OSL_ASSERT( rModel.is() );
1333 
1334     // Decide whether to use alternate release note pos ..
1335     bool autoDownloadEnabled = rModel->isAutoDownloadEnabled();
1336 
1337     std::vector< ReleaseNote >::iterator iter2 = m_aUpdateInfo.ReleaseNotes.begin();
1338     while( iter2 != m_aUpdateInfo.ReleaseNotes.end() )
1339     {
1340         if( ((1 == iter2->Pos) || (2 == iter2->Pos)) && autoDownloadEnabled && (iter2->URL2.getLength() > 0))
1341         {
1342             iter2->URL = iter2->URL2;
1343             iter2->URL2 = rtl::OUString();
1344             iter2->Pos = iter2->Pos2;
1345             iter2->Pos2 = 0;
1346         }
1347 
1348         ++iter2;
1349     }
1350 
1351     // do not move below store/clear ..
1352     rModel->updateLastChecked();
1353 
1354     UpdateState eUIState;
1355     if( m_aUpdateInfo.Sources.size() > 0 )
1356     {
1357         rModel->storeUpdateFound(aInfo, getBuildId());
1358 
1359         if( m_aUpdateInfo.Sources[0].IsDirect )
1360         {
1361             eUIState = UPDATESTATE_UPDATE_AVAIL;
1362 
1363             if( rModel->isAutoDownloadEnabled() )
1364             {
1365                 shutdownThread(false);
1366                 eUIState = UPDATESTATE_DOWNLOADING;
1367                 enableDownload(true);
1368             }
1369         }
1370         else
1371             eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1372     }
1373     else
1374     {
1375         eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
1376         rModel->clearUpdateFound();
1377     }
1378 
1379     aGuard.clear();
1380     setUIState(eUIState, bSuppressBubble);
1381 }
1382 
1383 //------------------------------------------------------------------------------
1384 
1385 void
setCheckFailedState()1386 UpdateCheck::setCheckFailedState()
1387 {
1388     setUIState(UPDATESTATE_ERROR_CHECKING);
1389 }
1390 
1391 //------------------------------------------------------------------------------
handleMenuBarUI(rtl::Reference<UpdateHandler> rUpdateHandler,UpdateState & eState,bool suppressBubble)1392 void UpdateCheck::handleMenuBarUI( rtl::Reference< UpdateHandler > rUpdateHandler,
1393                                    UpdateState& eState,
1394                                    bool suppressBubble )
1395 {
1396     uno::Reference<beans::XPropertySet> xMenuBarUI( m_xMenuBarUI );
1397 
1398     if ( ( UPDATESTATE_NO_UPDATE_AVAIL == eState ) && m_bHasExtensionUpdate )
1399         eState = UPDATESTATE_EXT_UPD_AVAIL;
1400 
1401     if ( UPDATESTATE_EXT_UPD_AVAIL == eState )
1402         m_bShowExtUpdDlg = true;
1403     else
1404         m_bShowExtUpdDlg = false;
1405 
1406     if( xMenuBarUI.is() )
1407     {
1408         if( UPDATESTATE_NO_UPDATE_AVAIL == eState )
1409         {
1410             xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::makeAny(sal_False) );
1411         }
1412         else
1413         {
1414             xMenuBarUI->setPropertyValue( PROPERTY_TITLE, uno::makeAny(rUpdateHandler->getBubbleTitle(eState)) );
1415             xMenuBarUI->setPropertyValue( PROPERTY_TEXT, uno::makeAny(rUpdateHandler->getBubbleText(eState)) );
1416 
1417             if( ! suppressBubble && ( ! rUpdateHandler->isVisible() || rUpdateHandler->isMinimized() ) )
1418                 xMenuBarUI->setPropertyValue( PROPERTY_SHOW_BUBBLE, uno::makeAny( sal_True ) );
1419 
1420             if( UPDATESTATE_CHECKING != eState )
1421                 xMenuBarUI->setPropertyValue( PROPERTY_SHOW_MENUICON, uno::makeAny(sal_True) );
1422         }
1423     }
1424 }
1425 
1426 //------------------------------------------------------------------------------
setUIState(UpdateState eState,bool suppressBubble)1427 void UpdateCheck::setUIState(UpdateState eState, bool suppressBubble)
1428 {
1429     osl::ClearableMutexGuard aGuard(m_aMutex);
1430 
1431     if( ! m_xMenuBarUI.is() &&
1432         (DISABLED != m_eState) &&
1433         ( m_bHasExtensionUpdate || (UPDATESTATE_NO_UPDATE_AVAIL != eState)) &&
1434         (UPDATESTATE_CHECKING != eState) &&
1435         (UPDATESTATE_ERROR_CHECKING != eState)
1436     )
1437     {
1438         m_xMenuBarUI = createMenuBarUI(m_xContext, new MenuBarButtonJob(this));
1439     }
1440 
1441     // Show bubble only when the status has changed
1442     if ( eState == m_eUpdateState )
1443         suppressBubble = true;
1444     else
1445         m_eUpdateState = eState;
1446 
1447     rtl::Reference<UpdateHandler> aUpdateHandler(getUpdateHandler());
1448     OSL_ASSERT( aUpdateHandler.is() );
1449 
1450     UpdateInfo aUpdateInfo(m_aUpdateInfo);
1451     rtl::OUString aImageName(m_aImageName);
1452 
1453     aGuard.clear();
1454 
1455     handleMenuBarUI( aUpdateHandler, eState, suppressBubble );
1456 
1457     if( (UPDATESTATE_UPDATE_AVAIL == eState)
1458      || (UPDATESTATE_DOWNLOAD_PAUSED == eState)
1459      || (UPDATESTATE_DOWNLOADING == eState) )
1460     {
1461         uno::Reference< uno::XComponentContext > xContext(m_xContext);
1462 
1463         rtl::OUString aDownloadDestination =
1464             UpdateCheckConfig::get(xContext, this)->getDownloadDestination();
1465 
1466         osl_getSystemPathFromFileURL(aDownloadDestination.pData, &aDownloadDestination.pData);
1467 
1468         aUpdateHandler->setDownloadPath(aDownloadDestination);
1469     }
1470     else if( UPDATESTATE_DOWNLOAD_AVAIL == eState )
1471     {
1472         aUpdateHandler->setDownloadFile(aImageName);
1473     }
1474 
1475     aUpdateHandler->setDescription(aUpdateInfo.Description);
1476     aUpdateHandler->setNextVersion(aUpdateInfo.Version);
1477     aUpdateHandler->setState(eState);
1478 }
1479 
1480 //------------------------------------------------------------------------------
1481 
1482 UpdateState
getUIState(const UpdateInfo & rInfo)1483 UpdateCheck::getUIState(const UpdateInfo& rInfo)
1484 {
1485     UpdateState eUIState = UPDATESTATE_NO_UPDATE_AVAIL;
1486 
1487     if( rInfo.BuildId.getLength() > 0 )
1488     {
1489         if( rInfo.Sources[0].IsDirect )
1490             eUIState = UPDATESTATE_UPDATE_AVAIL;
1491         else
1492             eUIState = UPDATESTATE_UPDATE_NO_DOWNLOAD;
1493     }
1494 
1495     return eUIState;
1496 }
1497 
1498 //------------------------------------------------------------------------------
1499 
1500 void
showReleaseNote(const rtl::OUString & rURL) const1501 UpdateCheck::showReleaseNote(const rtl::OUString& rURL) const
1502 {
1503     const uno::Reference< c3s::XSystemShellExecute > xShellExecute(
1504         createService( UNISTRING( "com.sun.star.system.SystemShellExecute" ), m_xContext ),
1505         uno::UNO_QUERY );
1506 
1507     try {
1508 
1509         if( xShellExecute.is() )
1510             xShellExecute->execute(rURL, rtl::OUString(), c3s::SystemShellExecuteFlags::DEFAULTS);
1511     } catch(c3s::SystemShellExecuteException&) {
1512     }
1513 }
1514 
1515 //------------------------------------------------------------------------------
1516 
1517 bool
storeReleaseNote(sal_Int8 nNum,const rtl::OUString & rURL)1518 UpdateCheck::storeReleaseNote(sal_Int8 nNum, const rtl::OUString &rURL)
1519 {
1520     osl::FileBase::RC rc;
1521     rtl::OUString aTargetDir( UpdateCheckConfig::getAllUsersDirectory() + UNISTRING( "/sun" ) );
1522 
1523     rc = osl::Directory::createPath( aTargetDir );
1524 
1525     rtl::OUString aFileName = UNISTRING("releasenote") +
1526                               rtl::OUString::valueOf( (sal_Int32) nNum ) +
1527                               UNISTRING(".url");
1528 
1529     rtl::OUString aFilePath;
1530     rc = osl::FileBase::getAbsoluteFileURL( aTargetDir, aFileName, aFilePath );
1531     if ( rc != osl::FileBase::E_None ) return false;
1532 
1533     rc = osl::File::remove( aFilePath );
1534 
1535     // don't store empty release notes, but delete old ones
1536     if ( rURL.getLength() == 0 )
1537         return true;
1538 
1539     osl::File aFile( aFilePath );
1540     rc = aFile.open( OpenFlag_Write | OpenFlag_Create );
1541 
1542     if ( rc != osl::FileBase::E_None ) return false;
1543 
1544     rtl::OString aLineBuf("[InternetShortcut]\r\n");
1545     sal_uInt64 nWritten = 0;
1546 
1547     rtl::OUString aURL( rURL );
1548 #ifdef WNT
1549     rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
1550     if ( rc != osl::FileBase::E_None ) return false;
1551     aURL = UNISTRING("URL=") + rURL;
1552 #endif
1553     aLineBuf = rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 );
1554     rc = aFile.write( aLineBuf.getStr(), aLineBuf.getLength(), nWritten );
1555     if ( rc != osl::FileBase::E_None ) return false;
1556 
1557     aFile.close();
1558     return true;
1559 }
1560 
1561 //------------------------------------------------------------------------------
showExtensionDialog()1562 void UpdateCheck::showExtensionDialog()
1563 {
1564     rtl::OUString sServiceName = UNISTRING("com.sun.star.deployment.ui.PackageManagerDialog");
1565     rtl::OUString sArguments = UNISTRING("SHOW_UPDATE_DIALOG");
1566     uno::Reference< uno::XInterface > xService;
1567 
1568     if( ! m_xContext.is() )
1569         throw uno::RuntimeException(
1570             UNISTRING( "UpdateCheck::showExtensionDialog(): empty component context" ), uno::Reference< uno::XInterface > () );
1571 
1572     uno::Reference< lang::XMultiComponentFactory > xServiceManager( m_xContext->getServiceManager() );
1573     if( !xServiceManager.is() )
1574         throw uno::RuntimeException(
1575             UNISTRING( "UpdateCheck::showExtensionDialog(): unable to obtain service manager from component context" ), uno::Reference< uno::XInterface > () );
1576 
1577     xService = xServiceManager->createInstanceWithContext( sServiceName, m_xContext );
1578     uno::Reference< task::XJobExecutor > xExecuteable( xService, uno::UNO_QUERY );
1579     if ( xExecuteable.is() )
1580         xExecuteable->trigger( sArguments );
1581 }
1582 
1583 //------------------------------------------------------------------------------
1584 
1585 rtl::Reference<UpdateHandler>
getUpdateHandler()1586 UpdateCheck::getUpdateHandler()
1587 {
1588     osl::MutexGuard aGuard(m_aMutex);
1589 
1590     if( ! m_aUpdateHandler.is() )
1591         m_aUpdateHandler = new UpdateHandler(m_xContext, this);
1592 
1593     return m_aUpdateHandler;
1594 }
1595 
1596 //------------------------------------------------------------------------------
1597 
1598 uno::Reference< task::XInteractionHandler >
getInteractionHandler() const1599 UpdateCheck::getInteractionHandler() const
1600 {
1601     osl::MutexGuard aGuard(m_aMutex);
1602 
1603     uno::Reference< task::XInteractionHandler > xHandler;
1604 
1605     if( m_aUpdateHandler.is() && m_aUpdateHandler->isVisible() )
1606         xHandler = m_aUpdateHandler.get();
1607 
1608     return xHandler;
1609 }
1610 
1611 //------------------------------------------------------------------------------
1612 
1613 uno::Reference< uno::XInterface >
createService(const rtl::OUString & rServiceName,const uno::Reference<uno::XComponentContext> & xContext)1614 UpdateCheck::createService(const rtl::OUString& rServiceName,
1615                            const uno::Reference<uno::XComponentContext>& xContext)
1616 {
1617     if( !xContext.is() )
1618         throw uno::RuntimeException(
1619             UNISTRING( "UpdateCheckConfig: empty component context" ),
1620             uno::Reference< uno::XInterface >() );
1621 
1622     const uno::Reference< lang::XMultiComponentFactory > xServiceManager(xContext->getServiceManager());
1623 
1624     if( !xServiceManager.is() )
1625         throw uno::RuntimeException(
1626             UNISTRING( "UpdateCheckConfig: unable to obtain service manager from component context" ),
1627             uno::Reference< uno::XInterface >() );
1628 
1629     return xServiceManager->createInstanceWithContext(rServiceName, xContext);
1630 }
1631 
1632 //------------------------------------------------------------------------------
1633 
1634 bool
isDialogShowing() const1635 UpdateCheck::isDialogShowing() const
1636 {
1637     osl::MutexGuard aGuard(m_aMutex);
1638     return sal_True == m_aUpdateHandler.is() && m_aUpdateHandler->isVisible();
1639 };
1640 
1641 //------------------------------------------------------------------------------
1642 
1643 void
autoCheckStatusChanged(bool enabled)1644 UpdateCheck::autoCheckStatusChanged(bool enabled)
1645 {
1646     osl::ClearableMutexGuard aGuard(m_aMutex);
1647 
1648     if( (CHECK_SCHEDULED == m_eState) && !enabled )
1649         shutdownThread(false);
1650 
1651     if( (DISABLED == m_eState) || (CHECK_SCHEDULED == m_eState) )
1652     {
1653         enableAutoCheck(enabled);
1654         UpdateState eState = getUIState(m_aUpdateInfo);
1655         aGuard.clear();
1656         setUIState(eState);
1657     }
1658 };
1659 
1660 //------------------------------------------------------------------------------
1661 
1662 void
autoCheckIntervalChanged()1663 UpdateCheck::autoCheckIntervalChanged()
1664 {
1665     // just wake-up
1666     m_aCondition.set();
1667 };
1668 
1669 //------------------------------------------------------------------------------
1670 
1671 oslInterlockedCount SAL_CALL
acquire()1672 UpdateCheck::acquire() SAL_THROW(())
1673 {
1674     return ReferenceObject::acquire();
1675 }
1676 
1677 //------------------------------------------------------------------------------
1678 
1679 oslInterlockedCount SAL_CALL
release()1680 UpdateCheck::release() SAL_THROW(())
1681 {
1682     return ReferenceObject::release();
1683 }
1684