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 // MARKER(update_precomp.py): autogen include statement, do not remove
28 #include "precompiled_desktop.hxx"
29 
30 #include "osl/file.hxx"
31 #include "osl/mutex.hxx"
32 
33 #include <rtl/bootstrap.hxx>
34 #include <rtl/ustring.hxx>
35 #include <rtl/logfile.hxx>
36 #include "cppuhelper/compbase3.hxx"
37 
38 #include "vcl/wrkwin.hxx"
39 #include "vcl/timer.hxx"
40 
41 #include <unotools/configmgr.hxx>
42 #include "toolkit/helper/vclunohelper.hxx"
43 
44 #include <comphelper/processfactory.hxx>
45 #include <comphelper/sequence.hxx>
46 #include <cppuhelper/bootstrap.hxx>
47 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
48 #include <com/sun/star/beans/XPropertySet.hpp>
49 #include <com/sun/star/beans/NamedValue.hpp>
50 #include "com/sun/star/deployment/XPackage.hpp"
51 #include "com/sun/star/deployment/ExtensionManager.hpp"
52 #include "com/sun/star/deployment/LicenseException.hpp"
53 #include "com/sun/star/deployment/ui/LicenseDialog.hpp"
54 #include <com/sun/star/task/XJob.hpp>
55 #include <com/sun/star/task/XJobExecutor.hpp>
56 #include <com/sun/star/task/XInteractionApprove.hpp>
57 #include <com/sun/star/task/XInteractionAbort.hpp>
58 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
59 #include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp"
60 #include <com/sun/star/util/XChangesBatch.hpp>
61 
62 #include "app.hxx"
63 
64 #include "../deployment/inc/dp_misc.h"
65 
66 using rtl::OUString;
67 using namespace desktop;
68 using namespace com::sun::star;
69 
70 #define UNISTRING(s) OUString(RTL_CONSTASCII_USTRINGPARAM(s))
71 
72 namespace
73 {
74 //For use with XExtensionManager.synchronize
75 class SilentCommandEnv
76     : public ::cppu::WeakImplHelper3< ucb::XCommandEnvironment,
77                                       task::XInteractionHandler,
78                                       ucb::XProgressHandler >
79 {
80     Desktop    *mpDesktop;
81     sal_Int32   mnLevel;
82     sal_Int32   mnProgress;
83 
84 public:
85              SilentCommandEnv( Desktop* pDesktop );
86     virtual ~SilentCommandEnv();
87 
88     // XCommandEnvironment
89     virtual uno::Reference<task::XInteractionHandler > SAL_CALL
90     getInteractionHandler() throw (uno::RuntimeException);
91     virtual uno::Reference<ucb::XProgressHandler >
92     SAL_CALL getProgressHandler() throw (uno::RuntimeException);
93 
94     // XInteractionHandler
95     virtual void SAL_CALL handle(
96         uno::Reference<task::XInteractionRequest > const & xRequest )
97         throw (uno::RuntimeException);
98 
99     // XProgressHandler
100     virtual void SAL_CALL push( uno::Any const & Status )
101         throw (uno::RuntimeException);
102     virtual void SAL_CALL update( uno::Any const & Status )
103         throw (uno::RuntimeException);
104     virtual void SAL_CALL pop() throw (uno::RuntimeException);
105 };
106 
107 //-----------------------------------------------------------------------------
108 SilentCommandEnv::SilentCommandEnv( Desktop* pDesktop )
109 {
110     mpDesktop = pDesktop;
111     mnLevel = 0;
112     mnProgress = 25;
113 }
114 
115 //-----------------------------------------------------------------------------
116 SilentCommandEnv::~SilentCommandEnv()
117 {
118     mpDesktop->SetSplashScreenText( OUString() );
119 }
120 
121 //-----------------------------------------------------------------------------
122 Reference<task::XInteractionHandler> SilentCommandEnv::getInteractionHandler()
123     throw (uno::RuntimeException)
124 {
125     return this;
126 }
127 
128 //-----------------------------------------------------------------------------
129 Reference<ucb::XProgressHandler> SilentCommandEnv::getProgressHandler()
130     throw (uno::RuntimeException)
131 {
132     return this;
133 }
134 
135 //-----------------------------------------------------------------------------
136 // XInteractionHandler
137 void SilentCommandEnv::handle( Reference< task::XInteractionRequest> const & xRequest )
138     throw (uno::RuntimeException)
139 {
140 	deployment::LicenseException licExc;
141 
142     uno::Any request( xRequest->getRequest() );
143     bool bApprove = true;
144 
145 	if ( request >>= licExc )
146     {
147         uno::Reference< uno::XComponentContext > xContext = comphelper_getProcessComponentContext();
148         uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
149             deployment::ui::LicenseDialog::create(
150             xContext, VCLUnoHelper::GetInterface( NULL ),
151             licExc.ExtensionName, licExc.Text ) );
152         sal_Int16 res = xDialog->execute();
153         if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
154             bApprove = false;
155         else if ( res == ui::dialogs::ExecutableDialogResults::OK )
156             bApprove = true;
157         else
158         {
159             OSL_ASSERT(0);
160         }
161 	}
162 
163     // We approve everything here
164     uno::Sequence< Reference< task::XInteractionContinuation > > conts( xRequest->getContinuations() );
165     Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
166     sal_Int32 len = conts.getLength();
167 
168     for ( sal_Int32 pos = 0; pos < len; ++pos )
169     {
170         if ( bApprove )
171         {
172             uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
173             if ( xInteractionApprove.is() )
174                 xInteractionApprove->select();
175         }
176         else
177         {
178             uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
179             if ( xInteractionAbort.is() )
180                 xInteractionAbort->select();
181         }
182     }
183 }
184 
185 //-----------------------------------------------------------------------------
186 // XProgressHandler
187 void SilentCommandEnv::push( uno::Any const & rStatus )
188     throw (uno::RuntimeException)
189 {
190     OUString sText;
191     mnLevel += 1;
192 
193     if ( rStatus.hasValue() && ( rStatus >>= sText) )
194     {
195         if ( mnLevel <= 3 )
196             mpDesktop->SetSplashScreenText( sText );
197         else
198             mpDesktop->SetSplashScreenProgress( ++mnProgress );
199     }
200 }
201 
202 //-----------------------------------------------------------------------------
203 void SilentCommandEnv::update( uno::Any const & rStatus )
204     throw (uno::RuntimeException)
205 {
206     OUString sText;
207     if ( rStatus.hasValue() && ( rStatus >>= sText) )
208     {
209         mpDesktop->SetSplashScreenText( sText );
210     }
211 }
212 
213 //-----------------------------------------------------------------------------
214 void SilentCommandEnv::pop() throw (uno::RuntimeException)
215 {
216     mnLevel -= 1;
217 }
218 
219 } // end namespace
220 
221 //-----------------------------------------------------------------------------
222 //-----------------------------------------------------------------------------
223 //-----------------------------------------------------------------------------
224 static const OUString sConfigSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationProvider" ) );
225 static const OUString sAccessSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess" ) );
226 //------------------------------------------------------------------------------
227 static sal_Int16 impl_showExtensionDialog( uno::Reference< uno::XComponentContext > &xContext )
228 {
229     rtl::OUString sServiceName = UNISTRING("com.sun.star.deployment.ui.UpdateRequiredDialog");
230     uno::Reference< uno::XInterface > xService;
231     sal_Int16 nRet = 0;
232 
233     uno::Reference< lang::XMultiComponentFactory > xServiceManager( xContext->getServiceManager() );
234     if( !xServiceManager.is() )
235         throw uno::RuntimeException(
236             UNISTRING( "impl_showExtensionDialog(): unable to obtain service manager from component context" ), uno::Reference< uno::XInterface > () );
237 
238     xService = xServiceManager->createInstanceWithContext( sServiceName, xContext );
239     uno::Reference< ui::dialogs::XExecutableDialog > xExecuteable( xService, uno::UNO_QUERY );
240     if ( xExecuteable.is() )
241         nRet = xExecuteable->execute();
242 
243     return nRet;
244 }
245 
246 //------------------------------------------------------------------------------
247 // Check dependencies of all packages
248 //------------------------------------------------------------------------------
249 static bool impl_checkDependencies( const uno::Reference< uno::XComponentContext > &xContext )
250 {
251     uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
252     uno::Reference< deployment::XExtensionManager > xExtensionManager = deployment::ExtensionManager::get( xContext );
253 
254     if ( !xExtensionManager.is() )
255     {
256         OSL_ENSURE( 0, "Could not get the Extension Manager!" );
257         return true;
258     }
259 
260     try {
261         xAllPackages = xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
262                                                             uno::Reference< ucb::XCommandEnvironment >() );
263     }
264     catch ( deployment::DeploymentException & ) { return true; }
265     catch ( ucb::CommandFailedException & ) { return true; }
266     catch ( ucb::CommandAbortedException & ) { return true; }
267     catch ( lang::IllegalArgumentException & e ) {
268         throw uno::RuntimeException( e.Message, e.Context );
269     }
270 
271     sal_Int32 nMax = 2;
272 #ifdef DEBUG
273     nMax = 3;
274 #endif
275 
276     for ( sal_Int32 i = 0; i < xAllPackages.getLength(); ++i )
277     {
278         uno::Sequence< uno::Reference< deployment::XPackage > > xPackageList = xAllPackages[i];
279 
280         for ( sal_Int32 j = 0; (j<nMax) && (j < xPackageList.getLength()); ++j )
281         {
282             uno::Reference< deployment::XPackage > xPackage = xPackageList[j];
283             if ( xPackage.is() )
284             {
285                 bool bRegistered = false;
286                 try {
287                     beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
288                                                                                                     uno::Reference< ucb::XCommandEnvironment >() ) );
289                     if ( option.IsPresent )
290                     {
291                         ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
292                         if ( reg.IsAmbiguous )
293                             bRegistered = false;
294                         else
295                             bRegistered = reg.Value ? true : false;
296                     }
297                     else
298                         bRegistered = false;
299                 }
300                 catch ( uno::RuntimeException & ) { throw; }
301                 catch ( uno::Exception & exc) {
302                     (void) exc;
303                     OSL_ENSURE( 0, ::rtl::OUStringToOString( exc.Message, RTL_TEXTENCODING_UTF8 ).getStr() );
304                 }
305 
306                 if ( bRegistered )
307                 {
308                     bool bDependenciesValid = false;
309                     try {
310                         bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
311                     }
312                     catch ( deployment::DeploymentException & ) {}
313                     if ( ! bDependenciesValid )
314                     {
315                         return false;
316                     }
317                 }
318             }
319         }
320     }
321     return true;
322 }
323 
324 //------------------------------------------------------------------------------
325 // resets the 'check needed' flag (needed, if aborted)
326 //------------------------------------------------------------------------------
327 static void impl_setNeedsCompatCheck()
328 {
329     try {
330         Reference < XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory();
331         // get configuration provider
332         Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory >(
333                 xFactory->createInstance(sConfigSrvc), UNO_QUERY_THROW);
334 
335         Sequence< Any > theArgs(1);
336         beans::NamedValue v( OUString::createFromAscii("NodePath"),
337                       makeAny( OUString::createFromAscii("org.openoffice.Setup/Office") ) );
338         theArgs[0] <<= v;
339         Reference< beans::XPropertySet > pset = Reference< beans::XPropertySet >(
340             theConfigProvider->createInstanceWithArguments( sAccessSrvc, theArgs ), UNO_QUERY_THROW );
341 
342         Any value = makeAny( OUString::createFromAscii("never") );
343 
344         pset->setPropertyValue( OUString::createFromAscii("LastCompatibilityCheckID"), value );
345         Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
346     }
347     catch (const Exception&) {}
348 }
349 
350 //------------------------------------------------------------------------------
351 static bool impl_check()
352 {
353     uno::Reference< uno::XComponentContext > xContext = comphelper_getProcessComponentContext();
354 
355     bool bDependenciesValid = impl_checkDependencies( xContext );
356 
357     short nRet = 0;
358 
359     if ( !bDependenciesValid )
360         nRet = impl_showExtensionDialog( xContext );
361 
362     if ( nRet == -1 )
363     {
364         impl_setNeedsCompatCheck();
365         return true;
366     }
367     else
368         return false;
369 }
370 
371 //------------------------------------------------------------------------------
372 // to check if we need checking the dependencies of the extensions again, we compare
373 // the build id of the office with the one of the last check
374 //------------------------------------------------------------------------------
375 static bool impl_needsCompatCheck()
376 {
377     bool bNeedsCheck = false;
378     rtl::OUString aLastCheckBuildID;
379     rtl::OUString aCurrentBuildID( UNISTRING( "${$OOO_BASE_DIR/program/" SAL_CONFIGFILE("version") ":buildid}" ) );
380 	rtl::Bootstrap::expandMacros( aCurrentBuildID );
381 
382     try {
383         Reference < XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory();
384         // get configuration provider
385         Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory >(
386                 xFactory->createInstance(sConfigSrvc), UNO_QUERY_THROW);
387 
388         Sequence< Any > theArgs(1);
389         beans::NamedValue v( OUString::createFromAscii("NodePath"),
390                       makeAny( OUString::createFromAscii("org.openoffice.Setup/Office") ) );
391         theArgs[0] <<= v;
392         Reference< beans::XPropertySet > pset = Reference< beans::XPropertySet >(
393             theConfigProvider->createInstanceWithArguments( sAccessSrvc, theArgs ), UNO_QUERY_THROW );
394 
395         Any result = pset->getPropertyValue( OUString::createFromAscii("LastCompatibilityCheckID") );
396 
397         result >>= aLastCheckBuildID;
398         if ( aLastCheckBuildID != aCurrentBuildID )
399         {
400             bNeedsCheck = true;
401             result <<= aCurrentBuildID;
402             pset->setPropertyValue( OUString::createFromAscii("LastCompatibilityCheckID"), result );
403             Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
404         }
405 #ifdef DEBUG
406         bNeedsCheck = true;
407 #endif
408     }
409     catch (const Exception&) {}
410 
411     return bNeedsCheck;
412 }
413 
414 //------------------------------------------------------------------------------
415 // Do we need to check the dependencies of the extensions?
416 // When there are unresolved issues, we can't continue with startup
417 sal_Bool Desktop::CheckExtensionDependencies()
418 {
419     sal_Bool bAbort = false;
420 
421     if ( impl_needsCompatCheck() )
422         bAbort = impl_check();
423 
424     return bAbort;
425 }
426 
427 void Desktop::SynchronizeExtensionRepositories()
428 {
429     RTL_LOGFILE_CONTEXT(aLog,"desktop (jl) ::Desktop::SynchronizeExtensionRepositories");
430     dp_misc::syncRepositories( new SilentCommandEnv( this ) );
431 }
432