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