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_desktop.hxx" 26 27 28 29 #include <dp_gui_handleversionexception.hxx> 30 31 #include "sal/config.h" 32 33 #include <cstddef> 34 35 #include "com/sun/star/beans/PropertyValue.hpp" 36 #include "com/sun/star/beans/NamedValue.hpp" 37 38 #include "com/sun/star/deployment/DependencyException.hpp" 39 #include "com/sun/star/deployment/LicenseException.hpp" 40 #include "com/sun/star/deployment/VersionException.hpp" 41 #include "com/sun/star/deployment/InstallException.hpp" 42 #include "com/sun/star/deployment/PlatformException.hpp" 43 44 #include "com/sun/star/deployment/ui/LicenseDialog.hpp" 45 #include "com/sun/star/deployment/DeploymentException.hpp" 46 #include "com/sun/star/deployment/UpdateInformationProvider.hpp" 47 #include "com/sun/star/deployment/XPackage.hpp" 48 49 #include "com/sun/star/task/XAbortChannel.hpp" 50 #include "com/sun/star/task/XInteractionAbort.hpp" 51 #include "com/sun/star/task/XInteractionApprove.hpp" 52 53 #include "com/sun/star/ucb/CommandAbortedException.hpp" 54 #include "com/sun/star/ucb/CommandFailedException.hpp" 55 #include "com/sun/star/ucb/XCommandEnvironment.hpp" 56 57 #include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp" 58 59 #include "com/sun/star/uno/Reference.hxx" 60 #include "com/sun/star/uno/RuntimeException.hpp" 61 #include "com/sun/star/uno/Sequence.hxx" 62 #include "com/sun/star/uno/XInterface.hpp" 63 #include "com/sun/star/uno/TypeClass.hpp" 64 #include "osl/diagnose.h" 65 #include "osl/mutex.hxx" 66 #include "rtl/ref.hxx" 67 #include "rtl/ustring.h" 68 #include "rtl/ustring.hxx" 69 #include "sal/types.h" 70 #include "ucbhelper/content.hxx" 71 #include "cppuhelper/exc_hlp.hxx" 72 #include "cppuhelper/implbase3.hxx" 73 #include "comphelper/anytostring.hxx" 74 #include "vcl/msgbox.hxx" 75 #include "vcl/threadex.hxx" 76 #include "toolkit/helper/vclunohelper.hxx" 77 #include "comphelper/processfactory.hxx" 78 79 #include "dp_gui.h" 80 #include "dp_gui_thread.hxx" 81 #include "dp_gui_extensioncmdqueue.hxx" 82 #include "dp_gui_dependencydialog.hxx" 83 #include "dp_gui_dialog2.hxx" 84 #include "dp_gui_shared.hxx" 85 #include "dp_gui_theextmgr.hxx" 86 #include "dp_gui_updatedialog.hxx" 87 #include "dp_gui_updateinstalldialog.hxx" 88 #include "dp_dependencies.hxx" 89 #include "dp_identifier.hxx" 90 #include "dp_version.hxx" 91 #include <dp_gui_handleversionexception.hxx> 92 93 #include <queue> 94 #include <boost/bind.hpp> 95 #include <boost/shared_ptr.hpp> 96 97 #if (defined(_MSC_VER) && (_MSC_VER < 1400)) 98 #define _WIN32_WINNT 0x0400 99 #endif 100 101 #ifdef WNT 102 #include "tools/prewin.h" 103 #include <objbase.h> 104 #include "tools/postwin.h" 105 #endif 106 107 108 using namespace ::com::sun::star; 109 using ::rtl::OUString; 110 111 namespace dp_gui { 112 113 //============================================================================== 114 115 class ProgressCmdEnv 116 : public ::cppu::WeakImplHelper3< ucb::XCommandEnvironment, 117 task::XInteractionHandler, 118 ucb::XProgressHandler > 119 { 120 uno::Reference< task::XInteractionHandler> m_xHandler; 121 uno::Reference< uno::XComponentContext > m_xContext; 122 uno::Reference< task::XAbortChannel> m_xAbortChannel; 123 124 DialogHelper *m_pDialogHelper; 125 OUString m_sTitle; 126 bool m_bAborted; 127 bool m_bWarnUser; 128 sal_Int32 m_nCurrentProgress; 129 130 void updateProgress(); 131 132 void update_( uno::Any const & Status ) throw ( uno::RuntimeException ); 133 134 public: 135 virtual ~ProgressCmdEnv(); 136 137 /** When param bAskWhenInstalling = true, then the user is asked if he 138 agrees to install this extension. In case this extension is already installed 139 then the user is also notified and asked if he wants to replace that existing 140 extension. In first case an interaction request with an InstallException 141 will be handled and in the second case a VersionException will be handled. 142 */ 143 144 ProgressCmdEnv( const uno::Reference< uno::XComponentContext > rContext, 145 DialogHelper *pDialogHelper, 146 const OUString &rTitle ) 147 : m_xContext( rContext ), 148 m_pDialogHelper( pDialogHelper ), 149 m_sTitle( rTitle ), 150 m_bAborted( false ), 151 m_bWarnUser( false ) 152 {} 153 154 Dialog * activeDialog() { return m_pDialogHelper ? m_pDialogHelper->getWindow() : NULL; } 155 156 void setTitle( const OUString& rNewTitle ) { m_sTitle = rNewTitle; } 157 void startProgress(); 158 void stopProgress(); 159 void progressSection( const OUString &rText, 160 const uno::Reference< task::XAbortChannel > &xAbortChannel = 0 ); 161 inline bool isAborted() const { return m_bAborted; } 162 inline void setWarnUser( bool bNewVal ) { m_bWarnUser = bNewVal; } 163 164 // XCommandEnvironment 165 virtual uno::Reference< task::XInteractionHandler > SAL_CALL getInteractionHandler() 166 throw ( uno::RuntimeException ); 167 virtual uno::Reference< ucb::XProgressHandler > SAL_CALL getProgressHandler() 168 throw ( uno::RuntimeException ); 169 170 // XInteractionHandler 171 virtual void SAL_CALL handle( uno::Reference< task::XInteractionRequest > const & xRequest ) 172 throw ( uno::RuntimeException ); 173 174 // XProgressHandler 175 virtual void SAL_CALL push( uno::Any const & Status ) 176 throw ( uno::RuntimeException ); 177 virtual void SAL_CALL update( uno::Any const & Status ) 178 throw ( uno::RuntimeException ); 179 virtual void SAL_CALL pop() throw ( uno::RuntimeException ); 180 }; 181 182 //------------------------------------------------------------------------------ 183 struct ExtensionCmd 184 { 185 enum E_CMD_TYPE { ADD, ENABLE, DISABLE, REMOVE, CHECK_FOR_UPDATES, ACCEPT_LICENSE }; 186 187 E_CMD_TYPE m_eCmdType; 188 bool m_bWarnUser; 189 OUString m_sExtensionURL; 190 OUString m_sRepository; 191 uno::Reference< deployment::XPackage > m_xPackage; 192 std::vector< uno::Reference< deployment::XPackage > > m_vExtensionList; 193 194 ExtensionCmd( const E_CMD_TYPE eCommand, 195 const OUString &rExtensionURL, 196 const OUString &rRepository, 197 const bool bWarnUser ) 198 : m_eCmdType( eCommand ), 199 m_bWarnUser( bWarnUser ), 200 m_sExtensionURL( rExtensionURL ), 201 m_sRepository( rRepository ) {}; 202 ExtensionCmd( const E_CMD_TYPE eCommand, 203 const uno::Reference< deployment::XPackage > &rPackage ) 204 : m_eCmdType( eCommand ), 205 m_bWarnUser( false ), 206 m_xPackage( rPackage ) {}; 207 ExtensionCmd( const E_CMD_TYPE eCommand, 208 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ) 209 : m_eCmdType( eCommand ), 210 m_bWarnUser( false ), 211 m_vExtensionList( vExtensionList ) {}; 212 }; 213 214 typedef ::boost::shared_ptr< ExtensionCmd > TExtensionCmd; 215 216 //------------------------------------------------------------------------------ 217 class ExtensionCmdQueue::Thread: public dp_gui::Thread 218 { 219 public: 220 Thread( DialogHelper *pDialogHelper, 221 TheExtensionManager *pManager, 222 const uno::Reference< uno::XComponentContext > & rContext ); 223 224 void addExtension( const OUString &rExtensionURL, 225 const OUString &rRepository, 226 const bool bWarnUser ); 227 void removeExtension( const uno::Reference< deployment::XPackage > &rPackage ); 228 void enableExtension( const uno::Reference< deployment::XPackage > &rPackage, 229 const bool bEnable ); 230 void checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ); 231 void acceptLicense( const uno::Reference< deployment::XPackage > &rPackage ); 232 void stop(); 233 bool isBusy(); 234 235 static OUString searchAndReplaceAll( const OUString &rSource, 236 const OUString &rWhat, 237 const OUString &rWith ); 238 private: 239 Thread( Thread & ); // not defined 240 void operator =( Thread & ); // not defined 241 242 virtual ~Thread(); 243 244 virtual void execute(); 245 virtual void SAL_CALL onTerminated(); 246 247 void _addExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 248 const OUString &rPackageURL, 249 const OUString &rRepository, 250 const bool bWarnUser ); 251 void _removeExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 252 const uno::Reference< deployment::XPackage > &xPackage ); 253 void _enableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 254 const uno::Reference< deployment::XPackage > &xPackage ); 255 void _disableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 256 const uno::Reference< deployment::XPackage > &xPackage ); 257 void _checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ); 258 void _solar_checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ); 259 void _acceptLicense( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 260 const uno::Reference< deployment::XPackage > &xPackage ); 261 262 enum Input { NONE, START, STOP }; 263 264 uno::Reference< uno::XComponentContext > m_xContext; 265 std::queue< TExtensionCmd > m_queue; 266 267 DialogHelper *m_pDialogHelper; 268 TheExtensionManager *m_pManager; 269 270 const OUString m_sEnablingPackages; 271 const OUString m_sDisablingPackages; 272 const OUString m_sAddingPackages; 273 const OUString m_sRemovingPackages; 274 const OUString m_sDefaultCmd; 275 const OUString m_sAcceptLicense; 276 osl::Condition m_wakeup; 277 osl::Mutex m_mutex; 278 Input m_eInput; 279 bool m_bTerminated; 280 bool m_bStopped; 281 bool m_bWorking; 282 }; 283 284 //------------------------------------------------------------------------------ 285 void ProgressCmdEnv::startProgress() 286 { 287 m_nCurrentProgress = 0; 288 289 if ( m_pDialogHelper ) 290 m_pDialogHelper->showProgress( true ); 291 } 292 293 //------------------------------------------------------------------------------ 294 void ProgressCmdEnv::stopProgress() 295 { 296 if ( m_pDialogHelper ) 297 m_pDialogHelper->showProgress( false ); 298 } 299 300 //------------------------------------------------------------------------------ 301 void ProgressCmdEnv::progressSection( const OUString &rText, 302 const uno::Reference< task::XAbortChannel > &xAbortChannel ) 303 { 304 m_xAbortChannel = xAbortChannel; 305 if (! m_bAborted) 306 { 307 m_nCurrentProgress = 0; 308 if ( m_pDialogHelper ) 309 { 310 m_pDialogHelper->updateProgress( rText, xAbortChannel ); 311 m_pDialogHelper->updateProgress( 5 ); 312 } 313 } 314 } 315 316 //------------------------------------------------------------------------------ 317 void ProgressCmdEnv::updateProgress() 318 { 319 if ( ! m_bAborted ) 320 { 321 long nProgress = ((m_nCurrentProgress*5) % 100) + 5; 322 if ( m_pDialogHelper ) 323 m_pDialogHelper->updateProgress( nProgress ); 324 } 325 } 326 327 //------------------------------------------------------------------------------ 328 ProgressCmdEnv::~ProgressCmdEnv() 329 { 330 // TODO: stop all threads and wait 331 } 332 333 334 //------------------------------------------------------------------------------ 335 // XCommandEnvironment 336 //------------------------------------------------------------------------------ 337 uno::Reference< task::XInteractionHandler > ProgressCmdEnv::getInteractionHandler() 338 throw ( uno::RuntimeException ) 339 { 340 return this; 341 } 342 343 //------------------------------------------------------------------------------ 344 uno::Reference< ucb::XProgressHandler > ProgressCmdEnv::getProgressHandler() 345 throw ( uno::RuntimeException ) 346 { 347 return this; 348 } 349 350 //------------------------------------------------------------------------------ 351 namespace { 352 353 // The bodies below are reached from the deployment worker thread, where 354 // AquaSalInstance::Yield cannot pump the Cocoa event queue; a dialog executed there 355 // appears but never receives events. syncExecute() marshals them onto the main thread. 356 357 short solar_dependencyDialog( DialogHelper * pDialogHelper, 358 std::vector< OUString > const & rDeps ) 359 { 360 vos::OGuard guard( Application::GetSolarMutex() ); 361 return DependencyDialog( pDialogHelper ? pDialogHelper->getWindow() : NULL, rDeps ).Execute(); 362 } 363 364 void solar_unsupportedPlatformBox( DialogHelper * pDialogHelper, 365 OUString const & rDisplayName ) 366 { 367 vos::OGuard guard( Application::GetSolarMutex() ); 368 String sMsg( ResId( RID_STR_UNSUPPORTED_PLATFORM, *DeploymentGuiResMgr::get() ) ); 369 sMsg.SearchAndReplaceAllAscii( "%Name", rDisplayName ); 370 ErrorBox box( pDialogHelper ? pDialogHelper->getWindow() : NULL, WB_OK, sMsg ); 371 box.Execute(); 372 } 373 374 void solar_errorBox( DialogHelper * pDialogHelper, OUString const & rMsg, bool bSetTitle ) 375 { 376 vos::OGuard guard( Application::GetSolarMutex() ); 377 ErrorBox box( pDialogHelper ? pDialogHelper->getWindow() : NULL, WB_OK, rMsg ); 378 if ( bSetTitle && pDialogHelper ) 379 box.SetText( pDialogHelper->getWindow()->GetText() ); 380 box.Execute(); 381 } 382 383 } // anon namespace 384 385 //------------------------------------------------------------------------------ 386 // XInteractionHandler 387 //------------------------------------------------------------------------------ 388 389 void ProgressCmdEnv::handle( uno::Reference< task::XInteractionRequest > const & xRequest ) 390 throw ( uno::RuntimeException ) 391 { 392 uno::Any request( xRequest->getRequest() ); 393 OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION ); 394 dp_misc::TRACE( OUSTR("[dp_gui_cmdenv.cxx] incoming request:\n") 395 + ::comphelper::anyToString(request) + OUSTR("\n")); 396 397 lang::WrappedTargetException wtExc; 398 deployment::DependencyException depExc; 399 deployment::LicenseException licExc; 400 deployment::VersionException verExc; 401 deployment::InstallException instExc; 402 deployment::PlatformException platExc; 403 404 // selections: 405 bool approve = false; 406 bool abort = false; 407 408 if (request >>= wtExc) { 409 // handable deployment error signalled, e.g. 410 // bundle item registration failed, notify cause only: 411 uno::Any cause; 412 deployment::DeploymentException dpExc; 413 if (wtExc.TargetException >>= dpExc) 414 cause = dpExc.Cause; 415 else { 416 ucb::CommandFailedException cfExc; 417 if (wtExc.TargetException >>= cfExc) 418 cause = cfExc.Reason; 419 else 420 cause = wtExc.TargetException; 421 } 422 update_( cause ); 423 424 // ignore intermediate errors of legacy packages, i.e. 425 // former pkgchk behaviour: 426 const uno::Reference< deployment::XPackage > xPackage( wtExc.Context, uno::UNO_QUERY ); 427 OSL_ASSERT( xPackage.is() ); 428 if ( xPackage.is() ) 429 { 430 const uno::Reference< deployment::XPackageTypeInfo > xPackageType( xPackage->getPackageType() ); 431 OSL_ASSERT( xPackageType.is() ); 432 if (xPackageType.is()) 433 { 434 approve = ( xPackage->isBundle() && 435 xPackageType->getMediaType().matchAsciiL( 436 RTL_CONSTASCII_STRINGPARAM( 437 "application/" 438 "vnd.sun.star.legacy-package-bundle") )); 439 } 440 } 441 abort = !approve; 442 } 443 else if (request >>= depExc) 444 { 445 std::vector< rtl::OUString > deps; 446 for (sal_Int32 i = 0; i < depExc.UnsatisfiedDependencies.getLength(); 447 ++i) 448 { 449 deps.push_back( 450 dp_misc::Dependencies::getErrorText( depExc.UnsatisfiedDependencies[i]) ); 451 } 452 { 453 short n = vcl::solarthread::syncExecute( 454 boost::bind( &solar_dependencyDialog, m_pDialogHelper, deps ) ); 455 // Distinguish between closing the dialog and programatically 456 // canceling the dialog (headless VCL): 457 approve = n == RET_OK 458 || (n == RET_CANCEL && !Application::IsDialogCancelEnabled()); 459 } 460 } 461 else if (request >>= licExc) 462 { 463 uno::Reference< ui::dialogs::XExecutableDialog > xDialog( 464 deployment::ui::LicenseDialog::create( 465 m_xContext, VCLUnoHelper::GetInterface( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL ), 466 licExc.ExtensionName, licExc.Text ) ); 467 sal_Int16 res = xDialog->execute(); 468 if ( res == ui::dialogs::ExecutableDialogResults::CANCEL ) 469 abort = true; 470 else if ( res == ui::dialogs::ExecutableDialogResults::OK ) 471 approve = true; 472 else 473 { 474 OSL_ASSERT(0); 475 } 476 } 477 else if (request >>= verExc) 478 { 479 approve = handleVersionException( verExc, m_pDialogHelper ); 480 abort = !approve; 481 } 482 else if (request >>= instExc) 483 { 484 if ( ! m_bWarnUser ) 485 { 486 approve = true; 487 } 488 else 489 { 490 if ( m_pDialogHelper ) 491 { 492 vos::OGuard guard(Application::GetSolarMutex()); 493 494 approve = m_pDialogHelper->installExtensionWarn( instExc.displayName ); 495 } 496 else 497 approve = false; 498 abort = !approve; 499 } 500 } 501 else if (request >>= platExc) 502 { 503 vcl::solarthread::syncExecute( 504 boost::bind( &solar_unsupportedPlatformBox, m_pDialogHelper, 505 platExc.package->getDisplayName() ) ); 506 approve = true; 507 } 508 509 if (approve == false && abort == false) 510 { 511 // forward to UUI handler: 512 if (! m_xHandler.is()) { 513 // late init: 514 uno::Sequence< uno::Any > handlerArgs( 1 ); 515 handlerArgs[ 0 ] <<= beans::PropertyValue( 516 OUSTR("Context"), -1, uno::Any( m_sTitle ), 517 beans::PropertyState_DIRECT_VALUE ); 518 m_xHandler.set( m_xContext->getServiceManager() 519 ->createInstanceWithArgumentsAndContext( 520 OUSTR("com.sun.star.uui.InteractionHandler"), 521 handlerArgs, m_xContext ), uno::UNO_QUERY_THROW ); 522 } 523 m_xHandler->handle( xRequest ); 524 } 525 else 526 { 527 // select: 528 uno::Sequence< uno::Reference< task::XInteractionContinuation > > conts( 529 xRequest->getContinuations() ); 530 uno::Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray(); 531 sal_Int32 len = conts.getLength(); 532 for ( sal_Int32 pos = 0; pos < len; ++pos ) 533 { 534 if (approve) { 535 uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY ); 536 if (xInteractionApprove.is()) { 537 xInteractionApprove->select(); 538 // don't query again for ongoing continuations: 539 approve = false; 540 } 541 } 542 else if (abort) { 543 uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY ); 544 if (xInteractionAbort.is()) { 545 xInteractionAbort->select(); 546 // don't query again for ongoing continuations: 547 abort = false; 548 } 549 } 550 } 551 } 552 } 553 554 //------------------------------------------------------------------------------ 555 // XProgressHandler 556 //------------------------------------------------------------------------------ 557 void ProgressCmdEnv::push( uno::Any const & rStatus ) 558 throw( uno::RuntimeException ) 559 { 560 update_( rStatus ); 561 } 562 563 //------------------------------------------------------------------------------ 564 void ProgressCmdEnv::update_( uno::Any const & rStatus ) 565 throw( uno::RuntimeException ) 566 { 567 OUString text; 568 if ( rStatus.hasValue() && !( rStatus >>= text) ) 569 { 570 if ( rStatus.getValueTypeClass() == uno::TypeClass_EXCEPTION ) 571 text = static_cast< uno::Exception const *>( rStatus.getValue() )->Message; 572 if ( text.getLength() == 0 ) 573 text = ::comphelper::anyToString( rStatus ); // fallback 574 575 vcl::solarthread::syncExecute( 576 boost::bind( &solar_errorBox, m_pDialogHelper, text, false ) ); 577 } 578 ++m_nCurrentProgress; 579 updateProgress(); 580 } 581 582 //------------------------------------------------------------------------------ 583 void ProgressCmdEnv::update( uno::Any const & rStatus ) 584 throw( uno::RuntimeException ) 585 { 586 update_( rStatus ); 587 } 588 589 //------------------------------------------------------------------------------ 590 void ProgressCmdEnv::pop() 591 throw( uno::RuntimeException ) 592 { 593 update_( uno::Any() ); // no message 594 } 595 596 //------------------------------------------------------------------------------ 597 ExtensionCmdQueue::Thread::Thread( DialogHelper *pDialogHelper, 598 TheExtensionManager *pManager, 599 const uno::Reference< uno::XComponentContext > & rContext ) : 600 m_xContext( rContext ), 601 m_pDialogHelper( pDialogHelper ), 602 m_pManager( pManager ), 603 m_sEnablingPackages( DialogHelper::getResourceString( RID_STR_ENABLING_PACKAGES ) ), 604 m_sDisablingPackages( DialogHelper::getResourceString( RID_STR_DISABLING_PACKAGES ) ), 605 m_sAddingPackages( DialogHelper::getResourceString( RID_STR_ADDING_PACKAGES ) ), 606 m_sRemovingPackages( DialogHelper::getResourceString( RID_STR_REMOVING_PACKAGES ) ), 607 m_sDefaultCmd( DialogHelper::getResourceString( RID_STR_ADD_PACKAGES ) ), 608 m_sAcceptLicense( DialogHelper::getResourceString( RID_STR_ACCEPT_LICENSE ) ), 609 m_eInput( NONE ), 610 m_bTerminated( false ), 611 m_bStopped( false ), 612 m_bWorking( false ) 613 { 614 OSL_ASSERT( pDialogHelper ); 615 } 616 617 //------------------------------------------------------------------------------ 618 void ExtensionCmdQueue::Thread::addExtension( const ::rtl::OUString &rExtensionURL, 619 const ::rtl::OUString &rRepository, 620 const bool bWarnUser ) 621 { 622 ::osl::MutexGuard aGuard( m_mutex ); 623 624 //If someone called stop then we do not add the extension -> game over! 625 if ( m_bStopped ) 626 return; 627 628 if ( rExtensionURL.getLength() ) 629 { 630 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ADD, rExtensionURL, rRepository, bWarnUser ) ); 631 632 m_queue.push( pEntry ); 633 m_eInput = START; 634 m_wakeup.set(); 635 } 636 } 637 638 //------------------------------------------------------------------------------ 639 void ExtensionCmdQueue::Thread::removeExtension( const uno::Reference< deployment::XPackage > &rPackage ) 640 { 641 ::osl::MutexGuard aGuard( m_mutex ); 642 643 //If someone called stop then we do not remove the extension -> game over! 644 if ( m_bStopped ) 645 return; 646 647 if ( rPackage.is() ) 648 { 649 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::REMOVE, rPackage ) ); 650 651 m_queue.push( pEntry ); 652 m_eInput = START; 653 m_wakeup.set(); 654 } 655 } 656 657 //------------------------------------------------------------------------------ 658 void ExtensionCmdQueue::Thread::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage ) 659 { 660 ::osl::MutexGuard aGuard( m_mutex ); 661 662 //If someone called stop then we do not remove the extension -> game over! 663 if ( m_bStopped ) 664 return; 665 666 if ( rPackage.is() ) 667 { 668 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::ACCEPT_LICENSE, rPackage ) ); 669 670 m_queue.push( pEntry ); 671 m_eInput = START; 672 m_wakeup.set(); 673 } 674 } 675 676 //------------------------------------------------------------------------------ 677 void ExtensionCmdQueue::Thread::enableExtension( const uno::Reference< deployment::XPackage > &rPackage, 678 const bool bEnable ) 679 { 680 ::osl::MutexGuard aGuard( m_mutex ); 681 682 //If someone called stop then we do not remove the extension -> game over! 683 if ( m_bStopped ) 684 return; 685 686 if ( rPackage.is() ) 687 { 688 TExtensionCmd pEntry( new ExtensionCmd( bEnable ? ExtensionCmd::ENABLE : 689 ExtensionCmd::DISABLE, 690 rPackage ) ); 691 m_queue.push( pEntry ); 692 m_eInput = START; 693 m_wakeup.set(); 694 } 695 } 696 697 //------------------------------------------------------------------------------ 698 void ExtensionCmdQueue::Thread::checkForUpdates( 699 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ) 700 { 701 ::osl::MutexGuard aGuard( m_mutex ); 702 703 //If someone called stop then we do not update the extension -> game over! 704 if ( m_bStopped ) 705 return; 706 707 TExtensionCmd pEntry( new ExtensionCmd( ExtensionCmd::CHECK_FOR_UPDATES, vExtensionList ) ); 708 m_queue.push( pEntry ); 709 m_eInput = START; 710 m_wakeup.set(); 711 } 712 713 //------------------------------------------------------------------------------ 714 //Stopping this thread will not abort the installation of extensions. 715 void ExtensionCmdQueue::Thread::stop() 716 { 717 osl::MutexGuard aGuard( m_mutex ); 718 m_bStopped = true; 719 m_eInput = STOP; 720 m_wakeup.set(); 721 } 722 723 //------------------------------------------------------------------------------ 724 bool ExtensionCmdQueue::Thread::isBusy() 725 { 726 osl::MutexGuard aGuard( m_mutex ); 727 return m_bWorking; 728 } 729 730 //------------------------------------------------------------------------------ 731 ExtensionCmdQueue::Thread::~Thread() {} 732 733 //------------------------------------------------------------------------------ 734 void ExtensionCmdQueue::Thread::execute() 735 { 736 #ifdef WNT 737 //Needed for use of the service "com.sun.star.system.SystemShellExecute" in 738 //DialogHelper::openWebBrowser 739 CoUninitialize(); 740 HRESULT r = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 741 #endif 742 for (;;) 743 { 744 if ( m_wakeup.wait() != osl::Condition::result_ok ) 745 { 746 dp_misc::TRACE( "dp_gui::ExtensionCmdQueue::Thread::run: ignored " 747 "osl::Condition::wait failure\n" ); 748 } 749 m_wakeup.reset(); 750 751 int nSize; 752 Input eInput; 753 { 754 osl::MutexGuard aGuard( m_mutex ); 755 eInput = m_eInput; 756 m_eInput = NONE; 757 nSize = m_queue.size(); 758 m_bWorking = false; 759 } 760 761 // If this thread has been woken up by anything else except start, stop 762 // then input is NONE and we wait again. 763 // We only install the extension which are currently in the queue. 764 // The progressbar will be set to show the progress of the current number 765 // of extensions. If we allowed to add extensions now then the progressbar may 766 // have reached the end while we still install newly added extensions. 767 if ( ( eInput == NONE ) || ( nSize == 0 ) ) 768 continue; 769 if ( eInput == STOP ) 770 break; 771 772 ::rtl::Reference< ProgressCmdEnv > currentCmdEnv( new ProgressCmdEnv( m_xContext, m_pDialogHelper, m_sDefaultCmd ) ); 773 774 // Do not lock the following part with addExtension. addExtension may be called in the main thread. 775 // If the message box "Do you want to install the extension (or similar)" is shown and then 776 // addExtension is called, which then blocks the main thread, then we deadlock. 777 bool bStartProgress = true; 778 779 while ( !currentCmdEnv->isAborted() && --nSize >= 0 ) 780 { 781 { 782 osl::MutexGuard aGuard( m_mutex ); 783 m_bWorking = true; 784 } 785 786 try 787 { 788 TExtensionCmd pEntry; 789 { 790 ::osl::MutexGuard queueGuard( m_mutex ); 791 pEntry = m_queue.front(); 792 m_queue.pop(); 793 } 794 795 if ( bStartProgress && ( pEntry->m_eCmdType != ExtensionCmd::CHECK_FOR_UPDATES ) ) 796 { 797 currentCmdEnv->startProgress(); 798 bStartProgress = false; 799 } 800 801 switch ( pEntry->m_eCmdType ) { 802 case ExtensionCmd::ADD : 803 _addExtension( currentCmdEnv, pEntry->m_sExtensionURL, pEntry->m_sRepository, pEntry->m_bWarnUser ); 804 break; 805 case ExtensionCmd::REMOVE : 806 _removeExtension( currentCmdEnv, pEntry->m_xPackage ); 807 break; 808 case ExtensionCmd::ENABLE : 809 _enableExtension( currentCmdEnv, pEntry->m_xPackage ); 810 break; 811 case ExtensionCmd::DISABLE : 812 _disableExtension( currentCmdEnv, pEntry->m_xPackage ); 813 break; 814 case ExtensionCmd::CHECK_FOR_UPDATES : 815 _checkForUpdates( pEntry->m_vExtensionList ); 816 break; 817 case ExtensionCmd::ACCEPT_LICENSE : 818 _acceptLicense( currentCmdEnv, pEntry->m_xPackage ); 819 break; 820 } 821 } 822 //catch ( deployment::DeploymentException &) 823 //{ 824 //} 825 //catch ( lang::IllegalArgumentException &) 826 //{ 827 //} 828 catch ( ucb::CommandAbortedException & ) 829 { 830 //This exception is thrown when the user clicks cancel on the progressbar. 831 //Then we cancel the installation of all extensions and remove them from 832 //the queue. 833 { 834 ::osl::MutexGuard queueGuard2(m_mutex); 835 while ( --nSize >= 0 ) 836 m_queue.pop(); 837 } 838 break; 839 } 840 catch ( ucb::CommandFailedException & ) 841 { 842 //This exception is thrown when a user clicked cancel in the messagebox which was 843 //startet by the interaction handler. For example the user will be asked if he/she 844 //really wants to install the extension. 845 //These interaction are run for exectly one extension at a time. Therefore we continue 846 //with installing the remaining extensions. 847 continue; 848 } 849 catch ( uno::Exception & ) 850 { 851 //Todo display the user an error 852 //see also DialogImpl::SyncPushButton::Click() 853 uno::Any exc( ::cppu::getCaughtException() ); 854 OUString msg; 855 deployment::DeploymentException dpExc; 856 if ((exc >>= dpExc) && 857 dpExc.Cause.getValueTypeClass() == uno::TypeClass_EXCEPTION) 858 { 859 // notify error cause only: 860 msg = reinterpret_cast< uno::Exception const * >( dpExc.Cause.getValue() )->Message; 861 } 862 if (msg.getLength() == 0) // fallback for debugging purposes 863 msg = ::comphelper::anyToString(exc); 864 865 vcl::solarthread::syncExecute( 866 boost::bind( &solar_errorBox, m_pDialogHelper, msg, true ) ); 867 //Continue with installation of the remaining extensions 868 } 869 { 870 osl::MutexGuard aGuard( m_mutex ); 871 m_bWorking = false; 872 } 873 } 874 875 { 876 // when leaving the while loop with break, we should set working to false, too 877 osl::MutexGuard aGuard( m_mutex ); 878 m_bWorking = false; 879 } 880 881 if ( !bStartProgress ) 882 currentCmdEnv->stopProgress(); 883 } 884 //end for 885 //enable all buttons 886 // m_pDialog->m_bAddingExtensions = false; 887 // m_pDialog->updateButtonStates(); 888 #ifdef WNT 889 CoUninitialize(); 890 #endif 891 } 892 893 //------------------------------------------------------------------------------ 894 void ExtensionCmdQueue::Thread::_addExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 895 const OUString &rPackageURL, 896 const OUString &rRepository, 897 const bool bWarnUser ) 898 { 899 //check if we have a string in anyTitle. For example "unopkg gui \" caused anyTitle to be void 900 //and anyTitle.get<OUString> throws as RuntimeException. 901 uno::Any anyTitle; 902 try 903 { 904 anyTitle = ::ucbhelper::Content( rPackageURL, rCmdEnv.get() ).getPropertyValue( OUSTR("Title") ); 905 } 906 catch ( uno::Exception & ) 907 { 908 return; 909 } 910 911 OUString sName; 912 if ( ! (anyTitle >>= sName) ) 913 { 914 OSL_ENSURE(0, "Could not get file name for extension."); 915 return; 916 } 917 918 rCmdEnv->setWarnUser( bWarnUser ); 919 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager(); 920 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() ); 921 OUString sTitle = searchAndReplaceAll( m_sAddingPackages, OUSTR("%EXTENSION_NAME"), sName ); 922 rCmdEnv->progressSection( sTitle, xAbortChannel ); 923 924 try 925 { 926 xExtMgr->addExtension(rPackageURL, uno::Sequence<beans::NamedValue>(), 927 rRepository, xAbortChannel, rCmdEnv.get() ); 928 } 929 catch ( ucb::CommandFailedException & ) 930 { 931 // When the extension is already installed we'll get a dialog asking if we want to overwrite. If we then press 932 // cancel this exception is thrown. 933 } 934 catch ( ucb::CommandAbortedException & ) 935 { 936 // User clicked the cancel button 937 // TODO: handle cancel 938 } 939 rCmdEnv->setWarnUser( false ); 940 } 941 942 //------------------------------------------------------------------------------ 943 void ExtensionCmdQueue::Thread::_removeExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 944 const uno::Reference< deployment::XPackage > &xPackage ) 945 { 946 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager(); 947 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() ); 948 OUString sTitle = searchAndReplaceAll( m_sRemovingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() ); 949 rCmdEnv->progressSection( sTitle, xAbortChannel ); 950 951 OUString id( dp_misc::getIdentifier( xPackage ) ); 952 try 953 { 954 xExtMgr->removeExtension( id, xPackage->getName(), xPackage->getRepositoryName(), xAbortChannel, rCmdEnv.get() ); 955 } 956 catch ( deployment::DeploymentException & ) 957 {} 958 catch ( ucb::CommandFailedException & ) 959 {} 960 catch ( ucb::CommandAbortedException & ) 961 {} 962 963 // Check, if there are still updates to be notified via menu bar icon 964 uno::Sequence< uno::Sequence< rtl::OUString > > aItemList; 965 UpdateDialog::createNotifyJob( false, aItemList ); 966 } 967 968 //------------------------------------------------------------------------------ 969 void ExtensionCmdQueue::Thread::_checkForUpdates( 970 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ) 971 { 972 // Dialog::Execute() must run on the main thread; on Mac OS X only that thread 973 // pumps the Cocoa event queue (AquaSalInstance::Yield), so executing the 974 // update dialog on this worker thread would leave it visible but frozen. 975 vcl::solarthread::syncExecute( 976 boost::bind( &ExtensionCmdQueue::Thread::_solar_checkForUpdates, 977 this, vExtensionList ) ); 978 } 979 980 //------------------------------------------------------------------------------ 981 void ExtensionCmdQueue::Thread::_solar_checkForUpdates( 982 const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ) 983 { 984 UpdateDialog* pUpdateDialog; 985 std::vector< UpdateData > vData; 986 987 pUpdateDialog = new UpdateDialog( m_xContext, m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, vExtensionList, &vData ); 988 989 pUpdateDialog->notifyMenubar( true, false ); // prepare the checking, if there updates to be notified via menu bar icon 990 991 if ( ( pUpdateDialog->Execute() == RET_OK ) && !vData.empty() ) 992 { 993 // If there is at least one directly downloadable dialog then we 994 // open the install dialog. 995 ::std::vector< UpdateData > dataDownload; 996 int countWebsiteDownload = 0; 997 typedef std::vector< dp_gui::UpdateData >::const_iterator cit; 998 999 for ( cit i = vData.begin(); i < vData.end(); i++ ) 1000 { 1001 if ( i->sWebsiteURL.getLength() > 0 ) 1002 countWebsiteDownload ++; 1003 else 1004 dataDownload.push_back( *i ); 1005 } 1006 1007 short nDialogResult = RET_OK; 1008 if ( !dataDownload.empty() ) 1009 { 1010 nDialogResult = UpdateInstallDialog( m_pDialogHelper? m_pDialogHelper->getWindow() : NULL, dataDownload, m_xContext ).Execute(); 1011 pUpdateDialog->notifyMenubar( false, true ); // Check, if there are still pending updates to be notified via menu bar icon 1012 } 1013 else 1014 pUpdateDialog->notifyMenubar( false, false ); // Check, if there are pending updates to be notified via menu bar icon 1015 1016 //Now start the webbrowser and navigate to the websites where we get the updates 1017 if ( RET_OK == nDialogResult ) 1018 { 1019 for ( cit i = vData.begin(); i < vData.end(); i++ ) 1020 { 1021 if ( m_pDialogHelper && ( i->sWebsiteURL.getLength() > 0 ) ) 1022 m_pDialogHelper->openWebBrowser( i->sWebsiteURL, m_pDialogHelper->getWindow()->GetText() ); 1023 } 1024 } 1025 } 1026 else 1027 pUpdateDialog->notifyMenubar( false, false ); // check if there updates to be notified via menu bar icon 1028 1029 delete pUpdateDialog; 1030 } 1031 1032 //------------------------------------------------------------------------------ 1033 void ExtensionCmdQueue::Thread::_enableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 1034 const uno::Reference< deployment::XPackage > &xPackage ) 1035 { 1036 if ( !xPackage.is() ) 1037 return; 1038 1039 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager(); 1040 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() ); 1041 OUString sTitle = searchAndReplaceAll( m_sEnablingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() ); 1042 rCmdEnv->progressSection( sTitle, xAbortChannel ); 1043 1044 try 1045 { 1046 xExtMgr->enableExtension( xPackage, xAbortChannel, rCmdEnv.get() ); 1047 if ( m_pDialogHelper ) 1048 m_pDialogHelper->updatePackageInfo( xPackage ); 1049 } 1050 catch ( ::ucb::CommandAbortedException & ) 1051 {} 1052 } 1053 1054 //------------------------------------------------------------------------------ 1055 void ExtensionCmdQueue::Thread::_disableExtension( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 1056 const uno::Reference< deployment::XPackage > &xPackage ) 1057 { 1058 if ( !xPackage.is() ) 1059 return; 1060 1061 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager(); 1062 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() ); 1063 OUString sTitle = searchAndReplaceAll( m_sDisablingPackages, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() ); 1064 rCmdEnv->progressSection( sTitle, xAbortChannel ); 1065 1066 try 1067 { 1068 xExtMgr->disableExtension( xPackage, xAbortChannel, rCmdEnv.get() ); 1069 if ( m_pDialogHelper ) 1070 m_pDialogHelper->updatePackageInfo( xPackage ); 1071 } 1072 catch ( ::ucb::CommandAbortedException & ) 1073 {} 1074 } 1075 1076 //------------------------------------------------------------------------------ 1077 void ExtensionCmdQueue::Thread::_acceptLicense( ::rtl::Reference< ProgressCmdEnv > &rCmdEnv, 1078 const uno::Reference< deployment::XPackage > &xPackage ) 1079 { 1080 if ( !xPackage.is() ) 1081 return; 1082 1083 uno::Reference< deployment::XExtensionManager > xExtMgr = m_pManager->getExtensionManager(); 1084 uno::Reference< task::XAbortChannel > xAbortChannel( xExtMgr->createAbortChannel() ); 1085 OUString sTitle = searchAndReplaceAll( m_sAcceptLicense, OUSTR("%EXTENSION_NAME"), xPackage->getDisplayName() ); 1086 rCmdEnv->progressSection( sTitle, xAbortChannel ); 1087 1088 try 1089 { 1090 xExtMgr->checkPrerequisitesAndEnable( xPackage, xAbortChannel, rCmdEnv.get() ); 1091 if ( m_pDialogHelper ) 1092 m_pDialogHelper->updatePackageInfo( xPackage ); 1093 } 1094 catch ( ::ucb::CommandAbortedException & ) 1095 {} 1096 } 1097 1098 //------------------------------------------------------------------------------ 1099 void ExtensionCmdQueue::Thread::onTerminated() 1100 { 1101 ::osl::MutexGuard g(m_mutex); 1102 m_bTerminated = true; 1103 } 1104 1105 //------------------------------------------------------------------------------ 1106 OUString ExtensionCmdQueue::Thread::searchAndReplaceAll( const OUString &rSource, 1107 const OUString &rWhat, 1108 const OUString &rWith ) 1109 { 1110 OUString aRet( rSource ); 1111 sal_Int32 nLen = rWhat.getLength(); 1112 1113 if ( !nLen ) 1114 return aRet; 1115 1116 sal_Int32 nIndex = rSource.indexOf( rWhat ); 1117 while ( nIndex != -1 ) 1118 { 1119 aRet = aRet.replaceAt( nIndex, nLen, rWith ); 1120 nIndex = aRet.indexOf( rWhat, nIndex + rWith.getLength() ); 1121 } 1122 return aRet; 1123 } 1124 1125 1126 //------------------------------------------------------------------------------ 1127 //------------------------------------------------------------------------------ 1128 //------------------------------------------------------------------------------ 1129 ExtensionCmdQueue::ExtensionCmdQueue( DialogHelper * pDialogHelper, 1130 TheExtensionManager *pManager, 1131 const uno::Reference< uno::XComponentContext > &rContext ) 1132 : m_thread( new Thread( pDialogHelper, pManager, rContext ) ) 1133 { 1134 m_thread->launch(); 1135 } 1136 1137 ExtensionCmdQueue::~ExtensionCmdQueue() { 1138 stop(); 1139 } 1140 1141 void ExtensionCmdQueue::addExtension( const ::rtl::OUString & extensionURL, 1142 const ::rtl::OUString & repository, 1143 const bool bWarnUser ) 1144 { 1145 m_thread->addExtension( extensionURL, repository, bWarnUser ); 1146 } 1147 1148 void ExtensionCmdQueue::removeExtension( const uno::Reference< deployment::XPackage > &rPackage ) 1149 { 1150 m_thread->removeExtension( rPackage ); 1151 } 1152 1153 void ExtensionCmdQueue::enableExtension( const uno::Reference< deployment::XPackage > &rPackage, 1154 const bool bEnable ) 1155 { 1156 m_thread->enableExtension( rPackage, bEnable ); 1157 } 1158 1159 void ExtensionCmdQueue::checkForUpdates( const std::vector<uno::Reference<deployment::XPackage > > &vExtensionList ) 1160 { 1161 m_thread->checkForUpdates( vExtensionList ); 1162 } 1163 1164 void ExtensionCmdQueue::acceptLicense( const uno::Reference< deployment::XPackage > &rPackage ) 1165 { 1166 m_thread->acceptLicense( rPackage ); 1167 } 1168 1169 void ExtensionCmdQueue::syncRepositories( const uno::Reference< uno::XComponentContext > &xContext ) 1170 { 1171 dp_misc::syncRepositories( new ProgressCmdEnv( xContext, NULL, OUSTR("Extension Manager") ) ); 1172 } 1173 1174 void ExtensionCmdQueue::stop() 1175 { 1176 m_thread->stop(); 1177 } 1178 1179 bool ExtensionCmdQueue::isBusy() 1180 { 1181 return m_thread->isBusy(); 1182 } 1183 1184 void handleInteractionRequest( const uno::Reference< uno::XComponentContext > & xContext, 1185 const uno::Reference< task::XInteractionRequest > & xRequest ) 1186 { 1187 ::rtl::Reference< ProgressCmdEnv > xCmdEnv( new ProgressCmdEnv( xContext, NULL, OUSTR("Extension Manager") ) ); 1188 xCmdEnv->handle( xRequest ); 1189 } 1190 1191 } //namespace dp_gui 1192