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