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