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