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