cid#1607171 Data race condition
[LibreOffice.git] / desktop / source / deployment / registry / script / dp_script.cxx
blobc4567230456beb8816c70328544476fcd154cf22
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <strings.hrc>
22 #include "dp_lib_container.h"
23 #include <dp_backend.h>
24 #include <dp_misc.h>
25 #include <dp_ucb.h>
26 #include <ucbhelper/content.hxx>
27 #include <cppuhelper/implbase.hxx>
28 #include <svl/inettype.hxx>
29 #include <com/sun/star/util/XUpdatable.hpp>
30 #include <com/sun/star/script/XLibraryContainer3.hpp>
31 #include <memory>
32 #include <string_view>
34 #include "dp_scriptbackenddb.hxx"
35 #include <cppuhelper/supportsservice.hxx>
37 using namespace ::dp_misc;
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::ucb;
42 namespace dp_registry::backend::script {
43 namespace {
45 typedef ::cppu::ImplInheritanceHelper<
46 ::dp_registry::backend::PackageRegistryBackend, util::XUpdatable > t_helper;
48 class BackendImpl : public t_helper
50 class PackageImpl : public ::dp_registry::backend::Package
52 BackendImpl * getMyBackend() const;
54 const OUString m_scriptURL;
55 const OUString m_dialogURL;
56 OUString m_dialogName;
58 // Package
59 virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
60 ::osl::ResettableMutexGuard & guard,
61 ::rtl::Reference<AbortChannel> const & abortChannel,
62 Reference<XCommandEnvironment> const & xCmdEnv ) override;
63 virtual void processPackage_(
64 ::osl::ResettableMutexGuard & guard,
65 bool registerPackage,
66 bool startup,
67 ::rtl::Reference<AbortChannel> const & abortChannel,
68 Reference<XCommandEnvironment> const & xCmdEnv ) override;
70 public:
71 PackageImpl(
72 ::rtl::Reference<BackendImpl> const & myBackend,
73 OUString const & url,
74 Reference<XCommandEnvironment> const &xCmdEnv,
75 OUString const & scriptURL, OUString const & dialogURL,
76 bool bRemoved, OUString const & identifier);
78 friend class PackageImpl;
80 // PackageRegistryBackend
81 virtual Reference<deployment::XPackage> bindPackage_(
82 OUString const & url, OUString const & mediaType,
83 bool bRemoved, OUString const & identifier,
84 Reference<XCommandEnvironment> const & xCmdEnv ) override;
86 void addDataToDb(OUString const & url);
87 bool hasActiveEntry(std::u16string_view url);
88 void revokeEntryFromDb(std::u16string_view url);
90 const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
91 const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
92 Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
93 std::unique_ptr<ScriptBackendDb> m_backendDb;
94 public:
95 BackendImpl( Sequence<Any> const & args,
96 Reference<XComponentContext> const & xComponentContext );
98 // XServiceInfo
99 virtual OUString SAL_CALL getImplementationName() override;
100 virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
101 virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
103 // XUpdatable
104 virtual void SAL_CALL update() override;
106 // XPackageRegistry
107 virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
108 getSupportedPackageTypes() override;
109 virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
114 BackendImpl::PackageImpl::PackageImpl(
115 ::rtl::Reference<BackendImpl> const & myBackend,
116 OUString const & url,
117 Reference<XCommandEnvironment> const &xCmdEnv,
118 OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
119 OUString const & identifier)
120 : Package( myBackend, url,
121 OUString(), OUString(), // will be late-initialized
122 !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
123 : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
124 m_scriptURL( scriptURL ),
125 m_dialogURL( dialogURL )
127 // name, displayName:
128 if (!dialogURL.isEmpty()) {
129 m_dialogName = LibraryContainer::get_libname(
130 dialogURL, xCmdEnv, myBackend->getComponentContext() );
132 if (!scriptURL.isEmpty()) {
133 assert(m_name.pData);
134 m_name = LibraryContainer::get_libname(
135 scriptURL, xCmdEnv, myBackend->getComponentContext() );
137 else
138 m_name = m_dialogName;
139 m_displayName = m_name;
143 BackendImpl::BackendImpl(
144 Sequence<Any> const & args,
145 Reference<XComponentContext> const & xComponentContext )
146 : t_helper( args, xComponentContext ),
147 m_xBasicLibTypeInfo( new Package::TypeInfo(
148 u"application/vnd.sun.star.basic-library"_ustr,
149 OUString() /* no file filter */,
150 DpResId(RID_STR_BASIC_LIB)
151 ) ),
152 m_xDialogLibTypeInfo( new Package::TypeInfo(
153 u"application/vnd.sun.star.dialog-library"_ustr,
154 OUString() /* no file filter */,
155 DpResId(RID_STR_DIALOG_LIB)
156 ) ),
157 m_typeInfos{ m_xBasicLibTypeInfo, m_xDialogLibTypeInfo }
159 OSL_ASSERT( ! transientMode() );
161 if (!transientMode())
163 OUString dbFile = makeURL(getCachePath(), u"backenddb.xml"_ustr);
164 m_backendDb.reset(
165 new ScriptBackendDb(getComponentContext(), dbFile));
170 // XServiceInfo
171 OUString BackendImpl::getImplementationName()
173 return u"com.sun.star.comp.deployment.script.PackageRegistryBackend"_ustr;
176 sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
178 return cppu::supportsService(this, ServiceName);
181 css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
183 return { BACKEND_SERVICE_NAME };
186 void BackendImpl::addDataToDb(OUString const & url)
188 if (m_backendDb)
189 m_backendDb->addEntry(url);
192 bool BackendImpl::hasActiveEntry(std::u16string_view url)
194 if (m_backendDb)
195 return m_backendDb->hasActiveEntry(url);
196 return false;
199 // XUpdatable
201 void BackendImpl::update()
203 // Nothing to do here after fixing i70283!?
206 // XPackageRegistry
208 Sequence< Reference<deployment::XPackageTypeInfo> >
209 BackendImpl::getSupportedPackageTypes()
211 return m_typeInfos;
213 void BackendImpl::revokeEntryFromDb(std::u16string_view url)
215 if (m_backendDb)
216 m_backendDb->revokeEntry(url);
219 void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
221 if (m_backendDb)
222 m_backendDb->removeEntry(url);
225 // PackageRegistryBackend
227 Reference<deployment::XPackage> BackendImpl::bindPackage_(
228 OUString const & url, OUString const & mediaType_,
229 bool bRemoved, OUString const & identifier,
230 Reference<XCommandEnvironment> const & xCmdEnv )
232 OUString mediaType( mediaType_ );
233 if (mediaType.isEmpty())
235 // detect media-type:
236 ::ucbhelper::Content ucbContent;
237 if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
238 ucbContent.isFolder())
240 // probe for script.xlb:
241 if (create_ucb_content(
242 nullptr, makeURL( url, u"script.xlb"_ustr ),
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, u"dialog.xlb"_ustr ),
248 xCmdEnv, false /* no throw */ ))
249 mediaType = "application/vnd.sun.star.dialog-library";
251 if (mediaType.isEmpty())
252 throw lang::IllegalArgumentException(
253 StrCannotDetectMediaType() + url,
254 static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
257 OUString type, subType;
258 INetContentTypeParameterList params;
259 if (INetContentTypes::parse( mediaType, type, subType, &params ))
261 if (type.equalsIgnoreAsciiCase("application"))
263 OUString dialogURL( makeURL( url, u"dialog.xlb"_ustr ) );
264 if (! create_ucb_content(
265 nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
266 dialogURL.clear();
269 if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
271 OUString scriptURL( makeURL( url, u"script.xlb"_ustr));
272 if (! create_ucb_content(
273 nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
274 scriptURL.clear();
277 return new PackageImpl(
278 this, url, xCmdEnv, scriptURL,
279 dialogURL, bRemoved, identifier);
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);
291 throw lang::IllegalArgumentException(
292 StrUnsupportedMediaType() + mediaType,
293 static_cast<OWeakObject *>(this),
294 static_cast<sal_Int16>(-1) );
298 // Package
299 BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
301 BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
302 if (nullptr == pBackend)
304 //May throw a DisposedException
305 check();
306 //We should never get here...
307 throw RuntimeException(
308 u"Failed to get the BackendImpl"_ustr,
309 static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
311 return pBackend;
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 */ )
320 BackendImpl * that = getMyBackend();
321 Reference< deployment::XPackage > xThisPackage( this );
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 */ ) );
329 void
330 lcl_maybeRemoveScript(
331 bool const bExists,
332 OUString const& rName,
333 std::u16string_view rScriptURL,
334 Reference<css::script::XLibraryContainer3> const& xScriptLibs)
336 if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
338 const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
339 if (sScriptUrl == rScriptURL)
340 xScriptLibs->removeLibrary(rName);
344 bool
345 lcl_maybeAddScript(
346 bool const bExists,
347 OUString const& rName,
348 OUString const& rScriptURL,
349 Reference<css::script::XLibraryContainer3> const& xScriptLibs)
351 if (!bExists || !xScriptLibs)
352 return false;
354 bool bCanAdd = true;
355 if (xScriptLibs->hasByName(rName))
357 const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
358 //We assume here that library names in extensions are unique, which may not be the case
359 //ToDo: If the script exist in another extension, then both extensions must have the
360 //same id
361 if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
362 || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
363 || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
364 || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
366 xScriptLibs->removeLibrary(rName);
367 bCanAdd = true;
369 else
371 bCanAdd = false;
375 if (bCanAdd)
377 xScriptLibs->createLibraryLink(rName, rScriptURL, false);
378 return xScriptLibs->hasByName(rName);
381 return false;
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 */ )
391 BackendImpl * that = getMyBackend();
393 Reference< deployment::XPackage > xThisPackage( this );
394 Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
396 bool bScript = !m_scriptURL.isEmpty();
397 Reference<css::script::XLibraryContainer3> xScriptLibs;
399 bool bDialog = !m_dialogURL.isEmpty();
400 Reference<css::script::XLibraryContainer3> xDialogLibs;
402 bool bRunning = !startup && office_is_running();
403 if( bRunning )
405 if( bScript )
407 xScriptLibs.set(
408 xComponentContext->getServiceManager()->createInstanceWithContext(
409 u"com.sun.star.script.ApplicationScriptLibraryContainer"_ustr,
410 xComponentContext ), UNO_QUERY_THROW );
413 if( bDialog )
415 xDialogLibs.set(
416 xComponentContext->getServiceManager()->createInstanceWithContext(
417 u"com.sun.star.script.ApplicationDialogLibraryContainer"_ustr,
418 xComponentContext ), UNO_QUERY_THROW );
421 bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
422 if( !doRegisterPackage )
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.
434 if (bRegistered)
436 //we also prevent and live deployment at startup
437 if (!isRemoved() && !startup)
439 lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
440 lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
442 getMyBackend()->revokeEntryFromDb(getURL());
443 return;
446 if (bRegistered)
447 return; // Already registered
449 // Update LibraryContainer
450 bool bScriptSuccess = false;
451 bool bDialogSuccess = false;
452 if (!startup)
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);
460 bool bSuccess = bScript || bDialog; // Something must have happened
461 if( bRunning )
462 if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
463 bSuccess = false;
465 if (bSuccess)
466 getMyBackend()->addDataToDb(getURL());
469 } // anon namespace
471 } // namespace dp_registry::backend::script
473 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
474 com_sun_star_comp_deployment_script_PackageRegistryBackend_get_implementation(
475 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
477 return cppu::acquire(new dp_registry::backend::script::BackendImpl(args, context));
480 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */