xref: /trunk/main/desktop/source/deployment/gui/dp_gui_extensioncmdqueue.cxx (revision 4cf5e4efbe93245353abf5e77db4069f4ae95c00)
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