LibreOffice Module desktop (master)  1
dp_script.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 
21 #include <strings.hrc>
22 #include "dp_lib_container.h"
23 #include <dp_backend.h>
24 #include <dp_ucb.h>
25 #include <ucbhelper/content.hxx>
26 #include <cppuhelper/implbase.hxx>
27 #include <svl/inettype.hxx>
28 #include <com/sun/star/util/XUpdatable.hpp>
29 #include <com/sun/star/script/XLibraryContainer3.hpp>
30 #include <memory>
31 #include "dp_scriptbackenddb.hxx"
33 
34 using namespace ::dp_misc;
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
37 using namespace ::com::sun::star::ucb;
38 
40 namespace {
41 
42 typedef ::cppu::ImplInheritanceHelper<
44 
45 class BackendImpl : public t_helper
46 {
47  class PackageImpl : public ::dp_registry::backend::Package
48  {
49  BackendImpl * getMyBackend() const;
50 
51  const OUString m_scriptURL;
52  const OUString m_dialogURL;
53  OUString m_dialogName;
54 
55  // Package
56  virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
57  ::osl::ResettableMutexGuard & guard,
58  ::rtl::Reference<AbortChannel> const & abortChannel,
59  Reference<XCommandEnvironment> const & xCmdEnv ) override;
60  virtual void processPackage_(
61  ::osl::ResettableMutexGuard & guard,
62  bool registerPackage,
63  bool startup,
64  ::rtl::Reference<AbortChannel> const & abortChannel,
65  Reference<XCommandEnvironment> const & xCmdEnv ) override;
66 
67  public:
68  PackageImpl(
69  ::rtl::Reference<BackendImpl> const & myBackend,
70  OUString const & url,
71  Reference<XCommandEnvironment> const &xCmdEnv,
72  OUString const & scriptURL, OUString const & dialogURL,
73  bool bRemoved, OUString const & identifier);
74  };
75  friend class PackageImpl;
76 
77  // PackageRegistryBackend
78  virtual Reference<deployment::XPackage> bindPackage_(
79  OUString const & url, OUString const & mediaType,
80  bool bRemoved, OUString const & identifier,
81  Reference<XCommandEnvironment> const & xCmdEnv ) override;
82 
83  void addDataToDb(OUString const & url);
84  bool hasActiveEntry(OUString const & url);
85  void revokeEntryFromDb(OUString const & url);
86 
87  const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
88  const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
89  Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
90  std::unique_ptr<ScriptBackendDb> m_backendDb;
91 public:
92  BackendImpl( Sequence<Any> const & args,
93  Reference<XComponentContext> const & xComponentContext );
94 
95  // XServiceInfo
96  virtual OUString SAL_CALL getImplementationName() override;
97  virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
98  virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
99 
100  // XUpdatable
101  virtual void SAL_CALL update() override;
102 
103  // XPackageRegistry
104  virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
105  getSupportedPackageTypes() override;
106  virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
107 
108 };
109 
110 
111 BackendImpl::PackageImpl::PackageImpl(
112  ::rtl::Reference<BackendImpl> const & myBackend,
113  OUString const & url,
114  Reference<XCommandEnvironment> const &xCmdEnv,
115  OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
116  OUString const & identifier)
117  : Package( myBackend.get(), url,
118  OUString(), OUString(), // will be late-initialized
119  !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
120  : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
121  m_scriptURL( scriptURL ),
122  m_dialogURL( dialogURL )
123 {
124  // name, displayName:
125  if (!dialogURL.isEmpty()) {
126  m_dialogName = LibraryContainer::get_libname(
127  dialogURL, xCmdEnv, myBackend->getComponentContext() );
128  }
129  if (!scriptURL.isEmpty()) {
130  assert(m_name.pData);
131  m_name = LibraryContainer::get_libname(
132  scriptURL, xCmdEnv, myBackend->getComponentContext() );
133  }
134  else
136  m_displayName = m_name;
137 }
138 
139 
140 BackendImpl::BackendImpl(
141  Sequence<Any> const & args,
142  Reference<XComponentContext> const & xComponentContext )
143  : t_helper( args, xComponentContext ),
144  m_xBasicLibTypeInfo( new Package::TypeInfo(
145  "application/vnd.sun.star.basic-library",
146  OUString() /* no file filter */,
147  DpResId(RID_STR_BASIC_LIB)
148  ) ),
149  m_xDialogLibTypeInfo( new Package::TypeInfo(
150  "application/vnd.sun.star.dialog-library",
151  OUString() /* no file filter */,
152  DpResId(RID_STR_DIALOG_LIB)
153  ) ),
154  m_typeInfos( 2 )
155 {
158 
159  OSL_ASSERT( ! transientMode() );
160 
161  if (!transientMode())
162  {
163  OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
164  m_backendDb.reset(
165  new ScriptBackendDb(getComponentContext(), dbFile));
166  }
167 
168 }
169 
170 // XServiceInfo
171 OUString BackendImpl::getImplementationName()
172 {
173  return "com.sun.star.comp.deployment.script.PackageRegistryBackend";
174 }
175 
176 sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
177 {
178  return cppu::supportsService(this, ServiceName);
179 }
180 
181 css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
182 {
183  return { BACKEND_SERVICE_NAME };
184 }
185 
186 void BackendImpl::addDataToDb(OUString const & url)
187 {
188  if (m_backendDb)
189  m_backendDb->addEntry(url);
190 }
191 
192 bool BackendImpl::hasActiveEntry(OUString const & url)
193 {
194  if (m_backendDb)
195  return m_backendDb->hasActiveEntry(url);
196  return false;
197 }
198 
199 // XUpdatable
200 
201 void BackendImpl::update()
202 {
203  // Nothing to do here after fixing i70283!?
204 }
205 
206 // XPackageRegistry
207 
208 Sequence< Reference<deployment::XPackageTypeInfo> >
209 BackendImpl::getSupportedPackageTypes()
210 {
211  return m_typeInfos;
212 }
213 void BackendImpl::revokeEntryFromDb(OUString const & url)
214 {
215  if (m_backendDb)
216  m_backendDb->revokeEntry(url);
217 }
218 
219 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
220 {
221  if (m_backendDb)
222  m_backendDb->removeEntry(url);
223 }
224 
225 // PackageRegistryBackend
226 
227 Reference<deployment::XPackage> BackendImpl::bindPackage_(
228  OUString const & url, OUString const & mediaType_,
229  bool bRemoved, OUString const & identifier,
230  Reference<XCommandEnvironment> const & xCmdEnv )
231 {
232  OUString mediaType( mediaType_ );
233  if (mediaType.isEmpty())
234  {
235  // detect media-type:
236  ::ucbhelper::Content ucbContent;
237  if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
238  ucbContent.isFolder())
239  {
240  // probe for script.xlb:
241  if (create_ucb_content(
242  nullptr, makeURL( url, "script.xlb" ),
243  xCmdEnv, false /* no throw */ ))
244  mediaType = "application/vnd.sun.star.basic-library";
245  // probe for dialog.xlb:
246  else if (create_ucb_content(
247  nullptr, makeURL( url, "dialog.xlb" ),
248  xCmdEnv, false /* no throw */ ))
249  mediaType = "application/vnd.sun.star.dialog-library";
250  }
251  if (mediaType.isEmpty())
252  throw lang::IllegalArgumentException(
253  StrCannotDetectMediaType() + url,
254  static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
255  }
256 
257  OUString type, subType;
259  if (INetContentTypes::parse( mediaType, type, subType, &params ))
260  {
261  if (type.equalsIgnoreAsciiCase("application"))
262  {
263  OUString dialogURL( makeURL( url, "dialog.xlb" ) );
264  if (! create_ucb_content(
265  nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
266  dialogURL.clear();
267  }
268 
269  if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
270  {
271  OUString scriptURL( makeURL( url, "script.xlb"));
272  if (! create_ucb_content(
273  nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
274  scriptURL.clear();
275  }
276 
277  return new PackageImpl(
278  this, url, xCmdEnv, scriptURL,
279  dialogURL, bRemoved, identifier);
280  }
281  else if (subType.equalsIgnoreAsciiCase(
282  "vnd.sun.star.dialog-library")) {
283  return new PackageImpl(
284  this, url, xCmdEnv,
285  OUString() /* no script lib */,
286  dialogURL,
287  bRemoved, identifier);
288  }
289  }
290  }
291  throw lang::IllegalArgumentException(
292  StrUnsupportedMediaType() + mediaType,
293  static_cast<OWeakObject *>(this),
294  static_cast<sal_Int16>(-1) );
295 }
296 
297 
298 // Package
299 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
300 {
301  BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
302  if (nullptr == pBackend)
303  {
304  //May throw a DisposedException
305  check();
306  //We should never get here...
307  throw RuntimeException(
308  "Failed to get the BackendImpl",
309  static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
310  }
311  return pBackend;
312 }
313 
314 beans::Optional< beans::Ambiguous<sal_Bool> >
315 BackendImpl::PackageImpl::isRegistered_(
316  ::osl::ResettableMutexGuard & /* guard */,
317  ::rtl::Reference<AbortChannel> const & /* abortChannel */,
318  Reference<XCommandEnvironment> const & /* xCmdEnv */ )
319 {
320  BackendImpl * that = getMyBackend();
321  Reference< deployment::XPackage > xThisPackage( this );
322 
323  bool registered = that->hasActiveEntry(getURL());
324  return beans::Optional< beans::Ambiguous<sal_Bool> >(
325  true /* IsPresent */,
326  beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
327 }
328 
329 void
330 lcl_maybeRemoveScript(
331  bool const bExists,
332  OUString const& rName,
333  OUString const& rScriptURL,
334  Reference<css::script::XLibraryContainer3> const& xScriptLibs)
335 {
336  if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
337  {
338  const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
339  if (sScriptUrl == rScriptURL)
340  xScriptLibs->removeLibrary(rName);
341  }
342 }
343 
344 bool
345 lcl_maybeAddScript(
346  bool const bExists,
347  OUString const& rName,
348  OUString const& rScriptURL,
349  Reference<css::script::XLibraryContainer3> const& xScriptLibs)
350 {
351  if (bExists && xScriptLibs.is())
352  {
353  bool bCanAdd = true;
354  if (xScriptLibs->hasByName(rName))
355  {
356  const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
357  //We assume here that library names in extensions are unique, which may not be the case
358  //ToDo: If the script exist in another extension, then both extensions must have the
359  //same id
360  if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
361  || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
362  || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
363  || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
364  {
365  xScriptLibs->removeLibrary(rName);
366  bCanAdd = true;
367  }
368  else
369  {
370  bCanAdd = false;
371  }
372  }
373 
374  if (bCanAdd)
375  {
376  xScriptLibs->createLibraryLink(rName, rScriptURL, false);
377  return xScriptLibs->hasByName(rName);
378  }
379  }
380 
381  return false;
382 }
383 
384 void BackendImpl::PackageImpl::processPackage_(
385  ::osl::ResettableMutexGuard & /* guard */,
386  bool doRegisterPackage,
387  bool startup,
388  ::rtl::Reference<AbortChannel> const & /* abortChannel */,
389  Reference<XCommandEnvironment> const & /* xCmdEnv */ )
390 {
391  BackendImpl * that = getMyBackend();
392 
393  Reference< deployment::XPackage > xThisPackage( this );
394  Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
395 
396  bool bScript = !m_scriptURL.isEmpty();
397  Reference<css::script::XLibraryContainer3> xScriptLibs;
398 
399  bool bDialog = !m_dialogURL.isEmpty();
400  Reference<css::script::XLibraryContainer3> xDialogLibs;
401 
402  bool bRunning = !startup && office_is_running();
403  if( bRunning )
404  {
405  if( bScript )
406  {
407  xScriptLibs.set(
408  xComponentContext->getServiceManager()->createInstanceWithContext(
409  "com.sun.star.script.ApplicationScriptLibraryContainer",
410  xComponentContext ), UNO_QUERY_THROW );
411  }
412 
413  if( bDialog )
414  {
415  xDialogLibs.set(
416  xComponentContext->getServiceManager()->createInstanceWithContext(
417  "com.sun.star.script.ApplicationDialogLibraryContainer",
418  xComponentContext ), UNO_QUERY_THROW );
419  }
420  }
421  bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
422  if( !doRegisterPackage )
423  {
424  //We cannot just call removeLibrary(name) because this could remove a
425  //script which was added by an extension in a different repository. For
426  //example, extension foo is contained in the bundled repository and then
427  //the user adds it to the user repository. The extension manager will
428  //then register the new script and revoke the script from the bundled
429  //extension. removeLibrary(name) would now remove the script from the
430  //user repository. That is, the script of the newly added user extension does
431  //not work anymore. Therefore we must check if the currently active
432  //script comes in fact from the currently processed extension.
433 
434  if (bRegistered)
435  {
436  //we also prevent and live deployment at startup
437  if (!isRemoved() && !startup)
438  {
439  lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
440  lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
441  }
442  getMyBackend()->revokeEntryFromDb(getURL());
443  return;
444  }
445  }
446  if (bRegistered)
447  return; // Already registered
448 
449  // Update LibraryContainer
450  bool bScriptSuccess = false;
451  bool bDialogSuccess = false;
452  if (!startup)
453  {
454  //If there is a bundled extension, and the user installs the same extension
455  //then the script from the bundled extension must be removed. If this does not work
456  //then live deployment does not work for scripts.
457  bScriptSuccess = lcl_maybeAddScript(bScript, m_name, m_scriptURL, xScriptLibs);
458  bDialogSuccess = lcl_maybeAddScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
459  }
460  bool bSuccess = bScript || bDialog; // Something must have happened
461  if( bRunning )
462  if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
463  bSuccess = false;
464 
465  if (bSuccess)
466  getMyBackend()->addDataToDb(getURL());
467 }
468 
469 } // anon namespace
470 
471 } // namespace dp_registry::backend::script
472 
473 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
475  css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
476 {
477  return cppu::acquire(new dp_registry::backend::script::BackendImpl(args, context));
478 }
479 
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
Sequence< Reference< deployment::XPackageTypeInfo > > m_typeInfos
Definition: dp_script.cxx:89
const Reference< deployment::XPackageTypeInfo > m_xBasicLibTypeInfo
Definition: dp_script.cxx:87
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_deployment_script_PackageRegistryBackend_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &args)
Definition: dp_script.cxx:474
OUString m_name
bool update()
Definition: updater.cxx:284
bool office_is_running()
Definition: dp_misc.cxx:333
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
OUString makeURL(OUString const &baseURL, OUString const &relPath_)
appends a relative path to a url.
Definition: dp_misc.cxx:251
static bool parse(OUString const &rMediaType, OUString &rType, OUString &rSubType, INetContentTypeParameterList *pParameters=nullptr)
const OUString m_dialogURL
Definition: dp_script.cxx:52
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
def check(model)
OUString m_dialogName
Definition: dp_script.cxx:53
::cppu::WeakImplHelper< css::script::provider::XScriptProvider, css::script::browse::XBrowseNode, css::lang::XServiceInfo, css::lang::XInitialization, css::container::XNameContainer > t_helper
const OUString m_scriptURL
Definition: dp_script.cxx:51
unsigned char sal_Bool
Reference< XComponentContext > getComponentContext(Reference< XMultiServiceFactory > const &factory)
DESKTOP_DEPLOYMENTMISC_DLLPUBLIC bool create_ucb_content(::ucbhelper::Content *ucb_content, OUString const &url, css::uno::Reference< css::ucb::XCommandEnvironment > const &xCmdEnv, bool throw_exc=true)
const Reference< deployment::XPackageTypeInfo > m_xDialogLibTypeInfo
Definition: dp_script.cxx:88
#define BACKEND_SERVICE_NAME
Definition: dp_backend.h:39
std::unordered_map< OString, INetContentTypeParameter > INetContentTypeParameterList
ResultType type
OUString DpResId(const char *pId)
Definition: dp_shared.hxx:36
std::unique_ptr< ScriptBackendDb > m_backendDb
Definition: dp_script.cxx:90