1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_desktop.hxx"
30 
31 #include "vcl/svapp.hxx"
32 #include "vcl/msgbox.hxx"
33 
34 #include "vos/mutex.hxx"
35 
36 #include "toolkit/helper/vclunohelper.hxx"
37 
38 #include "com/sun/star/beans/XPropertySet.hpp"
39 
40 #include "dp_gui_dialog2.hxx"
41 #include "dp_gui_extensioncmdqueue.hxx"
42 #include "dp_gui_theextmgr.hxx"
43 #include "dp_gui_theextmgr.hxx"
44 #include "dp_identifier.hxx"
45 #include "dp_update.hxx"
46 
47 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) )
48 
49 #define USER_PACKAGE_MANAGER    OUSTR("user")
50 #define SHARED_PACKAGE_MANAGER  OUSTR("shared")
51 #define BUNDLED_PACKAGE_MANAGER OUSTR("bundled")
52 
53 using namespace ::com::sun::star;
54 using ::rtl::OUString;
55 
56 namespace dp_gui {
57 
58 //------------------------------------------------------------------------------
59 
60 ::rtl::Reference< TheExtensionManager > TheExtensionManager::s_ExtMgr;
61 
62 //------------------------------------------------------------------------------
63 //                             TheExtensionManager
64 //------------------------------------------------------------------------------
65 
66 TheExtensionManager::TheExtensionManager( Window *pParent,
67                                           const uno::Reference< uno::XComponentContext > &xContext ) :
68     m_xContext( xContext ),
69     m_pParent( pParent ),
70     m_pExtMgrDialog( NULL ),
71     m_pUpdReqDialog( NULL ),
72     m_pExecuteCmdQueue( NULL )
73 {
74     m_xExtensionManager = deployment::ExtensionManager::get( xContext );
75     m_xExtensionManager->addModifyListener( this );
76 
77     uno::Reference< lang::XMultiServiceFactory > xConfig(
78         xContext->getServiceManager()->createInstanceWithContext(
79             OUSTR("com.sun.star.configuration.ConfigurationProvider"), xContext ), uno::UNO_QUERY_THROW);
80     uno::Any args[1];
81     beans::PropertyValue aValue( OUSTR("nodepath"), 0, uno::Any( OUSTR("/org.openoffice.Office.OptionsDialog/Nodes") ),
82                                  beans::PropertyState_DIRECT_VALUE );
83     args[0] <<= aValue;
84     m_xNameAccessNodes = uno::Reference< container::XNameAccess >(
85         xConfig->createInstanceWithArguments( OUSTR("com.sun.star.configuration.ConfigurationAccess"),
86                                               uno::Sequence< uno::Any >( args, 1 )), uno::UNO_QUERY_THROW);
87 
88     // get the 'get more extensions here' url
89     uno::Reference< container::XNameAccess > xNameAccessRepositories;
90     beans::PropertyValue aValue2( OUSTR("nodepath"), 0, uno::Any( OUSTR("/org.openoffice.Office.ExtensionManager/ExtensionRepositories") ),
91                                   beans::PropertyState_DIRECT_VALUE );
92     args[0] <<= aValue2;
93     xNameAccessRepositories = uno::Reference< container::XNameAccess > (
94         xConfig->createInstanceWithArguments( OUSTR("com.sun.star.configuration.ConfigurationAccess"),
95                                               uno::Sequence< uno::Any >( args, 1 )), uno::UNO_QUERY_THROW);
96     try
97     {   //throws css::container::NoSuchElementException, css::lang::WrappedTargetException
98         uno::Any value = xNameAccessRepositories->getByName( OUSTR( "WebsiteLink" ) );
99         m_sGetExtensionsURL = value.get< OUString > ();
100      }
101     catch ( uno::Exception& )
102     {}
103 
104     if ( dp_misc::office_is_running() )
105     {
106 		// the registration should be done after the construction has been ended
107 		// otherwise an exception prevents object creation, but it is registered as a listener
108         m_xDesktop.set( xContext->getServiceManager()->createInstanceWithContext(
109                             OUSTR("com.sun.star.frame.Desktop"), xContext ), uno::UNO_QUERY );
110         if ( m_xDesktop.is() )
111             m_xDesktop->addTerminateListener( this );
112     }
113 }
114 
115 //------------------------------------------------------------------------------
116 TheExtensionManager::~TheExtensionManager()
117 {
118     if ( m_pUpdReqDialog )
119         delete m_pUpdReqDialog;
120     if ( m_pExtMgrDialog )
121         delete m_pExtMgrDialog;
122     if ( m_pExecuteCmdQueue )
123         delete m_pExecuteCmdQueue;
124 }
125 
126 //------------------------------------------------------------------------------
127 void TheExtensionManager::createDialog( const bool bCreateUpdDlg )
128 {
129     const ::vos::OGuard guard( Application::GetSolarMutex() );
130 
131     if ( bCreateUpdDlg )
132     {
133         if ( !m_pUpdReqDialog )
134         {
135             m_pUpdReqDialog = new UpdateRequiredDialog( NULL, this );
136             delete m_pExecuteCmdQueue;
137             m_pExecuteCmdQueue = new ExtensionCmdQueue( (DialogHelper*) m_pUpdReqDialog, this, m_xContext );
138             createPackageList();
139         }
140     }
141     else if ( !m_pExtMgrDialog )
142     {
143         m_pExtMgrDialog = new ExtMgrDialog( m_pParent, this );
144         delete m_pExecuteCmdQueue;
145         m_pExecuteCmdQueue = new ExtensionCmdQueue( (DialogHelper*) m_pExtMgrDialog, this, m_xContext );
146         m_pExtMgrDialog->setGetExtensionsURL( m_sGetExtensionsURL );
147         createPackageList();
148     }
149 }
150 
151 //------------------------------------------------------------------------------
152 void TheExtensionManager::Show()
153 {
154     const ::vos::OGuard guard( Application::GetSolarMutex() );
155 
156     getDialog()->Show();
157 }
158 
159 //------------------------------------------------------------------------------
160 void TheExtensionManager::SetText( const ::rtl::OUString &rTitle )
161 {
162     const ::vos::OGuard guard( Application::GetSolarMutex() );
163 
164     getDialog()->SetText( rTitle );
165 }
166 
167 //------------------------------------------------------------------------------
168 void TheExtensionManager::ToTop( sal_uInt16 nFlags )
169 {
170     const ::vos::OGuard guard( Application::GetSolarMutex() );
171 
172     getDialog()->ToTop( nFlags );
173 }
174 
175 //------------------------------------------------------------------------------
176 bool TheExtensionManager::Close()
177 {
178     if ( m_pExtMgrDialog )
179         return m_pExtMgrDialog->Close();
180     else if ( m_pUpdReqDialog )
181         return m_pUpdReqDialog->Close();
182     else
183         return true;
184 }
185 
186 //------------------------------------------------------------------------------
187 sal_Int16 TheExtensionManager::execute()
188 {
189     sal_Int16 nRet = 0;
190 
191     if ( m_pUpdReqDialog )
192     {
193         nRet = m_pUpdReqDialog->Execute();
194         delete m_pUpdReqDialog;
195         m_pUpdReqDialog = NULL;
196     }
197 
198     return nRet;
199 }
200 
201 //------------------------------------------------------------------------------
202 bool TheExtensionManager::isVisible()
203 {
204     return getDialog()->IsVisible();
205 }
206 
207 //------------------------------------------------------------------------------
208 bool TheExtensionManager::checkUpdates( bool /* bShowUpdateOnly */, bool /*bParentVisible*/ )
209 {
210     std::vector< uno::Reference< deployment::XPackage >  > vEntries;
211     uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
212 
213     try {
214         xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
215                                                               uno::Reference< ucb::XCommandEnvironment >() );
216     } catch ( deployment::DeploymentException & ) {
217         return false;
218     } catch ( ucb::CommandFailedException & ) {
219         return false;
220     } catch ( ucb::CommandAbortedException & ) {
221         return false;
222     } catch ( lang::IllegalArgumentException & e ) {
223         throw uno::RuntimeException( e.Message, e.Context );
224     }
225 
226     for ( sal_Int32 i = 0; i < xAllPackages.getLength(); ++i )
227     {
228 		uno::Reference< deployment::XPackage > xPackage = dp_misc::getExtensionWithHighestVersion(xAllPackages[i]);
229 		OSL_ASSERT(xPackage.is());
230         if ( xPackage.is() )
231         {
232 			vEntries.push_back( xPackage );
233         }
234     }
235 
236     m_pExecuteCmdQueue->checkForUpdates( vEntries );
237     return true;
238 }
239 
240 //------------------------------------------------------------------------------
241 bool TheExtensionManager::installPackage( const OUString &rPackageURL, bool bWarnUser )
242 {
243     if ( rPackageURL.getLength() == 0 )
244         return false;
245 
246     createDialog( false );
247 
248     bool bInstall = true;
249     bool bInstallForAll = false;
250 
251     // DV! missing function is read only repository from extension manager
252     if ( !bWarnUser && ! m_xExtensionManager->isReadOnlyRepository( SHARED_PACKAGE_MANAGER ) )
253         bInstall = getDialogHelper()->installForAllUsers( bInstallForAll );
254 
255     if ( !bInstall )
256         return false;
257 
258     if ( bInstallForAll )
259         m_pExecuteCmdQueue->addExtension( rPackageURL, SHARED_PACKAGE_MANAGER, false );
260     else
261         m_pExecuteCmdQueue->addExtension( rPackageURL, USER_PACKAGE_MANAGER, bWarnUser );
262 
263     return true;
264 }
265 
266 //------------------------------------------------------------------------------
267 bool TheExtensionManager::queryTermination()
268 {
269     if ( dp_misc::office_is_running() )
270         return true;
271     // the standalone application unopkg must not close ( and quit ) the dialog
272     // when there are still actions in the queue
273     return true;
274 }
275 
276 //------------------------------------------------------------------------------
277 void TheExtensionManager::terminateDialog()
278 {
279     if ( ! dp_misc::office_is_running() )
280     {
281         const ::vos::OGuard guard( Application::GetSolarMutex() );
282         delete m_pExtMgrDialog;
283         m_pExtMgrDialog = NULL;
284         delete m_pUpdReqDialog;
285         m_pUpdReqDialog = NULL;
286         Application::Quit();
287     }
288 }
289 
290 //------------------------------------------------------------------------------
291 void TheExtensionManager::createPackageList()
292 {
293     uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
294 
295     try {
296         xAllPackages = m_xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
297                                                               uno::Reference< ucb::XCommandEnvironment >() );
298     } catch ( deployment::DeploymentException & ) {
299         return;
300     } catch ( ucb::CommandFailedException & ) {
301         return;
302     } catch ( ucb::CommandAbortedException & ) {
303         return;
304     } catch ( lang::IllegalArgumentException & e ) {
305         throw uno::RuntimeException( e.Message, e.Context );
306     }
307 
308     for ( sal_Int32 i = 0; i < xAllPackages.getLength(); ++i )
309     {
310         uno::Sequence< uno::Reference< deployment::XPackage > > xPackageList = xAllPackages[i];
311 
312         for ( sal_Int32 j = 0; j < xPackageList.getLength(); ++j )
313         {
314             uno::Reference< deployment::XPackage > xPackage = xPackageList[j];
315             if ( xPackage.is() )
316             {
317                 PackageState eState = getPackageState( xPackage );
318                 getDialogHelper()->addPackageToList( xPackage );
319                 // When the package is enabled, we can stop here, otherwise we have to look for
320                 // another version of this package
321                 if ( ( eState == REGISTERED ) || ( eState == NOT_AVAILABLE ) )
322                     break;
323             }
324         }
325     }
326 
327     uno::Sequence< uno::Reference< deployment::XPackage > > xNoLicPackages;
328     xNoLicPackages = m_xExtensionManager->getExtensionsWithUnacceptedLicenses( SHARED_PACKAGE_MANAGER,
329                                                                                uno::Reference< ucb::XCommandEnvironment >() );
330     for ( sal_Int32 i = 0; i < xNoLicPackages.getLength(); ++i )
331     {
332         uno::Reference< deployment::XPackage > xPackage = xNoLicPackages[i];
333         if ( xPackage.is() )
334         {
335             getDialogHelper()->addPackageToList( xPackage, true );
336         }
337     }
338 }
339 
340 //------------------------------------------------------------------------------
341 PackageState TheExtensionManager::getPackageState( const uno::Reference< deployment::XPackage > &xPackage ) const
342 {
343     try {
344         beans::Optional< beans::Ambiguous< sal_Bool > > option(
345             xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
346                                     uno::Reference< ucb::XCommandEnvironment >() ) );
347         if ( option.IsPresent )
348         {
349             ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
350             if ( reg.IsAmbiguous )
351                 return AMBIGUOUS;
352             else
353                 return reg.Value ? REGISTERED : NOT_REGISTERED;
354         }
355         else
356             return NOT_AVAILABLE;
357     }
358     catch ( uno::RuntimeException & ) {
359         throw;
360     }
361     catch ( uno::Exception & exc) {
362         (void) exc;
363         OSL_ENSURE( 0, ::rtl::OUStringToOString( exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
364         return NOT_AVAILABLE;
365     }
366 }
367 
368 //------------------------------------------------------------------------------
369 bool TheExtensionManager::isReadOnly( const uno::Reference< deployment::XPackage > &xPackage ) const
370 {
371     if ( m_xExtensionManager.is() && xPackage.is() )
372     {
373         return m_xExtensionManager->isReadOnlyRepository( xPackage->getRepositoryName() );
374     }
375     else
376         return true;
377 }
378 
379 //------------------------------------------------------------------------------
380 // The function investigates if the extension supports options.
381 bool TheExtensionManager::supportsOptions( const uno::Reference< deployment::XPackage > &xPackage ) const
382 {
383     bool bOptions = false;
384 
385     if ( ! xPackage->isBundle() )
386         return false;
387 
388     beans::Optional< OUString > aId = xPackage->getIdentifier();
389 
390     //a bundle must always have an id
391     OSL_ASSERT( aId.IsPresent );
392 
393     //iterate over all available nodes
394     uno::Sequence< OUString > seqNames = m_xNameAccessNodes->getElementNames();
395 
396     for ( int i = 0; i < seqNames.getLength(); i++ )
397     {
398         uno::Any anyNode = m_xNameAccessNodes->getByName( seqNames[i] );
399         //If we have a node then then it must contain the set of leaves. This is part of OptionsDialog.xcs
400         uno::Reference< XInterface> xIntNode = anyNode.get< uno::Reference< XInterface > >();
401         uno::Reference< container::XNameAccess > xNode( xIntNode, uno::UNO_QUERY_THROW );
402 
403         uno::Any anyLeaves = xNode->getByName( OUSTR("Leaves") );
404         uno::Reference< XInterface > xIntLeaves = anyLeaves.get< uno::Reference< XInterface > >();
405         uno::Reference< container::XNameAccess > xLeaves( xIntLeaves, uno::UNO_QUERY_THROW );
406 
407         //iterate over all available leaves
408         uno::Sequence< OUString > seqLeafNames = xLeaves->getElementNames();
409         for ( int j = 0; j < seqLeafNames.getLength(); j++ )
410         {
411             uno::Any anyLeaf = xLeaves->getByName( seqLeafNames[j] );
412             uno::Reference< XInterface > xIntLeaf = anyLeaf.get< uno::Reference< XInterface > >();
413             uno::Reference< beans::XPropertySet > xLeaf( xIntLeaf, uno::UNO_QUERY_THROW );
414             //investigate the Id property if it matches the extension identifier which
415             //has been passed in.
416             uno::Any anyValue = xLeaf->getPropertyValue( OUSTR("Id") );
417 
418             OUString sId = anyValue.get< OUString >();
419             if ( sId == aId.Value )
420             {
421                 bOptions = true;
422                 break;
423             }
424         }
425         if ( bOptions )
426             break;
427     }
428     return bOptions;
429 }
430 
431 //------------------------------------------------------------------------------
432 // XEventListener
433 void TheExtensionManager::disposing( lang::EventObject const & rEvt )
434     throw ( uno::RuntimeException )
435 {
436     bool shutDown = (rEvt.Source == m_xDesktop);
437 
438     if ( shutDown && m_xDesktop.is() )
439     {
440         m_xDesktop->removeTerminateListener( this );
441         m_xDesktop.clear();
442     }
443 
444     if ( shutDown )
445     {
446         if ( dp_misc::office_is_running() )
447         {
448             const ::vos::OGuard guard( Application::GetSolarMutex() );
449             delete m_pExtMgrDialog;
450             m_pExtMgrDialog = NULL;
451             delete m_pUpdReqDialog;
452             m_pUpdReqDialog = NULL;
453         }
454         s_ExtMgr.clear();
455     }
456 }
457 
458 //------------------------------------------------------------------------------
459 // XTerminateListener
460 void TheExtensionManager::queryTermination( ::lang::EventObject const & )
461     throw ( frame::TerminationVetoException, uno::RuntimeException )
462 {
463     DialogHelper *pDialogHelper = getDialogHelper();
464 
465     if ( m_pExecuteCmdQueue->isBusy() || ( pDialogHelper && pDialogHelper->isBusy() ) )
466     {
467         ToTop( TOTOP_RESTOREWHENMIN );
468         throw frame::TerminationVetoException(
469             OUSTR("The office cannot be closed while the Extension Manager is running"),
470             uno::Reference<XInterface>(static_cast<frame::XTerminateListener*>(this), uno::UNO_QUERY));
471     }
472     else
473     {
474         if ( m_pExtMgrDialog )
475             m_pExtMgrDialog->Close();
476         if ( m_pUpdReqDialog )
477             m_pUpdReqDialog->Close();
478     }
479 }
480 
481 //------------------------------------------------------------------------------
482 void TheExtensionManager::notifyTermination( ::lang::EventObject const & rEvt )
483     throw ( uno::RuntimeException )
484 {
485     disposing( rEvt );
486 }
487 
488 //------------------------------------------------------------------------------
489 // XModifyListener
490 void TheExtensionManager::modified( ::lang::EventObject const & /*rEvt*/ )
491     throw ( uno::RuntimeException )
492 {
493     getDialogHelper()->prepareChecking();
494     createPackageList();
495     getDialogHelper()->checkEntries();
496 }
497 
498 //------------------------------------------------------------------------------
499 ::rtl::Reference< TheExtensionManager > TheExtensionManager::get( const uno::Reference< uno::XComponentContext > &xContext,
500                                                                   const uno::Reference< awt::XWindow > &xParent,
501                                                                   const OUString & extensionURL )
502 {
503     if ( s_ExtMgr.is() )
504     {
505         OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
506         if ( extensionURL.getLength() )
507             s_ExtMgr->installPackage( extensionURL, true );
508         return s_ExtMgr;
509     }
510 
511     Window * pParent = DIALOG_NO_PARENT;
512     if ( xParent.is() )
513         pParent = VCLUnoHelper::GetWindow(xParent);
514 
515     ::rtl::Reference<TheExtensionManager> that( new TheExtensionManager( pParent, xContext ) );
516 
517     const ::vos::OGuard guard( Application::GetSolarMutex() );
518     if ( ! s_ExtMgr.is() )
519     {
520         OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
521         s_ExtMgr = that;
522     }
523 
524     if ( extensionURL.getLength() )
525         s_ExtMgr->installPackage( extensionURL, true );
526 
527     return s_ExtMgr;
528 }
529 
530 } //namespace dp_gui
531 
532