LibreOffice Module desktop (master)  1
check_ext_deps.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_folders.h>
21 #include <config_features.h>
22 
23 #include <osl/file.hxx>
24 
25 #include <rtl/bootstrap.hxx>
26 #include <rtl/ustring.hxx>
27 #include <sal/log.hxx>
28 #include <cppuhelper/implbase.hxx>
29 
30 #include <vcl/wrkwin.hxx>
31 #include <vcl/timer.hxx>
32 
33 #include <unotools/configmgr.hxx>
34 #include <tools/diagnose_ex.h>
36 
37 #include <comphelper/lok.hxx>
39 #include <cppuhelper/bootstrap.hxx>
40 #include <cppuhelper/exc_hlp.hxx>
41 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
42 #include <com/sun/star/ucb/CommandAbortedException.hpp>
43 #include <com/sun/star/ucb/CommandFailedException.hpp>
44 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/beans/NamedValue.hpp>
47 #include <com/sun/star/configuration/theDefaultProvider.hpp>
48 #include <com/sun/star/deployment/DeploymentException.hpp>
49 #include <com/sun/star/deployment/XPackage.hpp>
50 #include <com/sun/star/deployment/ExtensionManager.hpp>
51 #include <com/sun/star/deployment/LicenseException.hpp>
52 #include <com/sun/star/deployment/ui/LicenseDialog.hpp>
53 #include <com/sun/star/task/OfficeRestartManager.hpp>
54 #include <com/sun/star/task/XJob.hpp>
55 #include <com/sun/star/task/XInteractionApprove.hpp>
56 #include <com/sun/star/task/XInteractionAbort.hpp>
57 #include <com/sun/star/ui/dialogs/XExecutableDialog.hpp>
58 #include <com/sun/star/ui/dialogs/ExecutableDialogResults.hpp>
59 #include <com/sun/star/util/XChangesBatch.hpp>
60 
61 #include <app.hxx>
62 
63 #include <dp_misc.h>
64 
65 using namespace desktop;
66 using namespace com::sun::star;
67 using namespace com::sun::star::lang;
68 using namespace com::sun::star::task;
69 using namespace com::sun::star::uno;
70 
71 namespace
72 {
73 //For use with XExtensionManager.synchronize
74 class SilentCommandEnv
75  : public ::cppu::WeakImplHelper< ucb::XCommandEnvironment,
76  task::XInteractionHandler,
77  ucb::XProgressHandler >
78 {
79  uno::Reference<uno::XComponentContext> mxContext;
80  Desktop *mpDesktop;
81  sal_Int32 mnLevel;
82  sal_Int32 mnProgress;
83 
84 public:
85  SilentCommandEnv(
86  uno::Reference<uno::XComponentContext> const & xContext,
87  Desktop* pDesktop );
88  virtual ~SilentCommandEnv() override;
89 
90  // XCommandEnvironment
91  virtual uno::Reference<task::XInteractionHandler > SAL_CALL
92  getInteractionHandler() override;
93  virtual uno::Reference<ucb::XProgressHandler >
94  SAL_CALL getProgressHandler() override;
95 
96  // XInteractionHandler
97  virtual void SAL_CALL handle(
98  uno::Reference<task::XInteractionRequest > const & xRequest ) override;
99 
100  // XProgressHandler
101  virtual void SAL_CALL push( uno::Any const & Status ) override;
102  virtual void SAL_CALL update( uno::Any const & Status ) override;
103  virtual void SAL_CALL pop() override;
104 };
105 
106 
107 SilentCommandEnv::SilentCommandEnv(
108  uno::Reference<uno::XComponentContext> const & xContext,
109  Desktop* pDesktop ):
110  mxContext( xContext ),
111  mpDesktop( pDesktop ),
112  mnLevel( 0 ),
113  mnProgress( 25 )
114 {}
115 
116 
117 SilentCommandEnv::~SilentCommandEnv()
118 {
119  if (mpDesktop)
120  mpDesktop->SetSplashScreenText(OUString());
121 }
122 
123 
124 Reference<task::XInteractionHandler> SilentCommandEnv::getInteractionHandler()
125 {
126  return this;
127 }
128 
129 
130 Reference<ucb::XProgressHandler> SilentCommandEnv::getProgressHandler()
131 {
132  return this;
133 }
134 
135 
136 // XInteractionHandler
137 void SilentCommandEnv::handle( Reference< task::XInteractionRequest> const & xRequest )
138 {
139  deployment::LicenseException licExc;
140 
141  uno::Any request( xRequest->getRequest() );
142  bool bApprove = true;
143 
144  if ( request >>= licExc )
145  {
146  uno::Reference< ui::dialogs::XExecutableDialog > xDialog(
148  mxContext, VCLUnoHelper::GetInterface( nullptr ),
149  licExc.ExtensionName, licExc.Text ) );
150  sal_Int16 res = xDialog->execute();
151  if ( res == ui::dialogs::ExecutableDialogResults::CANCEL )
152  bApprove = false;
153  else if ( res == ui::dialogs::ExecutableDialogResults::OK )
154  bApprove = true;
155  else
156  {
157  OSL_ASSERT(false);
158  }
159  }
160 
161  // We approve everything here
162  uno::Sequence< Reference< task::XInteractionContinuation > > conts( xRequest->getContinuations() );
163  Reference< task::XInteractionContinuation > const * pConts = conts.getConstArray();
164  sal_Int32 len = conts.getLength();
165 
166  for ( sal_Int32 pos = 0; pos < len; ++pos )
167  {
168  if ( bApprove )
169  {
170  uno::Reference< task::XInteractionApprove > xInteractionApprove( pConts[ pos ], uno::UNO_QUERY );
171  if ( xInteractionApprove.is() )
172  xInteractionApprove->select();
173  }
174  else
175  {
176  uno::Reference< task::XInteractionAbort > xInteractionAbort( pConts[ pos ], uno::UNO_QUERY );
177  if ( xInteractionAbort.is() )
178  xInteractionAbort->select();
179  }
180  }
181 }
182 
183 
184 // XProgressHandler
185 void SilentCommandEnv::push( uno::Any const & rStatus )
186 {
187  OUString sText;
188  mnLevel += 1;
189 
190  if (mpDesktop && rStatus.hasValue() && (rStatus >>= sText))
191  {
192  if ( mnLevel <= 3 )
193  mpDesktop->SetSplashScreenText( sText );
194  else
195  mpDesktop->SetSplashScreenProgress( ++mnProgress );
196  }
197 }
198 
199 
200 void SilentCommandEnv::update( uno::Any const & rStatus )
201 {
202  OUString sText;
203  if (mpDesktop && rStatus.hasValue() && (rStatus >>= sText))
204  {
205  mpDesktop->SetSplashScreenText( sText );
206  }
207 }
208 
209 
210 void SilentCommandEnv::pop()
211 {
212  mnLevel -= 1;
213 }
214 
215 } // end namespace
216 
217 
218 static const char aAccessSrvc[] = "com.sun.star.configuration.ConfigurationUpdateAccess";
219 
220 static sal_Int16 impl_showExtensionDialog( uno::Reference< uno::XComponentContext > const &xContext )
221 {
222  OUString sServiceName = "com.sun.star.deployment.ui.UpdateRequiredDialog";
223  uno::Reference< uno::XInterface > xService;
224  sal_Int16 nRet = 0;
225 
226  uno::Reference< lang::XMultiComponentFactory > xServiceManager( xContext->getServiceManager() );
227  if( !xServiceManager.is() )
228  throw uno::RuntimeException(
229  "impl_showExtensionDialog(): unable to obtain service manager from component context", uno::Reference< uno::XInterface > () );
230 
231  xService = xServiceManager->createInstanceWithContext( sServiceName, xContext );
232  uno::Reference< ui::dialogs::XExecutableDialog > xExecuteable( xService, uno::UNO_QUERY );
233  if ( xExecuteable.is() )
234  nRet = xExecuteable->execute();
235 
236  return nRet;
237 }
238 
239 
240 // Check dependencies of all packages
241 
242 static bool impl_checkDependencies( const uno::Reference< uno::XComponentContext > &xContext )
243 {
244  uno::Sequence< uno::Sequence< uno::Reference< deployment::XPackage > > > xAllPackages;
245  uno::Reference< deployment::XExtensionManager > xExtensionManager = deployment::ExtensionManager::get( xContext );
246 
247  if ( !xExtensionManager.is() )
248  {
249  SAL_WARN( "desktop.app", "Could not get the Extension Manager!" );
250  return true;
251  }
252 
253  try {
254  xAllPackages = xExtensionManager->getAllExtensions( uno::Reference< task::XAbortChannel >(),
255  uno::Reference< ucb::XCommandEnvironment >() );
256  }
257  catch ( const deployment::DeploymentException & ) { return true; }
258  catch ( const ucb::CommandFailedException & ) { return true; }
259  catch ( const ucb::CommandAbortedException & ) { return true; }
260  catch ( const lang::IllegalArgumentException & e ) {
261  css::uno::Any anyEx = cppu::getCaughtException();
262  throw css::lang::WrappedTargetRuntimeException( e.Message,
263  e.Context, anyEx );
264  }
265 
266 #ifdef DEBUG
267  sal_Int32 const nMax = 3;
268 #else
269  sal_Int32 const nMax = 2;
270 #endif
271 
272  for ( sal_Int32 i = 0; i < xAllPackages.getLength(); ++i )
273  {
274  uno::Sequence< uno::Reference< deployment::XPackage > > xPackageList = xAllPackages[i];
275 
276  for ( sal_Int32 j = 0; (j<nMax) && (j < xPackageList.getLength()); ++j )
277  {
278  uno::Reference< deployment::XPackage > xPackage = xPackageList[j];
279  if ( xPackage.is() )
280  {
281  bool bRegistered = false;
282  try {
283  beans::Optional< beans::Ambiguous< sal_Bool > > option( xPackage->isRegistered( uno::Reference< task::XAbortChannel >(),
284  uno::Reference< ucb::XCommandEnvironment >() ) );
285  if ( option.IsPresent )
286  {
287  ::beans::Ambiguous< sal_Bool > const & reg = option.Value;
288  if ( reg.IsAmbiguous )
289  bRegistered = false;
290  else
291  bRegistered = reg.Value;
292  }
293  else
294  bRegistered = false;
295  }
296  catch ( const uno::RuntimeException & ) { throw; }
297  catch (const uno::Exception & ) {
298  css::uno::Any ex( cppu::getCaughtException() );
299  SAL_WARN( "desktop.app", exceptionToString(ex) );
300  }
301 
302  if ( bRegistered )
303  {
304  bool bDependenciesValid = false;
305  try {
306  bDependenciesValid = xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() );
307  }
308  catch ( const deployment::DeploymentException & ) {}
309  if ( ! bDependenciesValid )
310  {
311  return false;
312  }
313  }
314  }
315  }
316  }
317  return true;
318 }
319 
320 
321 // resets the 'check needed' flag (needed, if aborted)
322 
324 {
325  try {
326  Reference< XMultiServiceFactory > theConfigProvider(
327  configuration::theDefaultProvider::get(
329 
330  Sequence< Any > theArgs(1);
331  beans::NamedValue v( "nodepath",
332  makeAny( OUString("org.openoffice.Setup/Office") ) );
333  theArgs[0] <<= v;
335  theConfigProvider->createInstanceWithArguments( aAccessSrvc, theArgs ), UNO_QUERY_THROW );
336 
337  Any value = makeAny( OUString("never") );
338 
339  pset->setPropertyValue("LastCompatibilityCheckID", value );
340  Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
341  }
342  catch (const Exception&) {}
343 }
344 
345 
346 // to check if we need checking the dependencies of the extensions again, we compare
347 // the build id of the office with the one of the last check
348 
350 {
351  bool bNeedsCheck = false;
352  OUString aLastCheckBuildID;
353  OUString aCurrentBuildID( "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") ":buildid}" );
354  rtl::Bootstrap::expandMacros( aCurrentBuildID );
355 
356  try {
357  Reference< XMultiServiceFactory > theConfigProvider(
358  configuration::theDefaultProvider::get(
360 
361  Sequence< Any > theArgs(1);
362  beans::NamedValue v( "nodepath",
363  makeAny( OUString("org.openoffice.Setup/Office") ) );
364  theArgs[0] <<= v;
366  theConfigProvider->createInstanceWithArguments( aAccessSrvc, theArgs ), UNO_QUERY_THROW );
367 
368  Any result = pset->getPropertyValue("LastCompatibilityCheckID");
369 
370  result >>= aLastCheckBuildID;
371  if ( aLastCheckBuildID != aCurrentBuildID )
372  {
373  bNeedsCheck = true;
374  result <<= aCurrentBuildID;
375  pset->setPropertyValue("LastCompatibilityCheckID", result );
376  Reference< util::XChangesBatch >( pset, UNO_QUERY_THROW )->commitChanges();
377  }
378 #ifdef DEBUG
379  bNeedsCheck = true;
380 #endif
381  }
382  catch (const css::uno::Exception&) {}
383 
384  return bNeedsCheck;
385 }
386 
387 
388 // Do we need to check the dependencies of the extensions?
389 // When there are unresolved issues, we can't continue with startup
390 bool Desktop::CheckExtensionDependencies()
391 {
392  if (!impl_needsCompatCheck())
393  {
394  return false;
395  }
396 
397  uno::Reference< uno::XComponentContext > xContext(
399 
400  bool bDependenciesValid = impl_checkDependencies( xContext );
401 
402  short nRet = 0;
403 
404  if ( !bDependenciesValid )
405  nRet = impl_showExtensionDialog( xContext );
406 
407  if ( nRet == -1 )
408  {
410  return true;
411  }
412  else
413  return false;
414 }
415 
416 void Desktop::SynchronizeExtensionRepositories(bool bCleanedExtensionCache, Desktop* pDesktop)
417 {
418  uno::Reference< uno::XComponentContext > context(
420  uno::Reference< ucb::XCommandEnvironment > silent(
421  new SilentCommandEnv(context, pDesktop));
422  if (bCleanedExtensionCache) {
423  deployment::ExtensionManager::get(context)->reinstallDeployedExtensions(
424  true, "user", Reference<task::XAbortChannel>(), silent);
425 #if !HAVE_FEATURE_MACOSX_SANDBOX
427  task::OfficeRestartManager::get(context)->requestRestart(
428  silent->getInteractionHandler());
429 #endif
430  } else {
431  // reinstallDeployedExtensions above already calls syncRepositories internally
432 
433  // Force syncing repositories on startup. There are cases where the extension
434  // registration becomes invalid which leads to extensions not starting up, although
435  // installed and active. Syncing extension repos on startup fixes that.
436  dp_misc::syncRepositories(/*force=*/true, silent);
437  }
438 }
439 
440 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool hasValue()
IJScriptValueObject VARIANT value
bool update()
Definition: updater.cxx:284
static bool impl_checkDependencies(const uno::Reference< uno::XComponentContext > &xContext)
void syncRepositories(bool force, Reference< ucb::XCommandEnvironment > const &xCmdEnv)
Definition: dp_misc.cxx:532
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
Definition: app.cxx:167
JCOPY_OPTION option
Any SAL_CALL getCaughtException()
static bool impl_needsCompatCheck()
#define SAL_CONFIGFILE(name)
static sal_Int16 impl_showExtensionDialog(uno::Reference< uno::XComponentContext > const &xContext)
MetadataImporterPluginType * result
int i
static void impl_setNeedsCompatCheck()
static const char aAccessSrvc[]
OString exceptionToString(const css::uno::Any &caught)
float v
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:1546
static void SynchronizeExtensionRepositories(bool bCleanedExtensionCache, Desktop *pDesktop=nullptr)
Reference< XComponentContext > getProcessComponentContext()
#define SAL_WARN(area, stream)
static css::uno::Reference< css::awt::XWindow > GetInterface(vcl::Window *pWindow)
const sal_Char sServiceName[]
css::uno::Any SAL_CALL makeAny(const SharedUNOComponent< INTERFACE, COMPONENT > &value)