summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment/registry
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /desktop/source/deployment/registry
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'desktop/source/deployment/registry')
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.cxx131
-rw-r--r--desktop/source/deployment/registry/component/dp_compbackenddb.hxx95
-rw-r--r--desktop/source/deployment/registry/component/dp_component.cxx1720
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configuration.cxx818
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx161
-rw-r--r--desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx74
-rw-r--r--desktop/source/deployment/registry/dp_backend.cxx771
-rw-r--r--desktop/source/deployment/registry/dp_backenddb.cxx655
-rw-r--r--desktop/source/deployment/registry/dp_registry.cxx528
-rw-r--r--desktop/source/deployment/registry/executable/dp_executable.cxx336
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx55
-rw-r--r--desktop/source/deployment/registry/help/README.md1
-rw-r--r--desktop/source/deployment/registry/help/dp_help.cxx621
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.cxx126
-rw-r--r--desktop/source/deployment/registry/help/dp_helpbackenddb.hxx70
-rw-r--r--desktop/source/deployment/registry/inc/dp_backend.h287
-rw-r--r--desktop/source/deployment/registry/inc/dp_backenddb.hxx165
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.cxx111
-rw-r--r--desktop/source/deployment/registry/package/dp_extbackenddb.hxx69
-rw-r--r--desktop/source/deployment/registry/package/dp_package.cxx1594
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_lib_container.h48
-rw-r--r--desktop/source/deployment/registry/script/dp_script.cxx480
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx64
-rw-r--r--desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx55
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx103
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx64
-rw-r--r--desktop/source/deployment/registry/sfwk/dp_sfwk.cxx378
29 files changed, 9708 insertions, 0 deletions
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.cxx b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
new file mode 100644
index 000000000..fd9bb2c61
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.cxx
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_compbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/component-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"comp";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"component-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"component";
+
+namespace dp_registry::backend::component {
+
+ComponentBackendDb::ComponentBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ComponentBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ComponentBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ComponentBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ComponentBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ComponentBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> componentNode = writeKeyElement(url);
+ writeSimpleElement(u"java-type-library",
+ OUString::boolean(data.javaTypeLibrary),
+ componentNode);
+
+ writeSimpleList(
+ data.implementationNames,
+ u"implementation-names",
+ u"name",
+ componentNode);
+
+ writeVectorOfPair(
+ data.singletons,
+ u"singletons",
+ u"item",
+ u"key",
+ u"value",
+ componentNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ComponentBackendDb::Data ComponentBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ComponentBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ bool bJava = readSimpleElement(u"java-type-library", aNode) == "true";
+ retData.javaTypeLibrary = bJava;
+
+ retData.implementationNames =
+ readList( aNode, u"implementation-names", u"name");
+
+ retData.singletons =
+ readVectorOfPair( aNode, u"singletons", u"item", u"key", u"value");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+} // namespace dp_registry::backend::component
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_compbackenddb.hxx b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
new file mode 100644
index 000000000..84153b6fa
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_compbackenddb.hxx
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <deque>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry::backend::component {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+<component-backend-db xmlns="http://openoffice.org/extensionmanager/component-registry/2010">
+ <component url="vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages/5CD5.tmp_/leaves1.oxt/extensionoptions.jar">
+ <name>FileName</name>
+ <java-type-library>true</java-type-library>
+ <implementation-names>
+ <name>com.sun.star.comp.extensionoptions.OptionsEventHandler$_OptionsEventHandler</name>
+ ...
+ </implementation-names>
+ <singletons>
+ <item>
+ <key>com.sun.star.java.theJavaVirtualMachine</key>
+ <value>com.sun.star.java.JavaVirtualMachine</value>
+ </item>
+ ...
+ </singletons>
+ </component>
+
+ <component ...>
+ ...
+</component-backend-db>
+ */
+class ComponentBackendDb: public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ Data(): javaTypeLibrary(false) {};
+
+ std::deque< OUString> implementationNames;
+ std::vector< std::pair< OUString, OUString> >singletons;
+ // map from singleton names to implementation names
+ bool javaTypeLibrary;
+ };
+
+public:
+
+ ComponentBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+ void addEntry(OUString const & url, Data const & data);
+
+ Data getEntry(std::u16string_view url);
+
+
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/component/dp_component.cxx b/desktop/source/deployment/registry/component/dp_component.cxx
new file mode 100644
index 000000000..b9aa6518a
--- /dev/null
+++ b/desktop/source/deployment/registry/component/dp_component.cxx
@@ -0,0 +1,1720 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_misc.h>
+#include <dp_shared.hxx>
+#include <dp_backend.h>
+#include <dp_platform.hxx>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XSet.hpp>
+#include <com/sun/star/registry/XSimpleRegistry.hpp>
+#include <com/sun/star/registry/XImplementationRegistration.hpp>
+#include <com/sun/star/loader/XImplementationLoader.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <algorithm>
+#include <deque>
+#include <memory>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+#include "dp_compbackenddb.hxx"
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::component {
+namespace {
+
+/** return a vector of bootstrap variables which have been provided
+ as command arguments.
+*/
+std::vector<OUString> getCmdBootstrapVariables()
+{
+ std::vector<OUString> ret;
+ sal_uInt32 count = osl_getCommandArgCount();
+ for (sal_uInt32 i = 0; i < count; i++)
+ {
+ OUString arg;
+ osl_getCommandArg(i, &arg.pData);
+ if (arg.startsWith("-env:"))
+ ret.push_back(arg);
+ }
+ return ret;
+}
+
+bool jarManifestHeaderPresent(
+ OUString const & url, OUString const & name,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString buf = "vnd.sun.star.zip://"
+ + ::rtl::Uri::encode(
+ url, rtl_UriCharClassRegName, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 )
+ + "/META-INF/MANIFEST.MF";
+ ::ucbhelper::Content manifestContent;
+ OUString line;
+ return
+ create_ucb_content(
+ &manifestContent, buf, xCmdEnv,
+ false /* no throw */ )
+ && readLine( &line, name, manifestContent, RTL_TEXTENCODING_ASCII_US );
+}
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ComponentPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_loader;
+
+ enum class Reg { Uninit, Void, Registered, NotRegistered, MaybeRegistered };
+ Reg m_registered;
+
+ void getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > *
+ factories,
+ Reference<XComponentContext> const & xContext );
+
+ void componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories);
+
+ void componentLiveRemoval(ComponentBackendDb::Data const & data);
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ Reference<registry::XSimpleRegistry> getRDB() const;
+
+ public:
+ ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString loader, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class ComponentPackageImpl;
+
+ class ComponentsPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ public:
+ ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class ComponentsPackageImpl;
+
+ class TypelibraryPackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const bool m_jarFile;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved,
+ OUString const & identifier);
+ };
+ friend class TypelibraryPackageImpl;
+
+ /** Serves for unregistering packages that were registered on a
+ different platform. This can happen if one has remotely mounted
+ /home, for example.
+ */
+ class OtherPlatformPackageImpl : public ::dp_registry::backend::Package
+ {
+ public:
+ OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString platform);
+
+ private:
+ BackendImpl * getMyBackend() const;
+
+ Reference<registry::XSimpleRegistry> impl_openRDB() const;
+ Reference<XInterface> impl_createInstance(OUString const& rService) const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ private:
+ OUString const m_aPlatform;
+ };
+ friend class OtherPlatformPackageImpl;
+
+ std::deque<OUString> m_jar_typelibs;
+ std::deque<OUString> m_rdb_typelibs;
+ std::deque<OUString> m_components;
+
+ enum RcItem { RCITEM_JAR_TYPELIB, RCITEM_RDB_TYPELIB, RCITEM_COMPONENTS };
+
+ std::deque<OUString> & getRcItemList( RcItem kind ) {
+ switch (kind)
+ {
+ case RCITEM_JAR_TYPELIB:
+ return m_jar_typelibs;
+ case RCITEM_RDB_TYPELIB:
+ return m_rdb_typelibs;
+ default: // case RCITEM_COMPONENTS
+ return m_components;
+ }
+ }
+
+ bool m_unorc_inited;
+ bool m_unorc_modified;
+ bool bSwitchedRdbFiles;
+
+ typedef std::unordered_map< OUString, Reference<XInterface> > t_string2object;
+ t_string2object m_backendObjects;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xDynComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xPythonComponentTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xComponentsTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xRDBTypelibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xJavaTypelibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ OUString m_commonRDB;
+ OUString m_nativeRDB;
+
+ //URLs of the original rdbs (before any switching):
+ OUString m_commonRDB_orig;
+ OUString m_nativeRDB_orig;
+
+ std::unique_ptr<ComponentBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ComponentBackendDb::Data const & data);
+ ComponentBackendDb::Data readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ Reference<registry::XSimpleRegistry> m_xCommonRDB;
+ Reference<registry::XSimpleRegistry> m_xNativeRDB;
+
+ void unorc_verify_init( Reference<XCommandEnvironment> const & xCmdEnv );
+ void unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ Reference<XInterface> getObject( OUString const & id );
+ Reference<XInterface> insertObject(
+ OUString const & id, Reference<XInterface> const & xObject );
+ void releaseObject( OUString const & id );
+
+ void addToUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void removeFromUnoRc( RcItem kind, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ bool hasInUnoRc( RcItem kind, OUString const & url );
+
+ css::uno::Reference< css::uno::XComponentContext > getRootContext() const;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+
+ //Will be called from ComponentPackageImpl
+ void initServiceRdbFiles();
+};
+
+
+BackendImpl::ComponentPackageImpl::ComponentPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ OUString loader, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_loader(std::move( loader )),
+ m_registered( Reg::Uninit )
+{}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::ComponentPackageImpl::getRDB() const
+{
+ BackendImpl * that = getMyBackend();
+
+ //Late "initialization" of the services rdb files
+ //This is to prevent problems when running several
+ //instances of OOo with root rights in parallel. This
+ //would otherwise cause problems when copying the rdbs.
+ //See http://qa.openoffice.org/issues/show_bug.cgi?id=99257
+ {
+ const ::osl::MutexGuard guard( m_aMutex );
+ if (!that->bSwitchedRdbFiles)
+ {
+ that->bSwitchedRdbFiles = true;
+ that->initServiceRdbFiles();
+ }
+ }
+ if ( m_loader == "com.sun.star.loader.SharedLibrary" )
+ return that->m_xNativeRDB;
+ else
+ return that->m_xCommonRDB;
+}
+
+BackendImpl * BackendImpl::ComponentPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+
+void BackendImpl::disposing()
+{
+ try {
+ m_backendObjects = t_string2object();
+ if (m_xNativeRDB.is()) {
+ m_xNativeRDB->close();
+ m_xNativeRDB.clear();
+ }
+ if (m_xCommonRDB.is()) {
+ m_xCommonRDB->close();
+ m_xCommonRDB.clear();
+ }
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing...",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+void BackendImpl::initServiceRdbFiles()
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ ::ucbhelper::Content cacheDir( getCachePath(), xCmdEnv, m_xComponentContext );
+ ::ucbhelper::Content oldRDB;
+ // switch common rdb:
+ if (!m_commonRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL( getCachePath(), m_commonRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ m_commonRDB = m_commonRDB_orig == "common.rdb" ? std::u16string_view(u"common_.rdb") : std::u16string_view(u"common.rdb");
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_commonRDB, NameClash::OVERWRITE );
+ oldRDB = ::ucbhelper::Content();
+ }
+ // switch native rdb:
+ if (!m_nativeRDB_orig.isEmpty())
+ {
+ (void)create_ucb_content(
+ &oldRDB, makeURL(getCachePath(), m_nativeRDB_orig),
+ xCmdEnv, false /* no throw */ );
+ }
+ const OUString plt_rdb( getPlatformString() + ".rdb" );
+ const OUString plt_rdb_( getPlatformString() + "_.rdb" );
+ m_nativeRDB = (m_nativeRDB_orig == plt_rdb ) ? plt_rdb_ : plt_rdb;
+ if (oldRDB.get().is())
+ {
+ cacheDir.transferContent(
+ oldRDB, ::ucbhelper::InsertOperation::Copy,
+ m_nativeRDB, NameClash::OVERWRITE );
+ }
+
+ // UNO is bootstrapped, flush for next process start:
+ m_unorc_modified = true;
+ unorc_flush( Reference<XCommandEnvironment>() );
+
+
+ // common rdb for java, native rdb for shared lib components
+ if (!m_commonRDB.isEmpty()) {
+ m_xCommonRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_commonRDB ),
+ false, true);
+ }
+ if (!m_nativeRDB.isEmpty()) {
+ m_xNativeRDB.set(
+ m_xComponentContext->getServiceManager()
+ ->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ m_xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open(
+ makeURL( expandUnoRcUrl(getCachePath()), m_nativeRDB ),
+ false, true);
+ }
+}
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_unorc_inited( false ),
+ m_unorc_modified( false ),
+ bSwitchedRdbFiles(false),
+ m_xDynComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString(),
+ "*" SAL_DLLEXTENSION,
+ DpResId(RID_STR_DYN_COMPONENT)
+ ) ),
+ m_xJavaComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_COMPONENT)
+ ) ),
+ m_xPythonComponentTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-component;type=Python",
+ "*.py",
+ DpResId(
+ RID_STR_PYTHON_COMPONENT)
+ ) ),
+ m_xComponentsTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-components",
+ "*.components",
+ DpResId(RID_STR_COMPONENTS)
+ ) ),
+ m_xRDBTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=RDB",
+ "*.rdb",
+ DpResId(RID_STR_RDB_TYPELIB)
+ ) ),
+ m_xJavaTypelibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.uno-typelibrary;type=Java",
+ "*.jar",
+ DpResId(RID_STR_JAVA_TYPELIB)
+ ) ),
+ m_typeInfos{ m_xDynComponentTypeInfo, m_xJavaComponentTypeInfo, m_xPythonComponentTypeInfo,
+ m_xComponentsTypeInfo, m_xRDBTypelibTypeInfo, m_xJavaTypelibTypeInfo }
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // in-mem rdbs:
+ // common rdb for java, native rdb for shared lib components
+ m_xCommonRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xCommonRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ m_xNativeRDB.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry",
+ xComponentContext ), UNO_QUERY_THROW );
+ m_xNativeRDB->open( OUString() /* in-mem */,
+ false /* ! read-only */, true /* create */ );
+ }
+ else
+ {
+ unorc_verify_init( xCmdEnv );
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ComponentBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.component.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ComponentBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ComponentBackendDb::Data BackendImpl::readDataFromDb(std::u16string_view url)
+{
+ ComponentBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType(mediaType_);
+ if ( mediaType.isEmpty() || mediaType == "application/vnd.sun.star.uno-component" || mediaType == "application/vnd.sun.star.uno-typelibrary" )
+ {
+ // detect exact media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv )) {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(SAL_DLLEXTENSION))
+ {
+ mediaType = "application/vnd.sun.star.uno-component;type=native;platform=" +
+ getPlatformString();
+ }
+ else if (title.endsWithIgnoreAsciiCase(".jar"))
+ {
+ if (jarManifestHeaderPresent(
+ url, "RegistrationClassName", xCmdEnv ))
+ mediaType = "application/vnd.sun.star.uno-component;type=Java";
+ if (mediaType.isEmpty())
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=Java";
+ }
+ else if (title.endsWithIgnoreAsciiCase(".py"))
+ mediaType = "application/vnd.sun.star.uno-component;type=Python";
+ else if (title.endsWithIgnoreAsciiCase(".rdb"))
+ mediaType = "application/vnd.sun.star.uno-typelibrary;type=RDB";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-component"))
+ {
+ // xxx todo: probe and evaluate component xml description
+
+ auto const iter = params.find(OString("platform"));
+ bool bPlatformFits(iter == params.end());
+ OUString aPlatform;
+ if (!bPlatformFits) // platform is specified, we have to check
+ {
+ aPlatform = iter->second.m_sValue;
+ bPlatformFits = platform_fits(aPlatform);
+ }
+ // If the package is being removed, do not care whether
+ // platform fits. We won't be using it anyway.
+ if (bPlatformFits || bRemoved) {
+ auto const iterType = params.find(OString("type"));
+ if (iterType != params.end())
+ {
+ OUString const & value = iterType->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("native")) {
+ if (bPlatformFits)
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ "com.sun.star.loader.SharedLibrary",
+ bRemoved, identifier);
+ else
+ return new BackendImpl::OtherPlatformPackageImpl(
+ this, url, name, m_xDynComponentTypeInfo,
+ bRemoved, identifier, aPlatform);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xJavaComponentTypeInfo,
+ "com.sun.star.loader.Java2",
+ bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Python")) {
+ return new BackendImpl::ComponentPackageImpl(
+ this, url, name, m_xPythonComponentTypeInfo,
+ "com.sun.star.loader.Python",
+ bRemoved, identifier);
+ }
+ }
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase("vnd.sun.star.uno-components"))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter == params.end() || platform_fits(iter->second.m_sValue)) {
+ return new BackendImpl::ComponentsPackageImpl(
+ this, url, name, m_xComponentsTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-typelibrary"))
+ {
+ auto const iter = params.find(OString("type"));
+ if (iter != params.end()) {
+ OUString const & value = iter->second.m_sValue;
+ if (value.equalsIgnoreAsciiCase("RDB"))
+ {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xRDBTypelibTypeInfo,
+ false /* rdb */, bRemoved, identifier);
+ }
+ if (value.equalsIgnoreAsciiCase("Java")) {
+ return new BackendImpl::TypelibraryPackageImpl(
+ this, url, name, m_xJavaTypelibTypeInfo,
+ true /* jar */, bRemoved, identifier);
+ }
+ }
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::unorc_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( m_aMutex );
+ if ( m_unorc_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "unorc" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "UNO_JAVA_CLASSPATH=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = sizeof ("UNO_JAVA_CLASSPATH=") - 1;
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty())
+ {
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The jar file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_jar_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_TYPES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = sizeof ("UNO_TYPES=") - 1;
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.copy( 1 );
+ if (create_ucb_content(
+ nullptr, expandUnoRcTerm(token), xCmdEnv,
+ false /* no throw */ ))
+ {
+ //The RDB file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the unorc.
+ //After running XExtensionManager::synchronize, the unorc is
+ //cleaned up
+ m_rdb_typelibs.push_back( token );
+ }
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ // The UNO_SERVICES line always has the BNF form
+ // "UNO_SERVICES="
+ // ("?$ORIGIN/" <common-rdb>)? -- first
+ // "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}"? -- second
+ // ("?" ("BUNDLED_EXTENSIONS" | -- third
+ // "UNO_SHARED_PACKAGES_CACHE" | "UNO_USER_PACKAGES_CACHE")
+ // ...)*
+ // so can unambiguously be split into its three parts:
+ int state = 1;
+ for (sal_Int32 i = RTL_CONSTASCII_LENGTH("UNO_SERVICES=");
+ i >= 0;)
+ {
+ OUString token(line.getToken(0, ' ', i));
+ if (!token.isEmpty())
+ {
+ if (state == 1 && token.match("?$ORIGIN/"))
+ {
+ m_commonRDB_orig = token.copy(
+ RTL_CONSTASCII_LENGTH("?$ORIGIN/"));
+ state = 2;
+ }
+ else if ( state <= 2 && token == "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" )
+ {
+ state = 3;
+ }
+ else
+ {
+ if (token[0] == '?')
+ {
+ token = token.copy(1);
+ }
+ m_components.push_back(token);
+ state = 3;
+ }
+ }
+ }
+ }
+
+ // native rc:
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), getPlatformString() + "rc"),
+ xCmdEnv, false /* no throw */ )) {
+ if (readLine( &line, "UNO_SERVICES=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ m_nativeRDB_orig = line.copy(
+ sizeof ("UNO_SERVICES=?$ORIGIN/") - 1 );
+ }
+ }
+ }
+ m_unorc_modified = false;
+ m_unorc_inited = true;
+}
+
+
+void BackendImpl::unorc_flush( Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_unorc_inited || !m_unorc_modified)
+ return;
+
+ OStringBuffer buf;
+
+ buf.append("ORIGIN=");
+ OUString sOrigin = dp_misc::makeRcTerm(m_cachePath);
+ OString osOrigin = OUStringToOString(sOrigin, RTL_TEXTENCODING_UTF8);
+ buf.append(osOrigin);
+ buf.append(LF);
+
+ if (! m_jar_typelibs.empty())
+ {
+ auto iPos( m_jar_typelibs.cbegin() );
+ auto const iEnd( m_jar_typelibs.cend() );
+ buf.append( "UNO_JAVA_CLASSPATH=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_rdb_typelibs.empty())
+ {
+ auto iPos( m_rdb_typelibs.cbegin() );
+ auto const iEnd( m_rdb_typelibs.cend() );
+ buf.append( "UNO_TYPES=" );
+ while (iPos != iEnd) {
+ buf.append( '?' );
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // If we duplicated the common or native rdb then we must use those urls
+ //otherwise we use those of the original files. That is, m_commonRDB_orig
+ //and m_nativeRDB_orig;
+ OUString sCommonRDB(m_commonRDB.isEmpty() ? m_commonRDB_orig : m_commonRDB );
+ OUString sNativeRDB(m_nativeRDB.isEmpty() ? m_nativeRDB_orig : m_nativeRDB );
+
+ if (!sCommonRDB.isEmpty() || !sNativeRDB.isEmpty() ||
+ !m_components.empty())
+ {
+ buf.append( "UNO_SERVICES=" );
+ bool space = false;
+ if (!sCommonRDB.isEmpty())
+ {
+ buf.append( "?$ORIGIN/" );
+ buf.append( OUStringToOString(
+ sCommonRDB, RTL_TEXTENCODING_ASCII_US ) );
+ space = true;
+ }
+ if (!sNativeRDB.isEmpty())
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append( "${$ORIGIN/${_OS}_${_ARCH}rc:UNO_SERVICES}" );
+ space = true;
+
+ // write native rc:
+ OString buf2 =
+ "ORIGIN=" +
+ osOrigin +
+ OStringChar(LF) +
+ "UNO_SERVICES=?$ORIGIN/" +
+ OUStringToOString( sNativeRDB, RTL_TEXTENCODING_ASCII_US ) +
+ OStringChar(LF);
+
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf2.getStr()),
+ buf2.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), getPlatformString() + "rc" ),
+ xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+ }
+ for (auto const& component : m_components)
+ {
+ if (space)
+ {
+ buf.append(' ');
+ }
+ buf.append('?');
+ buf.append(OUStringToOString(component, RTL_TEXTENCODING_UTF8));
+ space = true;
+ }
+ buf.append(LF);
+ }
+
+ // write unorc:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "unorc" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_unorc_modified = false;
+}
+
+
+void BackendImpl::addToUnoRc( RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getRcItemList(kind);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+ }
+}
+
+
+void BackendImpl::removeFromUnoRc(
+ RcItem kind, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ unorc_verify_init( xCmdEnv );
+ std::deque<OUString> & aRcItemList = getRcItemList(kind);
+ aRcItemList.erase(std::remove(aRcItemList.begin(), aRcItemList.end(), rcterm), aRcItemList.end());
+ // write immediately:
+ m_unorc_modified = true;
+ unorc_flush( xCmdEnv );
+}
+
+
+bool BackendImpl::hasInUnoRc(
+ RcItem kind, OUString const & url_ )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ std::deque<OUString> const & rSet = getRcItemList(kind);
+ return std::find( rSet.begin(), rSet.end(), rcterm ) != rSet.end();
+}
+
+css::uno::Reference< css::uno::XComponentContext > BackendImpl::getRootContext()
+ const
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getComponentContext()->getValueByName("_root"),
+ css::uno::UNO_QUERY);
+ return rootContext.is() ? rootContext : getComponentContext();
+}
+
+
+void BackendImpl::releaseObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ m_backendObjects.erase( id );
+}
+
+
+Reference<XInterface> BackendImpl::getObject( OUString const & id )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ const t_string2object::const_iterator iFind( m_backendObjects.find( id ) );
+ if (iFind == m_backendObjects.end())
+ return Reference<XInterface>();
+ else
+ return iFind->second;
+}
+
+
+Reference<XInterface> BackendImpl::insertObject(
+ OUString const & id, Reference<XInterface> const & xObject )
+{
+ const ::osl::MutexGuard guard( m_aMutex );
+ const std::pair<t_string2object::iterator, bool> insertion(
+ m_backendObjects.emplace( id, xObject ) );
+ return insertion.first->second;
+}
+
+
+Reference<XComponentContext> raise_uno_process(
+ Reference<XComponentContext> const & xContext,
+ ::rtl::Reference<AbortChannel> const & abortChannel )
+{
+ OSL_ASSERT( xContext.is() );
+
+ OUString url( util::theMacroExpander::get(xContext)->expandMacros( "$URE_BIN_DIR/uno" ) );
+
+ const OUString connectStr = "uno:pipe,name=" + generateRandomPipeId() + ";urp;uno.ComponentContext";
+
+ // raise core UNO process to register/run a component,
+ // javavm service uses unorc next to executable to retrieve deployed
+ // jar typelibs
+
+ std::vector<OUString> args{
+#if OSL_DEBUG_LEVEL == 0
+ "--quiet",
+#endif
+ "--singleaccept",
+ "-u",
+ connectStr,
+ // don't inherit from unorc:
+ "-env:INIFILENAME=" };
+
+ //now add the bootstrap variables which were supplied on the command line
+ std::vector<OUString> bootvars = getCmdBootstrapVariables();
+ args.insert(args.end(), bootvars.begin(), bootvars.end());
+
+ oslProcess hProcess;
+ try {
+ hProcess = raiseProcess(
+ url, comphelper::containerToSequence(args) );
+ }
+ catch (...) {
+ OUStringBuffer sMsg = "error starting process: " + url;
+ for(const auto& arg : args)
+ sMsg.append(" " + arg);
+ throw uno::RuntimeException(sMsg.makeStringAndClear());
+ }
+ try {
+ return Reference<XComponentContext>(
+ resolveUnoURL( connectStr, xContext, abortChannel.get() ),
+ UNO_QUERY_THROW );
+ }
+ catch (...) {
+ // try to terminate process:
+ if ( osl_terminateProcess( hProcess ) != osl_Process_E_None )
+ {
+ OSL_ASSERT( false );
+ }
+ throw;
+ }
+}
+
+void extractComponentData(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::registry::XRegistryKey > const & registry,
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ css::uno::Reference< css::loader::XImplementationLoader > const &
+ componentLoader,
+ OUString const & componentUrl)
+{
+ OSL_ASSERT(
+ context.is() && registry.is() && data != nullptr && componentLoader.is());
+ OUString registryName(registry->getKeyName());
+ sal_Int32 prefix = registryName.getLength();
+ if (!registryName.endsWith("/")) {
+ prefix += RTL_CONSTASCII_LENGTH("/");
+ }
+ const css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > >
+ keys(registry->openKeys());
+ css::uno::Reference< css::lang::XMultiComponentFactory > smgr(
+ context->getServiceManager(), css::uno::UNO_SET_THROW);
+ for (css::uno::Reference< css::registry::XRegistryKey > const & key : keys) {
+ OUString name(key->getKeyName().copy(prefix));
+ data->implementationNames.push_back(name);
+ css::uno::Reference< css::registry::XRegistryKey > singletons(
+ key->openKey("UNO/SINGLETONS"));
+ if (singletons.is()) {
+ sal_Int32 prefix2 = key->getKeyName().getLength() +
+ RTL_CONSTASCII_LENGTH("/UNO/SINGLETONS/");
+ const css::uno::Sequence<
+ css::uno::Reference< css::registry::XRegistryKey > >
+ singletonKeys(singletons->openKeys());
+ for (css::uno::Reference< css::registry::XRegistryKey > const & singletonKey : singletonKeys) {
+ data->singletons.emplace_back(
+ singletonKey->getKeyName().copy(prefix2), name);
+ }
+ }
+ if (factories != nullptr) {
+ factories->push_back(
+ componentLoader->activate(
+ name, OUString(), componentUrl, key));
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::getComponentInfo(
+ ComponentBackendDb::Data * data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > * factories,
+ Reference<XComponentContext> const & xContext )
+{
+ const Reference<loader::XImplementationLoader> xLoader(
+ xContext->getServiceManager()->createInstanceWithContext(
+ m_loader, xContext ), UNO_QUERY );
+ if (! xLoader.is())
+ {
+ throw css::deployment::DeploymentException(
+ "cannot instantiate loader " + m_loader,
+ static_cast< OWeakObject * >(this), Any());
+ }
+
+ // HACK: highly dependent on stoc/source/servicemanager
+ // and stoc/source/implreg implementation which rely on the same
+ // services.rdb format!
+ // .../UNO/LOCATION and .../UNO/ACTIVATOR appear not to be written by
+ // writeRegistryInfo, however, but are known, fixed values here, so
+ // can be passed into extractComponentData
+ OUString url(getURL());
+ const Reference<registry::XSimpleRegistry> xMemReg(
+ xContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.SimpleRegistry", xContext ),
+ UNO_QUERY_THROW );
+ xMemReg->open( OUString() /* in mem */, false, true );
+ xLoader->writeRegistryInfo( xMemReg->getRootKey(), OUString(), url );
+ extractComponentData(
+ xContext, xMemReg->getRootKey(), data, factories, xLoader, url);
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveInsertion(
+ ComponentBackendDb::Data const & data,
+ std::vector< css::uno::Reference< css::uno::XInterface > > const &
+ factories)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ std::vector< css::uno::Reference< css::uno::XInterface > >::const_iterator
+ factory(factories.begin());
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->insert(css::uno::Any(*factory++));
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "implementation already registered " << implementationName);
+ }
+ }
+ if (data.singletons.empty()) return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Update should be atomic:
+ try {
+ cont->removeByName( name + "/arguments");
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->insertByName( name + "/service", css::uno::Any(singleton.second));
+ } catch (const container::ElementExistException &) {
+ cont->replaceByName( name + "/service", css::uno::Any(singleton.second));
+ }
+ try {
+ cont->insertByName(name, css::uno::Any());
+ } catch (const container::ElementExistException &) {
+ SAL_WARN("desktop.deployment", "singleton already registered " << singleton.first);
+ cont->replaceByName(name, css::uno::Any());
+ }
+ }
+}
+
+void BackendImpl::ComponentPackageImpl::componentLiveRemoval(
+ ComponentBackendDb::Data const & data)
+{
+ css::uno::Reference< css::uno::XComponentContext > rootContext(
+ getMyBackend()->getRootContext());
+ css::uno::Reference< css::container::XSet > set(
+ rootContext->getServiceManager(), css::uno::UNO_QUERY_THROW);
+ for (auto const& implementationName : data.implementationNames)
+ {
+ try {
+ set->remove(css::uno::Any(implementationName));
+ } catch (const css::container::NoSuchElementException &) {
+ // ignore if factory has not been live deployed
+ }
+ }
+ if (data.singletons.empty())
+ return;
+
+ css::uno::Reference< css::container::XNameContainer > cont(
+ rootContext, css::uno::UNO_QUERY_THROW);
+ for (auto const& singleton : data.singletons)
+ {
+ OUString name("/singletons/" + singleton.first);
+ //TODO: Removal should be atomic:
+ try {
+ cont->removeByName(name);
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/service" );
+ } catch (const container::NoSuchElementException &) {}
+ try {
+ cont->removeByName( name + "/arguments" );
+ } catch (const container::NoSuchElementException &) {}
+ }
+}
+
+// Package
+
+//We could use here BackendImpl::hasActiveEntry. However, this check is just as well.
+//And it also shows the problem if another extension has overwritten an implementation
+//entry, because it contains the same service implementation
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & )
+{
+ if (m_registered == Reg::Uninit)
+ {
+ m_registered = Reg::NotRegistered;
+ const Reference<registry::XSimpleRegistry> xRDB( getRDB() );
+ if (xRDB.is())
+ {
+ bool bAmbiguousComponentName = false;
+ // lookup rdb for location URL:
+ const Reference<registry::XRegistryKey> xRootKey(
+ xRDB->getRootKey() );
+ const Reference<registry::XRegistryKey> xImplKey(
+ xRootKey->openKey( "IMPLEMENTATIONS" ) );
+ Sequence<OUString> implNames;
+ if (xImplKey.is() && xImplKey->isValid())
+ implNames = xImplKey->getKeyNames();
+ OUString const * pImplNames = implNames.getConstArray();
+ sal_Int32 pos = implNames.getLength();
+ for ( ; pos--; )
+ {
+ checkAborted( abortChannel );
+ const OUString key(
+ pImplNames[ pos ] + "/UNO/LOCATION" );
+ const Reference<registry::XRegistryKey> xKey(
+ xRootKey->openKey(key) );
+ if (xKey.is() && xKey->isValid())
+ {
+ const OUString location( xKey->getAsciiValue() );
+ if (location.equalsIgnoreAsciiCase( getURL() ))
+ {
+ break;
+ }
+ else
+ {
+ //try to match only the file name
+ OUString thisUrl(getURL());
+ std::u16string_view thisFileName(thisUrl.subView(thisUrl.lastIndexOf('/')));
+
+ std::u16string_view locationFileName(location.subView(location.lastIndexOf('/')));
+ if (o3tl::equalsIgnoreAsciiCase(locationFileName, thisFileName))
+ bAmbiguousComponentName = true;
+ }
+ }
+ }
+ if (pos >= 0)
+ m_registered = Reg::Registered;
+ else if (bAmbiguousComponentName)
+ m_registered = Reg::MaybeRegistered;
+ }
+ }
+
+ //Different extensions can use the same service implementations. Then the extensions
+ //which was installed last will overwrite the one from the other extension. That is
+ //the registry will contain the path (the location) of the library or jar of the
+ //second extension. In this case isRegistered called for the lib of the first extension
+ //would return "not registered". That would mean that during uninstallation
+ //XPackage::registerPackage is not called, because it just was not registered. This is,
+ //however, necessary for jar files. Registering and unregistering update
+ //uno_packages/cache/registry/com.sun.star.comp.deployment.component.PackageRegistryBackend/unorc
+ //Therefore, we will return always "is ambiguous" if the path of this component cannot
+ //be found in the registry and if there is another path and both have the same file name (but
+ //the rest of the path is different).
+ //If the caller cannot precisely determine that this package was registered, then it must
+ //call registerPackage.
+ bool bAmbiguous = m_registered == Reg::Void // Reg::Void == we are in the progress of unregistration
+ || m_registered == Reg::MaybeRegistered;
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_registered == Reg::Registered, bAmbiguous) );
+}
+
+
+void BackendImpl::ComponentPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ ComponentBackendDb::Data data;
+ css::uno::Reference< css::uno::XComponentContext > context;
+ if (startup) {
+ context = that->getComponentContext();
+ } else {
+ context.set(that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration> impreg(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::registry::XSimpleRegistry > rdb(getRDB());
+ impreg->registerImplementation(m_loader, url, rdb);
+ // Only write to unorc after successful registration; it may fail if
+ // there is no suitable java
+ if (m_loader == "com.sun.star.loader.Java2" && !jarManifestHeaderPresent(url, "UNO-Type-Path", xCmdEnv))
+ {
+ that->addToUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ data.javaTypeLibrary = true;
+ }
+ std::vector< css::uno::Reference< css::uno::XInterface > > factories;
+ getComponentInfo(&data, startup ? nullptr : &factories, context);
+ if (!startup) {
+ try {
+ componentLiveInsertion(data, factories);
+ } catch (css::uno::Exception &) {
+ TOOLS_INFO_EXCEPTION("desktop.deployment", "caught");
+ try {
+ impreg->revokeImplementation(url, rdb);
+ } catch (css::uno::RuntimeException &) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignored");
+ }
+ throw;
+ }
+ }
+ m_registered = Reg::Registered;
+ that->addDataToDb(url, data);
+ } else { // revoke
+ m_registered = Reg::Void;
+ ComponentBackendDb::Data data(that->readDataFromDb(url));
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ bool remoteContext = context.is();
+ if (!remoteContext) {
+ context = that->getComponentContext();
+ }
+ if (!startup) {
+ componentLiveRemoval(data);
+ }
+ css::uno::Reference< css::registry::XImplementationRegistration >(
+ context->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.registry.ImplementationRegistration",
+ context),
+ css::uno::UNO_QUERY_THROW)->revokeImplementation(url, getRDB());
+ if (data.javaTypeLibrary) {
+ that->removeFromUnoRc(RCITEM_JAR_TYPELIB, url, xCmdEnv);
+ }
+ if (remoteContext) {
+ that->releaseObject(url);
+ }
+ m_registered = Reg::NotRegistered;
+ getMyBackend()->revokeEntryFromDb(url);
+ }
+}
+
+BackendImpl::TypelibraryPackageImpl::TypelibraryPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool jarFile, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_jarFile( jarFile )
+{
+}
+
+// Package
+BackendImpl * BackendImpl::TypelibraryPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException( "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<TypelibraryPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::TypelibraryPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ that->hasInUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, getURL() ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::TypelibraryPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ const OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ // live insertion:
+ if (m_jarFile) {
+ // xxx todo add to classpath at runtime: ???
+ //SB: It is probably not worth it to add the live inserted type
+ // library JAR to the UnoClassLoader in the soffice process. Any
+ // live inserted component JAR that might reference this type
+ // library JAR runs in its own uno process, so there is probably no
+ // Java code in the soffice process that would see any UNO types
+ // introduced by this type library JAR.
+ }
+ else // RDB:
+ {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->insert(
+ css::uno::Any(expandUnoRcUrl(url)));
+ }
+
+ that->addToUnoRc( m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB,
+ url, xCmdEnv );
+ }
+ else // revokePackage()
+ {
+ that->removeFromUnoRc(
+ m_jarFile ? RCITEM_JAR_TYPELIB : RCITEM_RDB_TYPELIB, url, xCmdEnv );
+
+ // revoking types at runtime, possible, sensible?
+ if (!m_jarFile) {
+ css::uno::Reference< css::container::XSet >(
+ that->getComponentContext()->getValueByName(
+ "/singletons"
+ "/com.sun.star.reflection.theTypeDescriptionManager"),
+ css::uno::UNO_QUERY_THROW)->remove(
+ css::uno::Any(expandUnoRcUrl(url)));
+ }
+ }
+}
+
+BackendImpl::OtherPlatformPackageImpl::OtherPlatformPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier, OUString platform)
+ : Package(myBackend, url, name, name, xPackageType, bRemoved, identifier)
+ , m_aPlatform(std::move(platform))
+{
+ OSL_PRECOND(bRemoved, "this class can only be used for removing packages!");
+}
+
+BackendImpl *
+BackendImpl::OtherPlatformPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<OtherPlatformPackageImpl*>(this)));
+ }
+ return pBackend;
+}
+
+Reference<registry::XSimpleRegistry>
+BackendImpl::OtherPlatformPackageImpl::impl_openRDB() const
+{
+ OUString const aRDB(m_aPlatform + ".rdb");
+ OUString const aRDBPath(makeURL(getMyBackend()->getCachePath(), aRDB));
+
+ Reference<registry::XSimpleRegistry> xRegistry;
+
+ try
+ {
+ xRegistry.set(
+ impl_createInstance("com.sun.star.registry.SimpleRegistry"),
+ UNO_QUERY)
+ ;
+ if (xRegistry.is())
+ xRegistry->open(expandUnoRcUrl(aRDBPath), false, false);
+ }
+ catch (registry::InvalidRegistryException const&)
+ {
+ // If the registry does not exist, we do not need to bother at all
+ xRegistry.set(nullptr);
+ }
+
+ SAL_WARN_IF( !xRegistry.is(), "desktop.deployment", "could not create registry for the package's platform");
+ return xRegistry;
+}
+
+Reference<XInterface>
+BackendImpl::OtherPlatformPackageImpl::impl_createInstance(OUString const& rService)
+const
+{
+ Reference<XComponentContext> const xContext(getMyBackend()->getComponentContext());
+ OSL_ASSERT(xContext.is());
+ Reference<XInterface> xService;
+ if (xContext.is())
+ xService.set(xContext->getServiceManager()->createInstanceWithContext(rService, xContext));
+ return xService;
+}
+
+beans::Optional<beans::Ambiguous<sal_Bool> >
+BackendImpl::OtherPlatformPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */ )
+{
+ return beans::Optional<beans::Ambiguous<sal_Bool> >(true,
+ beans::Ambiguous<sal_Bool>(true, false));
+}
+
+void
+BackendImpl::OtherPlatformPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard& /* guard */,
+ bool bRegisterPackage,
+ bool /* bStartup */,
+ ::rtl::Reference<AbortChannel> const& /* abortChannel */,
+ Reference<XCommandEnvironment> const& /* xCmdEnv */)
+{
+ OSL_PRECOND(!bRegisterPackage, "this class can only be used for removing packages!");
+
+ OUString const aURL(getURL());
+
+ Reference<registry::XSimpleRegistry> const xServicesRDB(impl_openRDB());
+ Reference<registry::XImplementationRegistration> const xImplReg(
+ impl_createInstance("com.sun.star.registry.ImplementationRegistration"),
+ UNO_QUERY)
+ ;
+ if (xImplReg.is() && xServicesRDB.is())
+ xImplReg->revokeImplementation(aURL, xServicesRDB);
+ if (xServicesRDB.is())
+ xServicesRDB->close();
+
+ getMyBackend()->revokeEntryFromDb(aURL);
+}
+
+BackendImpl * BackendImpl::ComponentsPackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //Throws a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ComponentsPackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ComponentsPackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true,
+ beans::Ambiguous<sal_Bool>(
+ getMyBackend()->hasInUnoRc(RCITEM_COMPONENTS, getURL()), false));
+}
+
+void BackendImpl::ComponentsPackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url(getURL());
+ if (doRegisterPackage) {
+ if (!startup) {
+ css::uno::Reference< css::uno::XComponentContext > context(
+ that->getObject(url), css::uno::UNO_QUERY);
+ if (!context.is()) {
+ context.set(
+ that->insertObject(
+ url,
+ raise_uno_process(
+ that->getComponentContext(), abortChannel)),
+ css::uno::UNO_QUERY_THROW);
+ }
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args
+ {
+ { "uri", css::uno::Any(expandUnoRcUrl(url)) },
+ { "component-context", css::uno::Any(context) }
+ };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->insert(css::uno::Any(args));
+ }
+ that->addToUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ } else { // revoke
+ that->removeFromUnoRc(RCITEM_COMPONENTS, url, xCmdEnv);
+ if (!startup) {
+ // This relies on the root component context's service manager
+ // supporting the extended XSet semantics:
+ css::uno::Sequence< css::beans::NamedValue > args { { "uri", css::uno::Any(expandUnoRcUrl(url)) } };
+ css::uno::Reference< css::container::XSet > smgr(
+ that->getRootContext()->getServiceManager(),
+ css::uno::UNO_QUERY_THROW);
+ smgr->remove(css::uno::Any(args));
+ }
+ that->releaseObject(url);
+ that->revokeEntryFromDb(url); // in case it got added with old code
+ }
+}
+
+BackendImpl::ComponentsPackageImpl::ComponentsPackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+{}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_component_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::component::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configuration.cxx b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
new file mode 100644
index 000000000..62d5ab88f
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configuration.cxx
@@ -0,0 +1,818 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+//TODO: Large parts of this file were copied from dp_component.cxx; those parts
+// should be consolidated.
+
+#include <config_extensions.h>
+
+#include <dp_backend.h>
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/string.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <comphelper/lok.hxx>
+#include <svl/inettype.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/configuration/Update.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <deque>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+#include "dp_configurationbackenddb.hxx"
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::configuration {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const ;
+
+ const bool m_isSchema;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool isSchema, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_isSchema( isSchema )
+ {}
+ };
+ friend class PackageImpl;
+
+ std::deque<OUString> m_xcs_files;
+ std::deque<OUString> m_xcu_files;
+ std::deque<OUString> & getFiles( bool xcs ) {
+ return xcs ? m_xcs_files : m_xcu_files;
+ }
+
+ bool m_configmgrini_inited;
+ bool m_configmgrini_modified;
+ std::unique_ptr<ConfigurationBackendDb> m_backendDb;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+#if HAVE_FEATURE_EXTENSIONS
+ // for backwards compatibility - nil if no (compatible) back-compat db present
+ std::unique_ptr<PersistentMap> m_registeredPackages;
+#endif
+
+ virtual void SAL_CALL disposing() override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xConfDataTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xConfSchemaTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ void configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv );
+ void configmgrini_flush( Reference<XCommandEnvironment> const & xCmdEnv );
+
+ /* The parameter isURL is false in the case of adding the conf:ini-entry
+ value from the backend db. This entry already contains the path as it
+ is used in the configmgr.ini.
+ */
+ void addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#if HAVE_FEATURE_EXTENSIONS
+ bool removeFromConfigmgrIni( bool isSchema, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv );
+#endif
+ void addDataToDb(OUString const & url, ConfigurationBackendDb::Data const & data);
+ ::std::optional<ConfigurationBackendDb::Data> readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+ bool hasActiveEntry(std::u16string_view url);
+ bool activateEntry(std::u16string_view url);
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using PackageRegistryBackend::disposing;
+};
+
+
+void BackendImpl::disposing()
+{
+ try {
+ configmgrini_flush( Reference<XCommandEnvironment>() );
+
+ PackageRegistryBackend::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing...",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_configmgrini_inited( false ),
+ m_configmgrini_modified( false ),
+ m_xConfDataTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-data",
+ "*.xcu",
+ DpResId(RID_STR_CONF_DATA)
+ ) ),
+ m_xConfSchemaTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.configuration-schema",
+ "*.xcs",
+ DpResId(RID_STR_CONF_SCHEMA)
+ ) ),
+ m_typeInfos{ m_xConfDataTypeInfo, m_xConfSchemaTypeInfo }
+{
+ const Reference<XCommandEnvironment> xCmdEnv;
+
+ if (transientMode())
+ {
+ // TODO
+ }
+ else
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ConfigurationBackendDb(getComponentContext(), dbFile));
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+
+ configmgrini_verify_init( xCmdEnv );
+
+#if HAVE_FEATURE_EXTENSIONS
+ std::unique_ptr<PersistentMap> pMap;
+ OUString aCompatURL( makeURL( getCachePath(), "registered_packages.pmap" ) );
+
+ // Don't create it if it doesn't exist already
+ if ( ::utl::UCBContentHelper::Exists( expandUnoRcUrl( aCompatURL ) ) )
+ {
+ try
+ {
+ pMap.reset( new PersistentMap( aCompatURL ) );
+ }
+ catch (const Exception &e)
+ {
+ OUString aStr = "Exception loading legacy package database: '" +
+ e.Message +
+ "' - ignoring file, please remove it.\n";
+ dp_misc::writeConsole( aStr );
+ }
+ }
+ m_registeredPackages = std::move(pMap);
+#endif
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.configuration.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ConfigurationBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+::std::optional<ConfigurationBackendDb::Data> BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ::std::optional<ConfigurationBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".xcu" )) {
+ mediaType = "application/vnd.sun.star.configuration-data";
+ }
+ if (title.endsWithIgnoreAsciiCase( ".xcs" )) {
+ mediaType = "application/vnd.sun.star.configuration-schema";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent( url, xCmdEnv, m_xComponentContext );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xConfDataTypeInfo, false /* data file */,
+ bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-schema")) {
+ return new PackageImpl(
+ this, url, name, m_xConfSchemaTypeInfo, true /* schema file */,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::configmgrini_verify_init(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ const ::osl::MutexGuard guard( m_aMutex );
+ if ( m_configmgrini_inited)
+ return;
+
+ // common rc:
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content,
+ makeURL( getCachePath(), "configmgr.ini" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ OUString line;
+ if (readLine( &line, "SCHEMA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 ))
+ {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("SCHEMA=");
+ do {
+ OUString token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.isEmpty()) {
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcs_files.push_back( token );
+ }
+ }
+ while (index >= 0);
+ }
+ if (readLine( &line, "DATA=", ucb_content,
+ RTL_TEXTENCODING_UTF8 )) {
+ sal_Int32 index = RTL_CONSTASCII_LENGTH("DATA=");
+ do {
+ std::u16string_view token( o3tl::trim(o3tl::getToken(line, 0, ' ', index )) );
+ if (!token.empty())
+ {
+ if (token[ 0 ] == '?')
+ token = token.substr( 1 );
+ //The file may not exist anymore if a shared or bundled
+ //extension was removed, but it can still be in the configmgrini.
+ //After running XExtensionManager::synchronize, the configmgrini is
+ //cleaned up
+ m_xcu_files.push_back( OUString(token) );
+ }
+ }
+ while (index >= 0);
+ }
+ }
+ m_configmgrini_modified = false;
+ m_configmgrini_inited = true;
+}
+
+
+void BackendImpl::configmgrini_flush(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (transientMode())
+ return;
+ if (!m_configmgrini_inited || !m_configmgrini_modified)
+ return;
+
+ OStringBuffer buf;
+ if (! m_xcs_files.empty())
+ {
+ auto iPos( m_xcs_files.cbegin() );
+ auto const iEnd( m_xcs_files.cend() );
+ buf.append( "SCHEMA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+ if (! m_xcu_files.empty())
+ {
+ auto iPos( m_xcu_files.cbegin() );
+ auto const iEnd( m_xcu_files.cend() );
+ buf.append( "DATA=" );
+ while (iPos != iEnd) {
+ // encoded ASCII file-urls:
+ const OString item(
+ OUStringToOString( *iPos, RTL_TEXTENCODING_ASCII_US ) );
+ buf.append( item );
+ ++iPos;
+ if (iPos != iEnd)
+ buf.append( ' ' );
+ }
+ buf.append(LF);
+ }
+
+ // write configmgr.ini:
+ const Reference<io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(buf.getStr()),
+ buf.getLength() ) );
+ ::ucbhelper::Content ucb_content(
+ makeURL( getCachePath(), "configmgr.ini" ), xCmdEnv, m_xComponentContext );
+ ucb_content.writeStream( xData, true /* replace existing */ );
+
+ m_configmgrini_modified = false;
+}
+
+
+void BackendImpl::addToConfigmgrIni( bool isSchema, bool isURL, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( isURL ? dp_misc::makeRcTerm(url_) : url_ );
+ const ::osl::MutexGuard guard( m_aMutex );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ if (std::find( rSet.begin(), rSet.end(), rcterm ) == rSet.end()) {
+ rSet.push_front( rcterm ); // prepend to list, thus overriding
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ }
+}
+
+#if HAVE_FEATURE_EXTENSIONS
+bool BackendImpl::removeFromConfigmgrIni(
+ bool isSchema, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ const OUString rcterm( dp_misc::makeRcTerm(url_) );
+ const ::osl::MutexGuard guard( m_aMutex );
+ configmgrini_verify_init( xCmdEnv );
+ std::deque<OUString> & rSet = getFiles(isSchema);
+ auto i(std::find(rSet.begin(), rSet.end(), rcterm));
+ if (i == rSet.end() && !isSchema)
+ {
+ //in case the xcu contained %origin% then the configmr.ini contains the
+ //url to the file in the user installation (e.g. $BUNDLED_EXTENSIONS_USER)
+ //However, m_url (getURL()) contains the URL for the file in the actual
+ //extension installation.
+ ::std::optional<ConfigurationBackendDb::Data> data = readDataFromDb(url_);
+ if (data)
+ i = std::find(rSet.begin(), rSet.end(), data->iniEntry);
+ }
+ if (i == rSet.end()) {
+ return false;
+ }
+ rSet.erase(i);
+ // write immediately:
+ m_configmgrini_modified = true;
+ configmgrini_flush( xCmdEnv );
+ return true;
+}
+#endif
+
+// Package
+
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+#if HAVE_FEATURE_EXTENSIONS
+ const OUString url(getURL());
+ if (!bReg && that->m_registeredPackages)
+ {
+ // fallback for user extension registered in berkeley DB
+ bReg = that->m_registeredPackages->has(
+ OUStringToOString( url, RTL_TEXTENCODING_UTF8 ));
+ }
+#endif
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+OUString encodeForXml( OUString const & text )
+{
+ // encode conforming xml:
+ sal_Int32 len = text.getLength();
+ OUStringBuffer buf;
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ sal_Unicode c = text[ pos ];
+ switch (c) {
+ case '<':
+ buf.append( "&lt;" );
+ break;
+ case '>':
+ buf.append( "&gt;" );
+ break;
+ case '&':
+ buf.append( "&amp;" );
+ break;
+ case '\'':
+ buf.append( "&apos;" );
+ break;
+ case '\"':
+ buf.append( "&quot;" );
+ break;
+ default:
+ buf.append( c );
+ break;
+ }
+ }
+ return buf.makeStringAndClear();
+}
+
+
+OUString replaceOrigin(
+ OUString const & url, std::u16string_view destFolder, Reference< XCommandEnvironment > const & xCmdEnv, Reference< XComponentContext > const & xContext, bool & out_replaced)
+{
+ // looking for %origin%:
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ std::vector<sal_Int8> filtered( bytes.size() * 2 );
+ bool use_filtered = false;
+ OString origin;
+ char const * pBytes = reinterpret_cast<char const *>(
+ bytes.data());
+ std::size_t nBytes = bytes.size();
+ size_t write_pos = 0;
+ while (nBytes > 0)
+ {
+ sal_Int32 index = rtl_str_indexOfChar_WithLength( pBytes, nBytes, '%' );
+ if (index < 0) {
+ if (! use_filtered) // opt
+ break;
+ index = nBytes;
+ }
+
+ if ((write_pos + index) > filtered.size())
+ filtered.resize( (filtered.size() + index) * 2 );
+ memcpy( filtered.data() + write_pos, pBytes, index );
+ write_pos += index;
+ pBytes += index;
+ nBytes -= index;
+ if (nBytes == 0)
+ break;
+
+ // consume %:
+ ++pBytes;
+ --nBytes;
+ char const * pAdd = "%";
+ sal_Int32 nAdd = 1;
+ if (nBytes > 1 && pBytes[ 0 ] == '%')
+ {
+ // %% => %
+ ++pBytes;
+ --nBytes;
+ use_filtered = true;
+ }
+ else if (rtl_str_shortenedCompare_WithLength(
+ pBytes, nBytes,
+ "origin%",
+ RTL_CONSTASCII_LENGTH("origin%"),
+ RTL_CONSTASCII_LENGTH("origin%")) == 0)
+ {
+ if (origin.isEmpty()) {
+ // encode only once
+ origin = OUStringToOString(
+ encodeForXml( url.copy( 0, url.lastIndexOf( '/' ) ) ),
+ // xxx todo: encode always for UTF-8? => lookup doc-header?
+ RTL_TEXTENCODING_UTF8 );
+ }
+ pAdd = origin.getStr();
+ nAdd = origin.getLength();
+ pBytes += RTL_CONSTASCII_LENGTH("origin%");
+ nBytes -= RTL_CONSTASCII_LENGTH("origin%");
+ use_filtered = true;
+ }
+ if ((write_pos + nAdd) > filtered.size())
+ filtered.resize( (filtered.size() + nAdd) * 2 );
+ memcpy( filtered.data() + write_pos, pAdd, nAdd );
+ write_pos += nAdd;
+ }
+ if (!use_filtered)
+ return url;
+ if (write_pos < filtered.size())
+ filtered.resize( write_pos );
+ OUString newUrl(url);
+ if (!destFolder.empty())
+ {
+ //get the file name of the xcu and add it to the url of the temporary folder
+ sal_Int32 i = url.lastIndexOf('/');
+ newUrl = OUString::Concat(destFolder) + url.subView(i);
+ }
+
+ ucbhelper::Content(newUrl, xCmdEnv, xContext).writeStream(
+ xmlscript::createInputStream(std::move(filtered)), true);
+ out_replaced = true;
+ return newUrl;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl * that = getMyBackend();
+ OUString url( getURL() );
+
+ if (doRegisterPackage)
+ {
+ if (getMyBackend()->activateEntry(getURL()))
+ {
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ OSL_ASSERT(data);
+ that->addToConfigmgrIni( m_isSchema, false, data->iniEntry, xCmdEnv );
+ }
+ else
+ {
+ ConfigurationBackendDb::Data data;
+ if (!m_isSchema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url = replaceOrigin(url, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ //No need for live-deployment for bundled extension, because OOo
+ //restarts after installation
+ if ((that->m_eContext != Context::Bundled && !startup)
+ || comphelper::LibreOfficeKit::isActive())
+ {
+ if (m_isSchema)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcsFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ else
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->insertExtensionXcuFile(
+ that->m_eContext == Context::Shared, expandUnoRcUrl(url));
+ }
+ }
+ that->addToConfigmgrIni( m_isSchema, true, url, xCmdEnv );
+ data.iniEntry = dp_misc::makeRcTerm(url);
+ that->addDataToDb(getURL(), data);
+ }
+ }
+ else // revoke
+ {
+#if HAVE_FEATURE_EXTENSIONS
+ if (!that->removeFromConfigmgrIni(m_isSchema, url, xCmdEnv) &&
+ that->m_registeredPackages) {
+ // Obsolete package database handling - should be removed for LibreOffice 4.0
+ t_string2string_map entries(
+ that->m_registeredPackages->getEntries());
+ for (auto const& entry : entries)
+ {
+ //If the xcu file was installed before the configmgr was changed
+ //to use the configmgr.ini, one needed to rebuild to whole directory
+ //structure containing the xcu, xcs files from all extensions. Now,
+ //we just add all other xcu/xcs files to the configmgr.ini instead of
+ //rebuilding the directory structure.
+ OUString url2(
+ OStringToOUString(entry.first, RTL_TEXTENCODING_UTF8));
+ if (url2 != url) {
+ bool schema = entry.second.equalsIgnoreAsciiCase(
+ "vnd.sun.star.configuration-schema");
+ OUString url_replaced(url2);
+ ConfigurationBackendDb::Data data;
+ if (!schema)
+ {
+ const OUString sModFolder = that->createFolder(xCmdEnv);
+ bool out_replaced = false;
+ url_replaced = replaceOrigin(
+ url2, sModFolder, xCmdEnv, that->getComponentContext(), out_replaced);
+ if (out_replaced)
+ data.dataUrl = sModFolder;
+ else
+ deleteTempFolder(sModFolder);
+ }
+ that->addToConfigmgrIni(schema, true, url_replaced, xCmdEnv);
+ data.iniEntry = dp_misc::makeRcTerm(url_replaced);
+ that->addDataToDb(url2, data);
+ }
+ that->m_registeredPackages->erase(entry.first);
+ }
+ try
+ {
+ ::ucbhelper::Content(
+ makeURL( that->getCachePath(), "registry" ),
+ xCmdEnv, that->getComponentContext() ).executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch(const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+ }
+#endif
+ ::std::optional<ConfigurationBackendDb::Data> data = that->readDataFromDb(url);
+ //If an xcu file was life deployed then always a data entry is written.
+ //If the xcu file was already in the configmr.ini then there is also
+ //a data entry
+ if (!m_isSchema && data)
+ {
+ css::configuration::Update::get(
+ that->m_xComponentContext)->removeExtensionXcuFile(expandUnoRcTerm(data->iniEntry));
+ }
+ that->revokeEntryFromDb(url);
+ }
+}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_configuration_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::configuration::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
new file mode 100644
index 000000000..afdc8112f
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.cxx
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/xpath/XXPathAPI.hpp>
+
+#include "dp_configurationbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/configuration-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"conf";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"configuration-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"configuration";
+
+namespace dp_registry::backend::configuration {
+
+ConfigurationBackendDb::ConfigurationBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ConfigurationBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ConfigurationBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ConfigurationBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ConfigurationBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void ConfigurationBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement(u"data-url", data.dataUrl, helpNode);
+ writeSimpleElement(u"ini-entry", data.iniEntry, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<ConfigurationBackendDb::Data>
+ConfigurationBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ConfigurationBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement(u"data-url", aNode);
+ retData.iniEntry = readSimpleElement(u"ini-entry", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> ConfigurationBackendDb::getAllDataUrls()
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression(
+ sPrefix + ":configuration/" + sPrefix + ":data-url/text()");
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sExpression);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in configuration backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::configuration
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
new file mode 100644
index 000000000..bd48aab7b
--- /dev/null
+++ b/desktop/source/deployment/registry/configuration/dp_configurationbackenddb.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <rtl/string.hxx>
+#include <vector>
+#include <optional>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::configuration
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ConfigurationBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the xcu or xcs files which contained
+ %origin%
+ */
+ OUString dataUrl;
+ /* the URL of the xcu or xcs file which is written in to the configmgr.ini
+ */
+ OUString iniEntry;
+ };
+
+public:
+ ConfigurationBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ ::std::optional<Data> getEntry(std::u16string_view url);
+ std::vector<OUString> getAllDataUrls();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_backend.cxx b/desktop/source/deployment/registry/dp_backend.cxx
new file mode 100644
index 000000000..2a681fe87
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_backend.cxx
@@ -0,0 +1,771 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/ustring.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/unwrapargs.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <com/sun/star/beans/StringPair.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <tools/diagnose_ex.h>
+#include <unotools/tempfile.hxx>
+#include <optional>
+#include <utility>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend {
+
+
+PackageRegistryBackend::~PackageRegistryBackend()
+{
+}
+
+
+void PackageRegistryBackend::disposing( lang::EventObject const & event )
+{
+ Reference<deployment::XPackage> xPackage(
+ event.Source, UNO_QUERY_THROW );
+ OUString url( xPackage->getURL() );
+ ::osl::MutexGuard guard( m_aMutex );
+ if ( m_bound.erase( url ) != 1 )
+ {
+ SAL_WARN("desktop.deployment", "erase(" << url << ") != 1");
+ }
+}
+
+
+PackageRegistryBackend::PackageRegistryBackend(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xContext )
+ : t_BackendBase( m_aMutex ),
+ m_xComponentContext( xContext ),
+ m_eContext( Context::Unknown )
+{
+ assert(xContext.is());
+ std::optional<OUString> cachePath;
+ std::optional<bool> readOnly;
+ comphelper::unwrapArgs( args, m_context, cachePath, readOnly );
+ if (cachePath)
+ m_cachePath = *cachePath;
+
+ if ( m_context == "user" )
+ m_eContext = Context::User;
+ else if ( m_context == "shared" )
+ m_eContext = Context::Shared;
+ else if ( m_context == "bundled" )
+ m_eContext = Context::Bundled;
+ else if ( m_context == "tmp" )
+ m_eContext = Context::Tmp;
+ else if (m_context.matchIgnoreAsciiCase("vnd.sun.star.tdoc:/"))
+ m_eContext = Context::Document;
+ else
+ m_eContext = Context::Unknown;
+}
+
+
+void PackageRegistryBackend::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "PackageRegistryBackend instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageRegistryBackend::disposing()
+{
+ try {
+ for (auto const& elem : m_bound)
+ elem.second->removeEventListener(this);
+ m_bound.clear();
+ m_xComponentContext.clear();
+ WeakComponentImplHelperBase::disposing();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ "caught unexpected exception while disposing!",
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+// XPackageRegistry
+
+Reference<deployment::XPackage> PackageRegistryBackend::bindPackage(
+ OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ ::osl::ResettableMutexGuard guard( m_aMutex );
+ check();
+
+ t_string2ref::const_iterator const iFind( m_bound.find( url ) );
+ if (iFind != m_bound.end())
+ {
+ Reference<deployment::XPackage> xPackage( iFind->second );
+ if (xPackage.is())
+ {
+ if (!mediaType.isEmpty() &&
+ mediaType != xPackage->getPackageType()->getMediaType())
+ throw lang::IllegalArgumentException
+ ("XPackageRegistry::bindPackage: media type does not match",
+ static_cast<OWeakObject*>(this), 1);
+ if (xPackage->isRemoved() != bRemoved)
+ throw deployment::InvalidRemovedParameterException(
+ "XPackageRegistry::bindPackage: bRemoved parameter does not match",
+ static_cast<OWeakObject*>(this), xPackage->isRemoved(), xPackage);
+ return xPackage;
+ }
+ }
+
+ guard.clear();
+
+ Reference<deployment::XPackage> xNewPackage;
+ try {
+ xNewPackage = bindPackage_( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "Error binding package: " + url,
+ static_cast<OWeakObject *>(this), exc );
+ }
+
+ guard.reset();
+
+ std::pair< t_string2ref::iterator, bool > insertion(
+ m_bound.emplace( url, xNewPackage ) );
+ if (insertion.second)
+ { // first insertion
+ SAL_WARN_IF(
+ Reference<XInterface>(insertion.first->second) != xNewPackage,
+ "desktop.deployment", "mismatch");
+ }
+ else
+ { // found existing entry
+ Reference<deployment::XPackage> xPackage( insertion.first->second );
+ if (xPackage.is())
+ return xPackage;
+ insertion.first->second = xNewPackage;
+ }
+
+ guard.clear();
+ xNewPackage->addEventListener( this ); // listen for disposing events
+ return xNewPackage;
+}
+
+OUString PackageRegistryBackend::createFolder(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ const OUString sDataFolder = makeURL(getCachePath(), OUString());
+ //make sure the folder exist
+ ucbhelper::Content dataContent;
+ ::dp_misc::create_folder(&dataContent, sDataFolder, xCmdEnv);
+
+ const OUString baseDir(sDataFolder);
+ ::utl::TempFile aTemp(&baseDir, true);
+ const OUString& url = aTemp.GetURL();
+ return sDataFolder + url.subView(url.lastIndexOf('/'));
+}
+
+//folderURL can have the extension .tmp or .tmp_
+//Before OOo 3.4 the created a tmp file with osl_createTempFile and
+//then created a Folder with a same name and a trailing '_'
+//If the folderURL has no '_' then there is no corresponding tmp file.
+void PackageRegistryBackend::deleteTempFolder(
+ OUString const & folderUrl)
+{
+ if (!folderUrl.isEmpty())
+ {
+ erase_path( folderUrl, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+
+ if (folderUrl.endsWith("_"))
+ {
+ const OUString tempFile = folderUrl.copy(0, folderUrl.getLength() - 1);
+ erase_path( tempFile, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ }
+ }
+}
+
+//usedFolders can contain folder names which have the extension .tmp or .tmp_
+//Before OOo 3.4 we created a tmp file with osl_createTempFile and
+//then created a Folder with a same name and a trailing '_'
+//If the folderURL has no '_' then there is no corresponding tmp file.
+void PackageRegistryBackend::deleteUnusedFolders(
+ std::vector< OUString> const & usedFolders)
+{
+ try
+ {
+ const OUString sDataFolder = makeURL(getCachePath(), OUString());
+ ::ucbhelper::Content tempFolder(
+ sDataFolder, Reference<ucb::XCommandEnvironment>(), m_xComponentContext);
+
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor( tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ // get all temp directories:
+ std::vector<OUString> tempEntries;
+
+ while (xResultSet->next())
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+
+ if (title.endsWith(".tmp"))
+ tempEntries.push_back(
+ makeURLAppendSysPathSegment(sDataFolder, title));
+ }
+
+ for (const OUString & tempEntrie : tempEntries)
+ {
+ if (std::find( usedFolders.begin(), usedFolders.end(), tempEntrie ) ==
+ usedFolders.end())
+ {
+ deleteTempFolder(tempEntrie);
+ }
+ }
+ }
+ catch (const ucb::InteractiveAugmentedIOException& e)
+ {
+ //In case the folder containing all the data folder does not
+ //exist yet, we ignore the exception
+ if (e.Code != ucb::IOErrorCode_NOT_EXISTING)
+ throw;
+ }
+
+}
+
+
+Package::~Package()
+{
+}
+
+
+Package::Package( ::rtl::Reference<PackageRegistryBackend> myBackend,
+ OUString url,
+ OUString aName,
+ OUString displayName,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved,
+ OUString identifier)
+ : t_PackageBase( m_aMutex ),
+ m_myBackend(std::move( myBackend )),
+ m_url(std::move( url )),
+ m_name(std::move( aName )),
+ m_displayName(std::move( displayName )),
+ m_xPackageType( xPackageType ),
+ m_bRemoved(bRemoved),
+ m_identifier(std::move(identifier))
+{
+ if (m_bRemoved)
+ {
+ //We use the last segment of the URL
+ SAL_WARN_IF(
+ !m_name.isEmpty(), "desktop.deployment", "non-empty m_name");
+ OUString name = m_url;
+ rtl::Bootstrap::expandMacros(name);
+ sal_Int32 index = name.lastIndexOf('/');
+ if (index != -1 && index < name.getLength())
+ m_name = name.copy(index + 1);
+ }
+}
+
+
+void Package::disposing()
+{
+ m_myBackend.clear();
+ WeakComponentImplHelperBase::disposing();
+}
+
+
+void Package::check() const
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "Package instance has already been disposed!",
+ static_cast<OWeakObject *>(const_cast<Package *>(this)));
+ }
+}
+
+// XComponent
+
+void Package::dispose()
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::dispose();
+}
+
+
+void Package::addEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::addEventListener( xListener );
+}
+
+
+void Package::removeEventListener(
+ Reference<lang::XEventListener> const & xListener )
+{
+ //Do not call check here. We must not throw an exception here if the object
+ //is being disposed or is already disposed. See com.sun.star.lang.XComponent
+ WeakComponentImplHelperBase::removeEventListener( xListener );
+}
+
+// XModifyBroadcaster
+
+void Package::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void Package::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void Package::checkAborted(
+ ::rtl::Reference<AbortChannel> const & abortChannel )
+{
+ if (abortChannel.is() && abortChannel->isAborted()) {
+ throw CommandAbortedException(
+ "abort!", static_cast<OWeakObject *>(this) );
+ }
+}
+
+// XPackage
+
+Reference<task::XAbortChannel> Package::createAbortChannel()
+{
+ check();
+ return new AbortChannel;
+}
+
+
+sal_Bool Package::isBundle()
+{
+ return false; // default
+}
+
+
+::sal_Int32 Package::checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >&,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >&,
+ sal_Bool)
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return 0;
+}
+
+
+sal_Bool Package::checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return true;
+}
+
+
+Sequence< Reference<deployment::XPackage> > Package::getBundle(
+ Reference<task::XAbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return Sequence< Reference<deployment::XPackage> >();
+}
+
+
+OUString Package::getName()
+{
+ return m_name;
+}
+
+beans::Optional<OUString> Package::getIdentifier()
+{
+ if (m_bRemoved)
+ return beans::Optional<OUString>(true, m_identifier);
+
+ return beans::Optional<OUString>();
+}
+
+
+OUString Package::getVersion()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+OUString Package::getURL()
+{
+ return m_url;
+}
+
+
+OUString Package::getDisplayName()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return m_displayName;
+}
+
+
+OUString Package::getDescription()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+OUString Package::getLicenseText()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return OUString();
+}
+
+
+Sequence<OUString> Package::getUpdateInformationURLs()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return Sequence<OUString>();
+}
+
+
+css::beans::StringPair Package::getPublisherInfo()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ css::beans::StringPair aEmptyPair;
+ return aEmptyPair;
+}
+
+
+uno::Reference< css::graphic::XGraphic > Package::getIcon( sal_Bool /*bHighContrast*/ )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ uno::Reference< css::graphic::XGraphic > aEmpty;
+ return aEmpty;
+}
+
+
+Reference<deployment::XPackageTypeInfo> Package::getPackageType()
+{
+ return m_xPackageType;
+}
+
+void Package::exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::ucbhelper::Content destFolder( destFolderURL, xCmdEnv, getMyBackend()->getComponentContext() );
+ ::ucbhelper::Content sourceContent( getURL(), xCmdEnv, getMyBackend()->getComponentContext() );
+ bool bOk=true;
+ try
+ {
+ destFolder.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ newTitle, nameClashAction);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (!bOk)
+ throw RuntimeException( "UCB transferContent() failed!", nullptr );
+}
+
+void Package::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * container = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (container == nullptr)
+ return;
+
+ const Sequence< Reference<XInterface> > elements(
+ container->getElements() );
+ lang::EventObject evt( static_cast<OWeakObject *>(this) );
+ for ( const Reference<XInterface>& x : elements )
+ {
+ Reference<util::XModifyListener> xListener( x, UNO_QUERY );
+ if (xListener.is())
+ xListener->modified( evt );
+ }
+}
+
+// XPackage
+
+beans::Optional< beans::Ambiguous<sal_Bool> > Package::isRegistered(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ try {
+ ::osl::ResettableMutexGuard guard( m_aMutex );
+ return isRegistered_( guard,
+ AbortChannel::get(xAbortChannel),
+ xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "unexpected " + exc.getValueTypeName() + ": " + e.Message,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+void Package::processPackage_impl(
+ bool doRegisterPackage,
+ bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ check();
+ bool action = false;
+
+ try {
+ try {
+ ::osl::ResettableMutexGuard guard( m_aMutex );
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ isRegistered_( guard, AbortChannel::get(xAbortChannel),
+ xCmdEnv ) );
+ action = (option.IsPresent &&
+ (option.Value.IsAmbiguous ||
+ (doRegisterPackage ? !option.Value.Value
+ : option.Value.Value)));
+ if (action) {
+
+ OUString displayName = isRemoved() ? getName() : getDisplayName();
+ ProgressLevel progress(
+ xCmdEnv,
+ (doRegisterPackage
+ ? PackageRegistryBackend::StrRegisteringPackage()
+ : PackageRegistryBackend::StrRevokingPackage())
+ + displayName );
+ processPackage_( guard,
+ doRegisterPackage,
+ startup,
+ AbortChannel::get(xAbortChannel),
+ xCmdEnv );
+ }
+ }
+ catch (lang::IllegalArgumentException &) {
+ Any e(cppu::getCaughtException());
+ throw deployment::DeploymentException(
+ ((doRegisterPackage
+ ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
+ : DpResId(RID_STR_ERROR_WHILE_REVOKING))
+ + getDisplayName()),
+ static_cast< OWeakObject * >(this), e);
+ }
+ catch (const RuntimeException &) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "unexpected");
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ throw;
+ }
+ catch (const CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ (doRegisterPackage
+ ? DpResId(RID_STR_ERROR_WHILE_REGISTERING)
+ : DpResId(RID_STR_ERROR_WHILE_REVOKING))
+ + getDisplayName() + ": " + exc.getValueType().getTypeName() + " \"" + e.Message
+ + "\"",
+ static_cast<OWeakObject *>(this), exc );
+ }
+ }
+ catch (...) {
+ if (action)
+ fireModified();
+ throw;
+ }
+ if (action)
+ fireModified();
+}
+
+
+void Package::registerPackage(
+ sal_Bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ processPackage_impl( true /* register */, startup, xAbortChannel, xCmdEnv );
+}
+
+
+void Package::revokePackage(
+ sal_Bool startup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ processPackage_impl( false /* revoke */, startup, xAbortChannel, xCmdEnv );
+
+}
+
+PackageRegistryBackend * Package::getMyBackend() const
+{
+ PackageRegistryBackend * pBackend = m_myBackend.get();
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<Package *>(this)));
+ }
+ return pBackend;
+}
+
+OUString Package::getRepositoryName()
+{
+ PackageRegistryBackend * backEnd = getMyBackend();
+ return backEnd->getContext();
+}
+
+beans::Optional< OUString > Package::getRegistrationDataURL()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return beans::Optional<OUString>();
+}
+
+sal_Bool Package::isRemoved()
+{
+ return m_bRemoved;
+}
+
+Package::TypeInfo::~TypeInfo()
+{
+}
+
+// XPackageTypeInfo
+
+OUString Package::TypeInfo::getMediaType()
+{
+ return m_mediaType;
+}
+
+
+OUString Package::TypeInfo::getDescription()
+{
+ return getShortDescription();
+}
+
+
+OUString Package::TypeInfo::getShortDescription()
+{
+ return m_shortDescr;
+}
+
+OUString Package::TypeInfo::getFileFilter()
+{
+ return m_fileFilter;
+}
+
+Any Package::TypeInfo::getIcon( sal_Bool /*highContrast*/, sal_Bool /*smallIcon*/ )
+{
+ return Any();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_backenddb.cxx b/desktop/source/deployment/registry/dp_backenddb.cxx
new file mode 100644
index 000000000..28effd95c
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_backenddb.cxx
@@ -0,0 +1,655 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <osl/file.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XActiveDataControl.hpp>
+#include <dp_misc.h>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <dp_backenddb.hxx>
+
+
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend {
+
+BackendDb::BackendDb(
+ Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url):
+ m_xContext(xContext)
+{
+ m_urlDb = dp_misc::expandUnoRcUrl(url);
+}
+
+void BackendDb::save()
+{
+ const Reference<css::io::XActiveDataSource> xDataSource(m_doc,css::uno::UNO_QUERY_THROW);
+ std::vector<sal_Int8> bytes;
+ xDataSource->setOutputStream(::xmlscript::createOutputStream(&bytes));
+ const Reference<css::io::XActiveDataControl> xDataControl(m_doc,css::uno::UNO_QUERY_THROW);
+ xDataControl->start();
+
+ const Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(std::move(bytes)));
+ ::ucbhelper::Content ucbDb(m_urlDb, nullptr, m_xContext);
+ ucbDb.writeStream(xData, true /*replace existing*/);
+}
+
+css::uno::Reference<css::xml::dom::XDocument> const & BackendDb::getDocument()
+{
+ if (!m_doc.is())
+ {
+ const Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(m_xContext) );
+
+ ::osl::DirectoryItem item;
+ ::osl::File::RC err = ::osl::DirectoryItem::get(m_urlDb, item);
+ if (err == ::osl::File::E_None)
+ {
+ ::ucbhelper::Content descContent(
+ m_urlDb, css::uno::Reference<css::ucb::XCommandEnvironment>(),
+ m_xContext);
+ Reference<css::io::XInputStream> xIn = descContent.openStream();
+ m_doc = xDocBuilder->parse(xIn);
+ }
+ else if (err == ::osl::File::E_NOENT)
+ {
+ //Create a new document and insert some basic stuff
+ m_doc = xDocBuilder->newDocument();
+ const Reference<css::xml::dom::XElement> rootNode =
+ m_doc->createElementNS(getDbNSName(), getNSPrefix() +
+ ":" + getRootElementName());
+
+ m_doc->appendChild(Reference<css::xml::dom::XNode>(
+ rootNode, UNO_QUERY_THROW));
+ save();
+ }
+ else
+ throw css::uno::RuntimeException(
+ "Extension manager could not access database file:"
+ + m_urlDb, nullptr);
+
+ if (!m_doc.is())
+ throw css::uno::RuntimeException(
+ "Extension manager could not get root node of data base file: "
+ + m_urlDb, nullptr);
+ }
+
+ return m_doc;
+}
+
+Reference<css::xml::xpath::XXPathAPI> const & BackendDb::getXPathAPI()
+{
+ if (!m_xpathApi.is())
+ {
+ m_xpathApi = css::xml::xpath::XPathAPI::create( m_xContext );
+
+ m_xpathApi->registerNS( getNSPrefix(), getDbNSName() );
+ }
+
+ return m_xpathApi;
+}
+
+void BackendDb::removeElement(OUString const & sXPathExpression)
+{
+ try
+ {
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ //find the extension element that is to be removed
+ const Reference<css::xml::dom::XNode> aNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+
+ if (aNode.is())
+ {
+ root->removeChild(aNode);
+ save();
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be any other entry with the same url
+ const Reference<css::xml::dom::XNode> nextNode =
+ xpathApi->selectSingleNode(root, sXPathExpression);
+ OSL_ASSERT(! nextNode.is());
+#endif
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+void BackendDb::removeEntry(std::u16string_view url)
+{
+ const OUString sKeyElement = getKeyElementName();
+ const OUString sPrefix = getNSPrefix();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ removeElement(sExpression);
+}
+
+void BackendDb::revokeEntry(std::u16string_view url)
+{
+ try
+ {
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ entry->setAttribute("revoked", "true");
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::activateEntry(std::u16string_view url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ //no attribute "active" means it is active, that is, registered.
+ entry->removeAttribute("revoked");
+ save();
+ ret = true;
+ }
+ return ret;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to revoke data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+bool BackendDb::hasActiveEntry(std::u16string_view url)
+{
+ try
+ {
+ bool ret = false;
+ Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
+ if (entry.is())
+ {
+ OUString sActive = entry->getAttribute("revoked");
+ if (!(sActive == "true"))
+ ret = true;
+ }
+ return ret;
+
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to determine an active entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+Reference<css::xml::dom::XNode> BackendDb::getKeyElement(
+ std::u16string_view url)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sExpression =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "[@url = \"" +
+ url +
+ "\"]";
+
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ return xpathApi->selectSingleNode(root, sExpression);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ std::u16string_view sVectorTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent)
+{
+ try{
+ if (vecPairs.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ OSL_ASSERT(!sNameSpace.isEmpty());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> vectorNode(
+ doc->createElementNS(sNameSpace, sPrefix + sVectorTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ vectorNode, css::uno::UNO_QUERY_THROW));
+ for (auto const& vecPair : vecPairs)
+ {
+ const Reference<css::xml::dom::XElement> pairNode(
+ doc->createElementNS(sNameSpace, sPrefix + sPairTagName));
+
+ vectorNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ pairNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> firstNode(
+ doc->createElementNS(sNameSpace, sPrefix + sFirstTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> firstTextNode(
+ doc->createTextNode( vecPair.first));
+
+ firstNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ firstTextNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XElement> secondNode(
+ doc->createElementNS(sNameSpace, sPrefix + sSecondTagName));
+
+ pairNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondNode, css::uno::UNO_QUERY_THROW));
+
+ const Reference<css::xml::dom::XText> secondTextNode(
+ doc->createTextNode( vecPair.second));
+
+ secondNode->appendChild(
+ Reference<css::xml::dom::XNode>(
+ secondTextNode, css::uno::UNO_QUERY_THROW));
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector< std::pair< OUString, OUString > >
+BackendDb::readVectorOfPair(
+ Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprPairs(
+ sPrefix + sListTagName + "/" + sPrefix + sPairTagName);
+ const Reference<css::xml::dom::XNodeList> listPairs =
+ xpathApi->selectNodeList(parent, sExprPairs);
+
+ std::vector< std::pair< OUString, OUString > > retVector;
+ sal_Int32 length = listPairs->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> aPair = listPairs->item(i);
+ const OUString sExprFirst(sPrefix + sFirstTagName + "/text()");
+ const Reference<css::xml::dom::XNode> first =
+ xpathApi->selectSingleNode(aPair, sExprFirst);
+
+ const OUString sExprSecond(sPrefix + sSecondTagName + "/text()");
+ const Reference<css::xml::dom::XNode> second =
+ xpathApi->selectSingleNode(aPair, sExprSecond);
+ OSL_ASSERT(first.is() && second.is());
+
+ retVector.emplace_back(
+ first->getNodeValue(), second->getNodeValue());
+ }
+ return retVector;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Only writes the data if there is at least one entry
+void BackendDb::writeSimpleList(
+ std::deque< OUString> const & list,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (list.empty())
+ return;
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+
+ const Reference<css::xml::dom::XElement> listNode(
+ doc->createElementNS(sNameSpace, sPrefix + sListTagName));
+
+ xParent->appendChild(
+ Reference<css::xml::dom::XNode>(
+ listNode, css::uno::UNO_QUERY_THROW));
+
+ for (auto const& elem : list)
+ {
+ const Reference<css::xml::dom::XNode> memberNode(
+ doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW);
+
+ listNode->appendChild(memberNode);
+
+ const Reference<css::xml::dom::XNode> textNode(
+ doc->createTextNode(elem), css::uno::UNO_QUERY_THROW);
+
+ memberNode->appendChild(textNode);
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+//Writes only the element if is has a value.
+//The prefix is automatically added to the element name
+void BackendDb::writeSimpleElement(
+ std::u16string_view sElementName, OUString const & value,
+ Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ if (value.isEmpty())
+ return;
+ const OUString sPrefix = getNSPrefix();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const OUString sNameSpace = getDbNSName();
+ const Reference<css::xml::dom::XNode> dataNode(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName),
+ UNO_QUERY_THROW);
+ xParent->appendChild(dataNode);
+
+ const Reference<css::xml::dom::XNode> dataValue(
+ doc->createTextNode(value), UNO_QUERY_THROW);
+ dataNode->appendChild(dataValue);
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+
+}
+
+/// The key elements have a url attribute and are always children of the root element.
+Reference<css::xml::dom::XNode> BackendDb::writeKeyElement(
+ OUString const & url)
+{
+ try
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sElementName = getKeyElementName();
+ const Reference<css::xml::dom::XDocument> doc = getDocument();
+ const Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ //Check if there are an entry with the same url. This can be the case if the
+ //status of an XPackage is ambiguous. In this case a call to activateExtension
+ //(dp_extensionmanager.cxx), will register the package again. See also
+ //Package::processPackage_impl in dp_backend.cxx.
+ //A package can become
+ //invalid after its successful registration, for example if a second extension with
+ //the same service is installed.
+ const OUString sExpression(
+ sPrefix + ":" + sElementName + "[@url = \"" + url + "\"]");
+ const Reference<css::xml::dom::XNode> existingNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ if (existingNode.is())
+ {
+ OSL_ASSERT(false);
+ //replace the existing entry.
+ removeEntry(url);
+ }
+
+ const Reference<css::xml::dom::XElement> keyElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName));
+
+ keyElement->setAttribute("url", url);
+
+ const Reference<css::xml::dom::XNode> keyNode(
+ keyElement, UNO_QUERY_THROW);
+ root->appendChild(keyNode);
+ return keyNode;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write key element in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+OUString BackendDb::readSimpleElement(
+ std::u16string_view sElementName, Reference<css::xml::dom::XNode> const & xParent)
+{
+ try
+ {
+ const OUString sPrefix = getNSPrefix();
+ const OUString sExpr(sPrefix + ":" + sElementName + "/text()");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const Reference<css::xml::dom::XNode> val =
+ xpathApi->selectSingleNode(xParent, sExpr);
+ if (val.is())
+ return val->getNodeValue();
+ return OUString();
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data (readSimpleElement) in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+std::deque< OUString> BackendDb::readList(
+ Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName)
+{
+ try
+ {
+ OSL_ASSERT(parent.is());
+ const OUString sPrefix(getNSPrefix() + ":");
+ const Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sExprList(
+ sPrefix + sListTagName + "/" + sPrefix + sMemberTagName + "/text()");
+ const Reference<css::xml::dom::XNodeList> list =
+ xpathApi->selectNodeList(parent, sExprList);
+
+ std::deque<OUString > retList;
+ sal_Int32 length = list->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ {
+ const Reference<css::xml::dom::XNode> member = list->item(i);
+ retList.push_back(member->getNodeValue());
+ }
+ return retList;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> BackendDb::getOneChildFromAllEntries(
+ std::u16string_view name)
+{
+ try
+ {
+ std::vector<OUString> listRet;
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+ Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sKeyElement = getKeyElementName();
+ OUString sNodeSelectExpr =
+ sPrefix +
+ ":" +
+ sKeyElement +
+ "/" +
+ sPrefix +
+ ":" +
+ name +
+ "/text()";
+
+ Reference<css::xml::dom::XNodeList> nodes =
+ xpathApi->selectNodeList(root, sNodeSelectExpr);
+ if (nodes.is())
+ {
+ sal_Int32 length = nodes->getLength();
+ for (sal_Int32 i = 0; i < length; i++)
+ listRet.push_back(nodes->item(i)->getNodeValue());
+ }
+ return listRet;
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+
+RegisteredDb::RegisteredDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+}
+
+void RegisteredDb::addEntry(OUString const & url)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ const OUString sNameSpace = getDbNSName();
+ const OUString sPrefix = getNSPrefix();
+ const OUString sEntry = getKeyElementName();
+
+ Reference<css::xml::dom::XDocument> doc = getDocument();
+ Reference<css::xml::dom::XNode> root = doc->getFirstChild();
+
+#if OSL_DEBUG_LEVEL > 0
+ //There must not be yet an entry with the same url
+ OUString sExpression(
+ sPrefix + ":" + sEntry + "[@url = \"" + url + "\"]");
+ Reference<css::xml::dom::XNode> _extensionNode =
+ getXPathAPI()->selectSingleNode(root, sExpression);
+ OSL_ASSERT(! _extensionNode.is());
+#endif
+ Reference<css::xml::dom::XElement> helpElement(
+ doc->createElementNS(sNameSpace, sPrefix + ":" + sEntry));
+
+ helpElement->setAttribute("url", url);
+
+ Reference<css::xml::dom::XNode> helpNode(
+ helpElement, UNO_QUERY_THROW);
+ root->appendChild(helpNode);
+
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/dp_registry.cxx b/desktop/source/deployment/registry/dp_registry.cxx
new file mode 100644
index 000000000..961afe7bb
--- /dev/null
+++ b/desktop/source/deployment/registry/dp_registry.cxx
@@ -0,0 +1,528 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <dp_shared.hxx>
+#include <dp_package.hxx>
+#include <strings.hrc>
+#include <dp_registry.hxx>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <osl/diagnose.h>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/uri.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <comphelper/sequence.hxx>
+#include <ucbhelper/content.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/DeploymentException.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/container/XContentEnumerationAccess.hpp>
+#include <com/sun/star/deployment/XPackageTypeInfo.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <set>
+#include <string_view>
+#include <unordered_map>
+#include <unordered_set>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+
+namespace dp_registry {
+
+namespace {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageRegistry, util::XUpdatable > t_helper;
+
+
+class PackageRegistryImpl : private cppu::BaseMutex, public t_helper
+{
+ struct ci_string_hash {
+ std::size_t operator () ( OUString const & str ) const {
+ return str.toAsciiLowerCase().hashCode();
+ }
+ };
+ struct ci_string_equals {
+ bool operator () ( std::u16string_view str1, std::u16string_view str2 ) const{
+ return o3tl::equalsIgnoreAsciiCase( str1, str2 );
+ }
+ };
+ typedef std::unordered_map<
+ OUString, Reference<deployment::XPackageRegistry>,
+ ci_string_hash, ci_string_equals > t_string2registry;
+ typedef std::unordered_map<
+ OUString, OUString,
+ ci_string_hash, ci_string_equals > t_string2string;
+ typedef std::set<
+ Reference<deployment::XPackageRegistry> > t_registryset;
+
+ t_string2registry m_mediaType2backend;
+ t_string2string m_filter2mediaType;
+ t_registryset m_ambiguousBackends;
+ t_registryset m_allBackends;
+ std::vector< Reference<deployment::XPackageTypeInfo> > m_typesInfos;
+
+ void insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend );
+
+protected:
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryImpl() override;
+ PackageRegistryImpl() : t_helper( m_aMutex ) {}
+
+
+public:
+ static Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Reference<deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+void PackageRegistryImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "PackageRegistry instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageRegistryImpl::disposing()
+{
+ // dispose all backends:
+ for (auto const& backend : m_allBackends)
+ {
+ try_dispose(backend);
+ }
+ m_mediaType2backend = t_string2registry();
+ m_ambiguousBackends = t_registryset();
+ m_allBackends = t_registryset();
+
+ t_helper::disposing();
+}
+
+
+PackageRegistryImpl::~PackageRegistryImpl()
+{
+}
+
+
+OUString normalizeMediaType( std::u16string_view mediaType )
+{
+ OUStringBuffer buf;
+ sal_Int32 index = 0;
+ for (;;) {
+ buf.append( o3tl::trim(o3tl::getToken(mediaType, 0, '/', index )) );
+ if (index < 0)
+ break;
+ buf.append( '/' );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+void PackageRegistryImpl::packageRemoved(
+ OUString const & url, OUString const & mediaType)
+{
+ const t_string2registry::const_iterator i =
+ m_mediaType2backend.find(mediaType);
+
+ if (i != m_mediaType2backend.end())
+ {
+ i->second->packageRemoved(url, mediaType);
+ }
+}
+
+void PackageRegistryImpl::insertBackend(
+ Reference<deployment::XPackageRegistry> const & xBackend )
+{
+ m_allBackends.insert( xBackend );
+ std::unordered_set<OUString> ambiguousFilters;
+
+ const Sequence< Reference<deployment::XPackageTypeInfo> > packageTypes(
+ xBackend->getSupportedPackageTypes() );
+ for ( Reference<deployment::XPackageTypeInfo> const & xPackageType : packageTypes )
+ {
+ m_typesInfos.push_back( xPackageType );
+
+ const OUString mediaType( normalizeMediaType(
+ xPackageType->getMediaType() ) );
+ std::pair<t_string2registry::iterator, bool> a_insertion(
+ m_mediaType2backend.emplace( mediaType, xBackend ) );
+ if (a_insertion.second)
+ {
+ // add parameterless media-type, too:
+ sal_Int32 semi = mediaType.indexOf( ';' );
+ if (semi >= 0) {
+ m_mediaType2backend.emplace( mediaType.copy( 0, semi ), xBackend );
+ }
+ const OUString fileFilter( xPackageType->getFileFilter() );
+ //The package backend shall also be called to determine the mediatype
+ //(XPackageRegistry.bindPackage) when the URL points to a directory.
+ const bool bExtension = (mediaType == "application/vnd.sun.star.package-bundle");
+ if (fileFilter.isEmpty() || fileFilter == "*.*" || fileFilter == "*" || bExtension)
+ {
+ m_ambiguousBackends.insert( xBackend );
+ }
+ else
+ {
+ sal_Int32 nIndex = 0;
+ do {
+ OUString token( fileFilter.getToken( 0, ';', nIndex ) );
+ if (token.match( "*." ))
+ token = token.copy( 1 );
+ if (token.isEmpty())
+ continue;
+ // mark any further wildcards ambig:
+ bool ambig = (token.indexOf('*') >= 0 ||
+ token.indexOf('?') >= 0);
+ if (! ambig) {
+ std::pair<t_string2string::iterator, bool> ins(
+ m_filter2mediaType.emplace(
+ token, mediaType ) );
+ ambig = !ins.second;
+ if (ambig) {
+ // filter has already been in: add previously
+ // added backend to ambig set
+ const t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find(
+ /* media-type of pr. added backend */
+ ins.first->second ) );
+ OSL_ASSERT(
+ iFind != m_mediaType2backend.end() );
+ if (iFind != m_mediaType2backend.end())
+ m_ambiguousBackends.insert( iFind->second );
+ }
+ }
+ if (ambig) {
+ m_ambiguousBackends.insert( xBackend );
+ // mark filter to be removed later from filters map:
+ ambiguousFilters.insert( token );
+ }
+ }
+ while (nIndex >= 0);
+ }
+ }
+#if OSL_DEBUG_LEVEL > 0
+ else
+ {
+ SAL_WARN( "desktop", "more than one PackageRegistryBackend for media-type=\""
+ << mediaType
+ << "\" => "
+ << Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )->getImplementationName()
+ << "\"!" );
+ }
+#endif
+ }
+
+ // cut out ambiguous filters:
+ for (auto const& ambiguousFilter : ambiguousFilters)
+ {
+ m_filter2mediaType.erase(ambiguousFilter);
+ }
+}
+
+
+Reference<deployment::XPackageRegistry> PackageRegistryImpl::create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ rtl::Reference<PackageRegistryImpl> that = new PackageRegistryImpl;
+
+ // auto-detect all registered package registries:
+ Reference<container::XEnumeration> xEnum(
+ Reference<container::XContentEnumerationAccess>(
+ xComponentContext->getServiceManager(),
+ UNO_QUERY_THROW )->createContentEnumeration(
+ "com.sun.star.deployment.PackageRegistryBackend" ) );
+ if (xEnum.is())
+ {
+ while (xEnum->hasMoreElements())
+ {
+ Any element( xEnum->nextElement() );
+ Sequence<Any> registryArgs(cachePath.isEmpty() ? 1 : 3 );
+ auto pregistryArgs = registryArgs.getArray();
+ pregistryArgs[ 0 ] <<= context;
+ if (!cachePath.isEmpty())
+ {
+ Reference<lang::XServiceInfo> xServiceInfo(
+ element, UNO_QUERY_THROW );
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ pregistryArgs[ 1 ] <<= registryCachePath;
+ pregistryArgs[ 2 ] <<= false; // readOnly;
+ create_folder( nullptr, registryCachePath,
+ Reference<XCommandEnvironment>() );
+ }
+
+ Reference<deployment::XPackageRegistry> xBackend;
+ Reference<lang::XSingleComponentFactory> xFac( element, UNO_QUERY );
+ if (xFac.is()) {
+ xBackend.set(
+ xFac->createInstanceWithArgumentsAndContext(
+ registryArgs, xComponentContext ), UNO_QUERY );
+ }
+ else {
+ Reference<lang::XSingleServiceFactory> xSingleServiceFac(
+ element, UNO_QUERY_THROW );
+ xBackend.set(
+ xSingleServiceFac->createInstanceWithArguments(
+ registryArgs ), UNO_QUERY );
+ }
+ if (! xBackend.is()) {
+ throw DeploymentException(
+ "cannot instantiate PackageRegistryBackend service: "
+ + Reference<lang::XServiceInfo>(
+ element, UNO_QUERY_THROW )->getImplementationName(),
+ static_cast<OWeakObject *>(that.get()) );
+ }
+
+ that->insertBackend( xBackend );
+ }
+ }
+
+ // Insert bundle back-end.
+ // Always register as last, because we want to add extensions also as folders
+ // and as a default we accept every folder, which was not recognized by the other
+ // backends.
+ Reference<deployment::XPackageRegistry> extensionBackend =
+ ::dp_registry::backend::bundle::create(
+ that, context, cachePath, xComponentContext);
+ that->insertBackend(extensionBackend);
+
+ Reference<lang::XServiceInfo> xServiceInfo(
+ extensionBackend, UNO_QUERY_THROW );
+
+ OSL_ASSERT(xServiceInfo.is());
+ OUString registryCachePath(
+ makeURL( cachePath,
+ ::rtl::Uri::encode(
+ xServiceInfo->getImplementationName(),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+ create_folder( nullptr, registryCachePath, Reference<XCommandEnvironment>());
+
+
+#if OSL_DEBUG_LEVEL > 0
+ // dump tables:
+ {
+ t_registryset allBackends;
+ dp_misc::TRACE("> [dp_registry.cxx] media-type detection:\n\n" );
+ for (auto const& elem : that->m_filter2mediaType)
+ {
+ const Reference<deployment::XPackageRegistry> xBackend(
+ that->m_mediaType2backend.find( elem.second )->second );
+ allBackends.insert( xBackend );
+ dp_misc::TRACE(
+ "extension \"" + elem.first + "\" maps to media-type \"" + elem.second
+ + "\" maps to backend "
+ + Reference<lang::XServiceInfo>(
+ xBackend, UNO_QUERY_THROW )
+ ->getImplementationName()
+ + "\n");
+ }
+ dp_misc::TRACE( "> [dp_registry.cxx] ambiguous backends:\n\n" );
+ for (auto const& ambiguousBackend : that->m_ambiguousBackends)
+ {
+ OUStringBuffer buf;
+ buf.append(
+ Reference<lang::XServiceInfo>(
+ ambiguousBackend, UNO_QUERY_THROW )->getImplementationName() );
+ buf.append( ": " );
+ const Sequence< Reference<deployment::XPackageTypeInfo> > types(
+ ambiguousBackend->getSupportedPackageTypes() );
+ for ( sal_Int32 pos = 0; pos < types.getLength(); ++pos ) {
+ Reference<deployment::XPackageTypeInfo> const & xInfo =
+ types[ pos ];
+ buf.append( xInfo->getMediaType() );
+ const OUString filter( xInfo->getFileFilter() );
+ if (!filter.isEmpty()) {
+ buf.append( " (" );
+ buf.append( filter );
+ buf.append( ")" );
+ }
+ if (pos < (types.getLength() - 1))
+ buf.append( ", " );
+ }
+ dp_misc::TRACE(buf + "\n\n");
+ }
+ allBackends.insert( that->m_ambiguousBackends.begin(),
+ that->m_ambiguousBackends.end() );
+ OSL_ASSERT( allBackends == that->m_allBackends );
+ }
+#endif
+
+ return that;
+}
+
+// XUpdatable: broadcast to backends
+
+void PackageRegistryImpl::update()
+{
+ check();
+ for (auto const& backend : m_allBackends)
+ {
+ const Reference<util::XUpdatable> xUpdatable(backend, UNO_QUERY);
+ if (xUpdatable.is())
+ xUpdatable->update();
+ }
+}
+
+// XPackageRegistry
+
+Reference<deployment::XPackage> PackageRegistryImpl::bindPackage(
+ OUString const & url, OUString const & mediaType_, sal_Bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ check();
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ {
+ ::ucbhelper::Content ucbContent;
+ bool bOk=true;
+
+ try
+ {
+ bOk = create_ucb_content(
+ &ucbContent, url, xCmdEnv, false /* no throw */ )
+ && !ucbContent.isFolder();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (bOk)
+ {
+ OUString title( StrTitle::getTitle( ucbContent ) );
+ for (;;)
+ {
+ const t_string2string::const_iterator iFind(
+ m_filter2mediaType.find(title) );
+ if (iFind != m_filter2mediaType.end()) {
+ mediaType = iFind->second;
+ break;
+ }
+ sal_Int32 point = title.indexOf( '.', 1 /* consume . */ );
+ if (point < 0)
+ break;
+ title = title.copy(point);
+ }
+ }
+ }
+ if (mediaType.isEmpty())
+ {
+ // try ambiguous backends:
+ for (auto const& ambiguousBackend : m_ambiguousBackends)
+ {
+ try {
+ return ambiguousBackend->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ }
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE) + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ else
+ {
+ // get backend by media-type:
+ t_string2registry::const_iterator iFind(
+ m_mediaType2backend.find( normalizeMediaType(mediaType) ) );
+ if (iFind == m_mediaType2backend.end()) {
+ // xxx todo: more sophisticated media-type argument parsing...
+ sal_Int32 q = mediaType.indexOf( ';' );
+ if (q >= 0) {
+ iFind = m_mediaType2backend.find(
+ normalizeMediaType(
+ // cut parameters:
+ mediaType.subView( 0, q ) ) );
+ }
+ }
+ if (iFind == m_mediaType2backend.end()) {
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE) + mediaType,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+ return iFind->second->bindPackage( url, mediaType, bRemoved,
+ identifier, xCmdEnv );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageRegistryImpl::getSupportedPackageTypes()
+{
+ return comphelper::containerToSequence(m_typesInfos);
+}
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ OUString const & context,
+ OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ return PackageRegistryImpl::create(
+ context, cachePath, xComponentContext );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executable.cxx b/desktop/source/deployment/registry/executable/dp_executable.cxx
new file mode 100644
index 000000000..40b253587
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executable.cxx
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <string_view>
+
+#include <dp_misc.h>
+#include <dp_backend.h>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include "dp_executablebackenddb.hxx"
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace dp_misc;
+
+namespace dp_registry::backend::executable {
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class ExecutablePackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ bool getFileAttributes(sal_uInt64& out_Attributes);
+ bool isUrlTargetInExtension() const;
+
+ public:
+ ExecutablePackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier)
+ {}
+ };
+ friend class ExecutablePackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ Reference<deployment::XPackageTypeInfo> m_xExecutableTypeInfo;
+ std::unique_ptr<ExecutableBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xExecutableTypeInfo(new Package::TypeInfo(
+ "application/vnd.sun.star.executable",
+ "", "Executable" ) )
+{
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ExecutableBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.executable.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+
+// XPackageRegistry
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence<Reference<deployment::XPackageTypeInfo> >(
+ & m_xExecutableTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (mediaType.isEmpty())
+ {
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.executable"))
+ {
+ return new BackendImpl::ExecutablePackageImpl(
+ this, url, name, m_xExecutableTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ return Reference<deployment::XPackage>();
+}
+
+
+// Package
+BackendImpl * BackendImpl::ExecutablePackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException( "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<ExecutablePackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::ExecutablePackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<dp_misc::AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ bool registered = getMyBackend()->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ registered, false /* IsAmbiguous */ ) );
+}
+
+void BackendImpl::ExecutablePackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /*startup*/,
+ ::rtl::Reference<dp_misc::AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ checkAborted(abortChannel);
+ if (doRegisterPackage)
+ {
+ if (!isUrlTargetInExtension())
+ {
+ OSL_ASSERT(false);
+ return;
+ }
+ sal_uInt64 attributes = 0;
+ //Setting the executable attribute does not affect executables on Windows
+ if (getFileAttributes(attributes))
+ {
+ if(getMyBackend()->m_context == "user")
+ attributes |= osl_File_Attribute_OwnExe;
+ else if (getMyBackend()->m_context == "shared")
+ attributes |= (osl_File_Attribute_OwnExe | osl_File_Attribute_GrpExe
+ | osl_File_Attribute_OthExe);
+ else if (getMyBackend()->m_context != "bundled")
+ //Bundled extensions are required to be in the properly
+ //installed. That is an executable must have the right flags
+ OSL_ASSERT(false);
+
+ //This won't have effect on Windows
+ osl::File::setAttributes(
+ dp_misc::expandUnoRcUrl(m_url), attributes);
+ }
+ getMyBackend()->addDataToDb(getURL());
+ }
+ else
+ {
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+//We currently cannot check if this XPackage represents a content of a particular extension
+//But we can check if we are within $UNO_USER_PACKAGES_CACHE etc.
+//Done for security reasons. For example an extension manifest could contain a path to
+//an executable outside the extension.
+bool BackendImpl::ExecutablePackageImpl::isUrlTargetInExtension() const
+{
+ bool bSuccess = false;
+ OUString sExtensionDir;
+ if(getMyBackend()->m_context == "user")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_USER_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "shared")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$UNO_SHARED_PACKAGES_CACHE");
+ else if (getMyBackend()->m_context == "bundled")
+ sExtensionDir = dp_misc::expandUnoRcTerm("$BUNDLED_EXTENSIONS");
+ else
+ OSL_ASSERT(false);
+ //remove file ellipses
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(OUString(), sExtensionDir, sExtensionDir))
+ {
+ OUString sFile;
+ if (osl::File::E_None == osl::File::getAbsoluteFileURL(
+ OUString(), dp_misc::expandUnoRcUrl(m_url), sFile))
+ {
+ if (sFile.match(sExtensionDir))
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+bool BackendImpl::ExecutablePackageImpl::getFileAttributes(sal_uInt64& out_Attributes)
+{
+ bool bSuccess = false;
+ const OUString url(dp_misc::expandUnoRcUrl(m_url));
+ osl::DirectoryItem item;
+ if (osl::FileBase::E_None == osl::DirectoryItem::get(url, item))
+ {
+ osl::FileStatus aStatus(osl_FileStatus_Mask_Attributes);
+ if( osl::FileBase::E_None == item.getFileStatus(aStatus))
+ {
+ out_Attributes = aStatus.getAttributes();
+ bSuccess = true;
+ }
+ }
+ return bSuccess;
+}
+
+
+} // anon namespace
+
+
+} // namespace dp_registry::backend::executable
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_executable_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::executable::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
new file mode 100644
index 000000000..29cbf85b3
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_executablebackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/executable-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"exe";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"executable-backend-db";
+constexpr OUStringLiteral ENTRY_NAME = u"executable";
+
+namespace dp_registry::backend::executable {
+
+ExecutableBackendDb::ExecutableBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ExecutableBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExecutableBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExecutableBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExecutableBackendDb::getKeyElementName()
+{
+ return ENTRY_NAME;
+}
+
+
+} // namespace dp_registry::backend::executable
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
new file mode 100644
index 000000000..0561a8c54
--- /dev/null
+++ b/desktop/source/deployment/registry/executable/dp_executablebackenddb.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::executable
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ The format looks like this:
+
+<?xml version="1.0"?>
+ */
+class ExecutableBackendDb : public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ ExecutableBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/README.md b/desktop/source/deployment/registry/help/README.md
new file mode 100644
index 000000000..24ea19518
--- /dev/null
+++ b/desktop/source/deployment/registry/help/README.md
@@ -0,0 +1 @@
+Support for help integrated in extensions. Also see /README.help.md.
diff --git a/desktop/source/deployment/registry/help/dp_help.cxx b/desktop/source/deployment/registry/help/dp_help.cxx
new file mode 100644
index 000000000..9e1a75fb9
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_help.cxx
@@ -0,0 +1,621 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <config_features.h>
+
+#include <strings.hrc>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include "dp_helpbackenddb.hxx"
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <osl/file.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <unotools/pathoptions.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/string_view.hxx>
+
+#if HAVE_FEATURE_XMLHELP
+#include <helpcompiler/compilehelp.hxx>
+#include <helpcompiler/HelpIndexer.hxx>
+#endif
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/ucb/SimpleFileAccess.hpp>
+#include <com/sun/star/util/XMacroExpander.hpp>
+#include <optional>
+#include <string_view>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::help {
+namespace {
+
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier);
+
+ bool extensionContainsCompiledHelp();
+
+ //XPackage
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void implProcessHelp( PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ void implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector );
+
+ ::std::optional<HelpBackendDb::Data> readDataFromDb(std::u16string_view url);
+ bool hasActiveEntry(std::u16string_view url);
+ bool activateEntry(std::u16string_view url);
+
+ Reference< ucb::XSimpleFileAccess3 > const & getFileAccess();
+ Reference< ucb::XSimpleFileAccess3 > m_xSFA;
+
+ const Reference<deployment::XPackageTypeInfo> m_xHelpTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<HelpBackendDb> m_backendDb;
+
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xHelpTypeInfo( new Package::TypeInfo("application/vnd.sun.star.help",
+ OUString(),
+ DpResId(RID_STR_HELP)
+ ) ),
+ m_typeInfos{ m_xHelpTypeInfo }
+{
+ if (transientMode())
+ return;
+
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new HelpBackendDb(getComponentContext(), dbFile));
+
+ //clean up data folders which are no longer used.
+ //This must not be done in the same process where the help files
+ //are still registers. Only after revoking and restarting OOo the folders
+ //can be removed. This works now, because the extension manager is a singleton
+ //and the backends are only create once per process.
+ std::vector<OUString> folders = m_backendDb->getAllDataUrls();
+ deleteUnusedFolders(folders);
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.help.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ // we don't support auto detection:
+ if (mediaType_.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType_, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+
+ if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.help"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xHelpTypeInfo, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType_,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+::std::optional<HelpBackendDb::Data> BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ::std::optional<HelpBackendDb::Data> data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+bool BackendImpl::activateEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->activateEntry(url);
+ return false;
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url, OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name, xPackageType, bRemoved,
+ identifier)
+{
+}
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+bool BackendImpl::PackageImpl::extensionContainsCompiledHelp()
+{
+ bool bCompiled = true;
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl(getURL());
+
+ ::osl::Directory helpFolder(aExpandedHelpURL);
+ if ( helpFolder.open() == ::osl::File::E_None)
+ {
+ //iterate over the contents of the help folder
+ //We assume that all folders within the help folder contain language specific
+ //help files. If just one of them does not contain compiled help then this
+ //function returns false.
+ ::osl::DirectoryItem item;
+ ::osl::File::RC errorNext = ::osl::File::E_None;
+ while ((errorNext = helpFolder.getNextItem(item)) == ::osl::File::E_None)
+ {
+ //No find the language folders
+ ::osl::FileStatus stat(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |osl_FileStatus_Mask_FileURL);
+ if (item.getFileStatus(stat) == ::osl::File::E_None)
+ {
+ if (stat.getFileType() != ::osl::FileStatus::Directory)
+ continue;
+
+ //look if there is the folder help.idxl in the language folder
+ OUString compUrl(stat.getFileURL() + "/help.idxl");
+ ::osl::Directory compiledFolder(compUrl);
+ if (compiledFolder.open() != ::osl::File::E_None)
+ {
+ bCompiled = false;
+ break;
+ }
+ }
+ else
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ break;
+ }
+ }
+ if (errorNext != ::osl::File::E_NOENT
+ && errorNext != ::osl::File::E_None)
+ {
+ //Error
+ OSL_ASSERT(false);
+ bCompiled = false;
+ }
+ }
+ return bCompiled;
+}
+
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ BackendImpl * that = getMyBackend();
+
+ bool bReg = false;
+ if (that->hasActiveEntry(getURL()))
+ bReg = true;
+
+ return beans::Optional< beans::Ambiguous<sal_Bool> >( true, beans::Ambiguous<sal_Bool>( bReg, false ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ BackendImpl* that = getMyBackend();
+ that->implProcessHelp( this, doRegisterPackage, xCmdEnv);
+}
+
+beans::Optional< OUString > BackendImpl::PackageImpl::getRegistrationDataURL()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::std::optional<HelpBackendDb::Data> data =
+ getMyBackend()->readDataFromDb(getURL());
+
+ if (data && getMyBackend()->hasActiveEntry(getURL()))
+ return beans::Optional<OUString>(true, data->dataUrl);
+
+ return beans::Optional<OUString>(true, OUString());
+}
+
+void BackendImpl::implProcessHelp(
+ PackageImpl * package, bool doRegisterPackage,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference< deployment::XPackage > xPackage(package);
+ OSL_ASSERT(xPackage.is());
+ if (doRegisterPackage)
+ {
+ //revive already processed help if possible
+ if ( !activateEntry(xPackage->getURL()))
+ {
+ HelpBackendDb::Data data;
+ data.dataUrl = xPackage->getURL();
+ if (!package->extensionContainsCompiledHelp())
+ {
+#if HAVE_FEATURE_XMLHELP
+ const OUString sHelpFolder = createFolder(xCmdEnv);
+ data.dataUrl = sHelpFolder;
+
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+ OUString aHelpURL = xPackage->getURL();
+ OUString aExpandedHelpURL = dp_misc::expandUnoRcUrl( aHelpURL );
+ if( !xSFA->isFolder( aExpandedHelpURL ) )
+ {
+ OUString aErrStr = DpResId( RID_STR_HELPPROCESSING_GENERAL_ERROR ) +
+ "No help folder";
+ OWeakObject* oWeakThis = this;
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ Any( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+
+ // Scan languages
+ Sequence< OUString > aLanguageFolderSeq = xSFA->getFolderContents( aExpandedHelpURL, true );
+ sal_Int32 nLangCount = aLanguageFolderSeq.getLength();
+ const OUString* pSeq = aLanguageFolderSeq.getConstArray();
+ for( sal_Int32 iLang = 0 ; iLang < nLangCount ; ++iLang )
+ {
+ OUString aLangURL = pSeq[iLang];
+ if( xSFA->isFolder( aLangURL ) )
+ {
+ std::vector< OUString > aXhpFileVector;
+
+ // calculate jar file URL
+ sal_Int32 indexStartSegment = aLangURL.lastIndexOf('/');
+ // for example "/en"
+ OUString langFolderURLSegment(
+ aLangURL.copy(
+ indexStartSegment + 1, aLangURL.getLength() - indexStartSegment - 1));
+
+ //create the folder in the "temporary folder"
+ ::ucbhelper::Content langFolderContent;
+ const OUString langFolderDest = makeURL(sHelpFolder, langFolderURLSegment);
+ const OUString langFolderDestExpanded = ::dp_misc::expandUnoRcUrl(langFolderDest);
+ ::dp_misc::create_folder(
+ &langFolderContent,
+ langFolderDest, xCmdEnv);
+
+ static const OUStringLiteral aHelpStr(u"help");
+
+ OUString aJarFile(
+ makeURL(sHelpFolder, langFolderURLSegment + "/" + aHelpStr + ".jar"));
+ aJarFile = ::dp_misc::expandUnoRcUrl(aJarFile);
+
+ OUString aEncodedJarFilePath = rtl::Uri::encode(
+ aJarFile, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ OUString aDestBasePath = "vnd.sun.star.zip://" +
+ aEncodedJarFilePath + "/" ;
+
+ sal_Int32 nLenLangFolderURL = aLangURL.getLength() + 1;
+
+ Sequence< OUString > aSubLangSeq = xSFA->getFolderContents( aLangURL, true );
+ sal_Int32 nSubLangCount = aSubLangSeq.getLength();
+ const OUString* pSubLangSeq = aSubLangSeq.getConstArray();
+ for( sal_Int32 iSubLang = 0 ; iSubLang < nSubLangCount ; ++iSubLang )
+ {
+ OUString aSubFolderURL = pSubLangSeq[iSubLang];
+ if( !xSFA->isFolder( aSubFolderURL ) )
+ continue;
+
+ implCollectXhpFiles( aSubFolderURL, aXhpFileVector );
+
+ // Copy to package (later: move?)
+ std::u16string_view aPureFolderName = aSubFolderURL.subView( nLenLangFolderURL );
+ OUString aDestPath = aDestBasePath + aPureFolderName;
+ xSFA->copy( aSubFolderURL, aDestPath );
+ }
+
+ // Call compiler
+ sal_Int32 nXhpFileCount = aXhpFileVector.size();
+ std::unique_ptr<OUString[]> pXhpFiles(new OUString[nXhpFileCount]);
+ for( sal_Int32 iXhp = 0 ; iXhp < nXhpFileCount ; ++iXhp )
+ {
+ OUString aXhpFile = aXhpFileVector[iXhp];
+ OUString aXhpRelFile = aXhpFile.copy( nLenLangFolderURL );
+ pXhpFiles[iXhp] = aXhpRelFile;
+ }
+
+ OUString aOfficeHelpPath( SvtPathOptions().GetHelpPath() );
+ OUString aOfficeHelpPathFileURL;
+ ::osl::File::getFileURLFromSystemPath( aOfficeHelpPath, aOfficeHelpPathFileURL );
+
+ HelpProcessingErrorInfo aErrorInfo;
+ bool bSuccess = compileExtensionHelp(
+ aOfficeHelpPathFileURL, aHelpStr, aLangURL,
+ nXhpFileCount, pXhpFiles.get(),
+ langFolderDestExpanded, aErrorInfo );
+
+ pXhpFiles.reset();
+
+ if( bSuccess )
+ {
+ OUString aLang;
+ sal_Int32 nLastSlash = aLangURL.lastIndexOf( '/' );
+ if( nLastSlash != -1 )
+ aLang = aLangURL.copy( nLastSlash + 1 );
+ else
+ aLang = "en";
+
+ HelpIndexer aIndexer(aLang, "help", langFolderDestExpanded, langFolderDestExpanded);
+ aIndexer.indexDocuments();
+ }
+
+ if( !bSuccess )
+ {
+ TranslateId pErrStrId;
+ switch( aErrorInfo.m_eErrorClass )
+ {
+ case HelpProcessingErrorClass::General: pErrStrId = RID_STR_HELPPROCESSING_GENERAL_ERROR; break;
+ case HelpProcessingErrorClass::XmlParsing: pErrStrId = RID_STR_HELPPROCESSING_XMLPARSING_ERROR; break;
+ default: ;
+ };
+
+ OUString aErrStr;
+ if (pErrStrId)
+ {
+ aErrStr = DpResId(pErrStrId);
+
+ // Remove CR/LF
+ OUString aErrMsg( aErrorInfo.m_aErrorMsg );
+ sal_Unicode const nCR = 13, nLF = 10;
+ sal_Int32 nSearchCR = aErrMsg.indexOf( nCR );
+ sal_Int32 nSearchLF = aErrMsg.indexOf( nLF );
+ if( nSearchCR != -1 || nSearchLF != -1 )
+ {
+ sal_Int32 nCopy;
+ if( nSearchCR == -1 )
+ nCopy = nSearchLF;
+ else if( nSearchLF == -1 )
+ nCopy = nSearchCR;
+ else
+ nCopy = ( nSearchCR < nSearchLF ) ? nSearchCR : nSearchLF;
+
+ aErrMsg = aErrMsg.copy( 0, nCopy );
+ }
+ aErrStr += aErrMsg;
+ if (pErrStrId != RID_STR_HELPPROCESSING_XMLPARSING_ERROR && !aErrorInfo.m_aXMLParsingFile.isEmpty() )
+ {
+ aErrStr += " in ";
+
+ OUString aDecodedFile = rtl::Uri::decode( aErrorInfo.m_aXMLParsingFile,
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ aErrStr += aDecodedFile;
+ if( aErrorInfo.m_nXMLParsingLine != -1 )
+ {
+ aErrStr += ", line " +
+ OUString::number( aErrorInfo.m_nXMLParsingLine );
+ }
+ }
+ }
+
+ OWeakObject* oWeakThis = this;
+ throw deployment::DeploymentException( OUString(), oWeakThis,
+ Any( uno::Exception( aErrStr, oWeakThis ) ) );
+ }
+ }
+ }
+#else
+ (void) xCmdEnv;
+#endif
+ }
+ // Writing the data entry replaces writing the flag file. If we got to this
+ // point the registration was successful.
+ if (m_backendDb)
+ m_backendDb->addEntry(xPackage->getURL(), data);
+ }
+ } //if (doRegisterPackage)
+ else
+ {
+ if (m_backendDb)
+ m_backendDb->revokeEntry(xPackage->getURL());
+ }
+}
+
+void BackendImpl::implCollectXhpFiles( const OUString& aDir,
+ std::vector< OUString >& o_rXhpFileVector )
+{
+ Reference< ucb::XSimpleFileAccess3 > xSFA = getFileAccess();
+
+ // Scan xhp files recursively
+ Sequence< OUString > aSeq = xSFA->getFolderContents( aDir, true );
+ sal_Int32 nCount = aSeq.getLength();
+ const OUString* pSeq = aSeq.getConstArray();
+ for( sal_Int32 i = 0 ; i < nCount ; ++i )
+ {
+ OUString aURL = pSeq[i];
+ if( xSFA->isFolder( aURL ) )
+ {
+ implCollectXhpFiles( aURL, o_rXhpFileVector );
+ }
+ else
+ {
+ sal_Int32 nLastDot = aURL.lastIndexOf( '.' );
+ if( nLastDot != -1 )
+ {
+ std::u16string_view aExt = aURL.subView( nLastDot + 1 );
+ if( o3tl::equalsIgnoreAsciiCase( aExt, u"xhp" ) )
+ o_rXhpFileVector.push_back( aURL );
+ }
+ }
+ }
+}
+
+Reference< ucb::XSimpleFileAccess3 > const & BackendImpl::getFileAccess()
+{
+ if( !m_xSFA.is() )
+ {
+ Reference<XComponentContext> const & xContext = getComponentContext();
+ if( xContext.is() )
+ {
+ m_xSFA = ucb::SimpleFileAccess::create(xContext);
+ }
+ if( !m_xSFA.is() )
+ {
+ throw RuntimeException(
+ "dp_registry::backend::help::BackendImpl::getFileAccess(), "
+ "could not instantiate SimpleFileAccess." );
+ }
+ }
+ return m_xSFA;
+}
+
+} // anon namespace
+
+} // namespace dp_registry
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_help_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::help::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
new file mode 100644
index 000000000..19bb3c3ce
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.cxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_helpbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/help-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"help";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"help-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"help";
+
+namespace dp_registry::backend::help {
+
+HelpBackendDb::HelpBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString HelpBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString HelpBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString HelpBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString HelpBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+void HelpBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> helpNode
+ = writeKeyElement(url);
+
+ writeSimpleElement(u"data-url", data.dataUrl, helpNode);
+ save();
+ }
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+
+::std::optional<HelpBackendDb::Data>
+HelpBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ HelpBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+ if (aNode.is())
+ {
+ retData.dataUrl = readSimpleElement(u"data-url", aNode);
+ }
+ else
+ {
+ return ::std::optional<Data>();
+ }
+ return ::std::optional<Data>(retData);
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ throw;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in help backend db: " + m_urlDb, nullptr, exc);
+ }
+}
+
+std::vector<OUString> HelpBackendDb::getAllDataUrls()
+{
+ return getOneChildFromAllEntries(u"data-url");
+}
+
+} // namespace dp_registry::backend::help
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
new file mode 100644
index 000000000..a46bd8663
--- /dev/null
+++ b/desktop/source/deployment/registry/help/dp_helpbackenddb.hxx
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <optional>
+#include <string_view>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::help
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class HelpBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* the URL to the folder containing the compiled help files, etc.
+ */
+ OUString dataUrl;
+ };
+
+public:
+ HelpBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ ::std::optional<Data> getEntry(std::u16string_view url);
+ //must also return the data urls for entries with @active="false". That is,
+ //those are currently revoked.
+ std::vector<OUString> getAllDataUrls();
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backend.h b/desktop/source/deployment/registry/inc/dp_backend.h
new file mode 100644
index 000000000..0fe39db45
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backend.h
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <dp_shared.hxx>
+#include <dp_interact.h>
+#include <rtl/ref.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <unordered_map>
+#include <strings.hrc>
+#include <utility>
+
+namespace dp_registry::backend
+{
+
+class PackageRegistryBackend;
+
+inline constexpr OUStringLiteral BACKEND_SERVICE_NAME = u"com.sun.star.deployment.PackageRegistryBackend";
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackage > t_PackageBase;
+
+
+class Package : protected cppu::BaseMutex, public t_PackageBase
+{
+ PackageRegistryBackend * getMyBackend() const;
+ void processPackage_impl(
+ bool registerPackage,
+ bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+protected:
+ ::rtl::Reference<PackageRegistryBackend> m_myBackend;
+ const OUString m_url;
+ OUString m_name;
+ OUString m_displayName;
+ const css::uno::Reference<css::deployment::XPackageTypeInfo> m_xPackageType;
+ const bool m_bRemoved;
+ //Only set if m_bRemoved = true;
+ const OUString m_identifier;
+
+ void check() const;
+ void fireModified();
+ virtual void SAL_CALL disposing() override;
+
+ void checkAborted(
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel );
+
+ // @@@ to be implemented by specific backend:
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference< ::dp_misc::AbortChannel > const & abortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ virtual ~Package() override;
+ Package( ::rtl::Reference<PackageRegistryBackend> myBackend,
+ OUString url,
+ OUString name,
+ OUString displayName,
+ css::uno::Reference<css::deployment::XPackageTypeInfo> const & xPackageType,
+ bool bRemoved,
+ OUString identifier);
+
+public:
+
+ class TypeInfo :
+ public ::cppu::WeakImplHelper<css::deployment::XPackageTypeInfo>
+ {
+ const OUString m_mediaType;
+ const OUString m_fileFilter;
+ const OUString m_shortDescr;
+ public:
+ virtual ~TypeInfo() override;
+ TypeInfo( OUString mediaType,
+ OUString fileFilter,
+ OUString shortDescr )
+ : m_mediaType(std::move(mediaType)), m_fileFilter(std::move(fileFilter)),
+ m_shortDescr(std::move(shortDescr))
+ {}
+ // XPackageTypeInfo
+ virtual OUString SAL_CALL getMediaType() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getShortDescription() override;
+ virtual OUString SAL_CALL getFileFilter() override;
+ virtual css::uno::Any SAL_CALL getIcon( sal_Bool highContrast,
+ sal_Bool smallIcon ) override;
+ };
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+ virtual void SAL_CALL removeEventListener(
+ css::uno::Reference<css::lang::XEventListener> const & xListener ) override;
+
+ // XModifyBroadcaster
+ virtual void SAL_CALL addModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+ virtual void SAL_CALL removeModifyListener(
+ css::uno::Reference<css::util::XModifyListener> const & xListener ) override;
+
+ // XPackage
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+ virtual css::beans::Optional< css::beans::Ambiguous<sal_Bool> >
+ SAL_CALL isRegistered(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >& xAbortChannel,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual ::sal_Bool SAL_CALL checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual void SAL_CALL registerPackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void SAL_CALL revokePackage(
+ sal_Bool startup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual sal_Bool SAL_CALL isBundle() override;
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getBundle(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getIdentifier() override;
+ virtual OUString SAL_CALL getVersion() override;
+ virtual OUString SAL_CALL getURL() override;
+ virtual OUString SAL_CALL getDisplayName() override;
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL
+ getUpdateInformationURLs() override;
+ virtual css::beans::StringPair SAL_CALL getPublisherInfo() override;
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ virtual css::uno::Reference<css::deployment::XPackageTypeInfo> SAL_CALL
+ getPackageType() override;
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL,
+ OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getRepositoryName() override;
+ virtual css::beans::Optional< OUString > SAL_CALL getRegistrationDataURL() override;
+ virtual sal_Bool SAL_CALL isRemoved() override;
+
+};
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::lang::XEventListener,
+ css::deployment::XPackageRegistry,
+ css::lang::XServiceInfo > t_BackendBase;
+
+
+class PackageRegistryBackend
+ : protected cppu::BaseMutex, public t_BackendBase
+{
+ //The map held originally WeakReferences. The map entries are removed in the disposing
+ //function, which is called when the XPackages are destructed or they are
+ //explicitly disposed. The latter happens, for example, when an extension is
+ //removed (see dp_manager.cxx). However, because of how the help systems work, now
+ // XPackageManager::getDeployedPackages is called often. This results in a lot
+ //of bindPackage calls which are costly. Therefore we keep hard references in
+ //the map now.
+ typedef std::unordered_map<
+ OUString, css::uno::Reference<css::deployment::XPackage> > t_string2ref;
+ t_string2ref m_bound;
+
+protected:
+ OUString m_cachePath;
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+
+ OUString m_context;
+ // currently only for library containers:
+ enum class Context {
+ Unknown, User, Shared, Bundled, Tmp, Document
+ } m_eContext;
+
+ static OUString StrCannotDetectMediaType() { return DpResId(RID_STR_CANNOT_DETECT_MEDIA_TYPE); }
+ static OUString StrUnsupportedMediaType() { return DpResId(RID_STR_UNSUPPORTED_MEDIA_TYPE); }
+
+ // @@@ to be implemented by specific backend:
+ virtual css::uno::Reference<css::deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+ = 0;
+
+ void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageRegistryBackend() override;
+ PackageRegistryBackend(
+ css::uno::Sequence<css::uno::Any> const & args,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+
+ /* creates a folder with a unique name.
+ If url is empty then it is created in the backend folder, otherwise
+ at a location relative to that folder specified by url.
+ */
+ OUString createFolder(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+ /* deletes folders and files.
+
+ All folder all files which end with ".tmp" or ".tmp_" and which are
+ not used are deleted.
+ */
+ void deleteUnusedFolders(
+ std::vector< OUString> const & usedFolders);
+ /* deletes one folder with a "temporary" name and the corresponding
+ tmp file, which was used to derive the folder name.
+ */
+ static void deleteTempFolder(
+ OUString const & folderUrl);
+
+public:
+ static OUString StrRegisteringPackage() { return DpResId(RID_STR_REGISTERING_PACKAGE); }
+ static OUString StrRevokingPackage() { return DpResId(RID_STR_REVOKING_PACKAGE); }
+
+ css::uno::Reference<css::uno::XComponentContext> const &
+ getComponentContext() const { return m_xComponentContext; }
+
+ OUString const & getCachePath() const { return m_cachePath; }
+ bool transientMode() const { return m_cachePath.isEmpty(); }
+
+ const OUString& getContext() const {return m_context; }
+
+ // XEventListener
+ virtual void SAL_CALL disposing( css::lang::EventObject const & evt ) override;
+
+ // XPackageRegistry
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL bindPackage(
+ OUString const & url, OUString const & mediaType,
+ sal_Bool bRemoved, OUString const & identifier,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+// virtual void SAL_CALL packageRemoved(
+// OUString const & url, OUString const & mediaType)
+// throw (css::deployment::DeploymentException,
+// css::uno::RuntimeException);
+
+};
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/inc/dp_backenddb.hxx b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
new file mode 100644
index 000000000..785201466
--- /dev/null
+++ b/desktop/source/deployment/registry/inc/dp_backenddb.hxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <rtl/ustring.hxx>
+#include <deque>
+#include <string_view>
+#include <vector>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace xml::dom {
+ class XDocument;
+ class XNode;
+ }
+ namespace xml::xpath {
+ class XXPathAPI;
+ }
+}
+
+namespace dp_registry::backend {
+
+class BackendDb
+{
+private:
+
+ css::uno::Reference<css::xml::dom::XDocument> m_doc;
+ css::uno::Reference<css::xml::xpath::XXPathAPI> m_xpathApi;
+
+ BackendDb(BackendDb const &) = delete;
+ BackendDb & operator = (BackendDb const &) = delete;
+
+protected:
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ OUString m_urlDb;
+
+protected:
+
+ /* caller must make sure that only one thread accesses the function
+ */
+ css::uno::Reference<css::xml::dom::XDocument> const & getDocument();
+
+ /* the namespace prefix is "reg" (without quotes)
+ */
+ css::uno::Reference<css::xml::xpath::XXPathAPI> const & getXPathAPI();
+ void save();
+ void removeElement(OUString const & sXPathExpression);
+
+ css::uno::Reference<css::xml::dom::XNode> getKeyElement(
+ std::u16string_view url);
+
+ void writeSimpleList(
+ std::deque< OUString> const & list,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeVectorOfPair(
+ std::vector< std::pair< OUString, OUString > > const & vecPairs,
+ std::u16string_view sVectorTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ void writeSimpleElement(
+ std::u16string_view sElementName, OUString const & value,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ css::uno::Reference<css::xml::dom::XNode> writeKeyElement(
+ OUString const & url);
+
+ OUString readSimpleElement(
+ std::u16string_view sElementName,
+ css::uno::Reference<css::xml::dom::XNode> const & xParent);
+
+ std::vector< std::pair< OUString, OUString > >
+ readVectorOfPair(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sPairTagName,
+ std::u16string_view sFirstTagName,
+ std::u16string_view sSecondTagName);
+
+ std::deque< OUString> readList(
+ css::uno::Reference<css::xml::dom::XNode> const & parent,
+ std::u16string_view sListTagName,
+ std::u16string_view sMemberTagName);
+
+ /* returns the values of one particularly child element of all key elements.
+ */
+ std::vector< OUString> getOneChildFromAllEntries(
+ std::u16string_view sElementName);
+
+
+ /* returns the namespace which is to be written as xmlns attribute
+ into the root element.
+ */
+ virtual OUString getDbNSName()=0;
+ /* return the namespace prefix which is to be registered with the XPath API.
+
+ The prefix can then be used in XPath expressions.
+ */
+ virtual OUString getNSPrefix()=0;
+ /* returns the name of the root element without any namespace prefix.
+ */
+ virtual OUString getRootElementName()=0;
+ /* returns the name of xml element for each entry
+ */
+ virtual OUString getKeyElementName()=0;
+
+public:
+ BackendDb(css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+ virtual ~BackendDb() {};
+
+ void removeEntry(std::u16string_view url);
+
+ /* This is called to write the "revoked" attribute to the entry.
+ This is done when XPackage::revokePackage is called.
+ */
+ void revokeEntry(std::u16string_view url);
+
+ /* returns false if the entry does not exist yet.
+ */
+ bool activateEntry(std::u16string_view url);
+
+ bool hasActiveEntry(std::u16string_view url);
+
+};
+
+class RegisteredDb: public BackendDb
+{
+
+public:
+ RegisteredDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+
+
+ void addEntry(OUString const & url);
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.cxx b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
new file mode 100644
index 000000000..8e656c5d7
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.cxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include "dp_extbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/extension-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"ext";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"extension-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"extension";
+
+namespace dp_registry::backend::bundle {
+
+ExtensionBackendDb::ExtensionBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):BackendDb(xContext, url)
+{
+
+}
+
+OUString ExtensionBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ExtensionBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ExtensionBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ExtensionBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+void ExtensionBackendDb::addEntry(OUString const & url, Data const & data)
+{
+ try{
+ //reactive revoked entry if possible.
+ if (!activateEntry(url))
+ {
+ Reference<css::xml::dom::XNode> extensionNodeNode = writeKeyElement(url);
+ writeVectorOfPair( data.items, u"extension-items", u"item",
+ u"url", u"media-type", extensionNodeNode);
+ save();
+ }
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to write data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+ExtensionBackendDb::Data ExtensionBackendDb::getEntry(std::u16string_view url)
+{
+ try
+ {
+ ExtensionBackendDb::Data retData;
+ Reference<css::xml::dom::XNode> aNode = getKeyElement(url);
+
+ if (aNode.is())
+ {
+ retData.items =
+ readVectorOfPair( aNode, u"extension-items", u"item",
+ u"url", u"media-type");
+ }
+ return retData;
+ }
+ catch(const css::uno::Exception &)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Extension Manager: failed to read data entry in backend db: " +
+ m_urlDb, nullptr, exc);
+ }
+}
+
+} // namespace dp_registry::backend::bundle
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_extbackenddb.hxx b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
new file mode 100644
index 000000000..fb736e6e2
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_extbackenddb.hxx
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+
+#include <dp_backenddb.hxx>
+
+namespace com::sun::star::uno
+{
+class XComponentContext;
+}
+
+namespace dp_registry::backend::bundle
+{
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ExtensionBackendDb : public dp_registry::backend::BackendDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+ virtual OUString getNSPrefix() override;
+ virtual OUString getRootElementName() override;
+ virtual OUString getKeyElementName() override;
+
+public:
+ struct Data
+ {
+ /* every element consists of a pair of the url to the item (jar,rdb, etc)
+ and the media type
+ */
+ std::vector<std::pair<OUString, OUString>> items;
+ };
+
+public:
+ ExtensionBackendDb(css::uno::Reference<css::uno::XComponentContext> const& xContext,
+ OUString const& url);
+
+ void addEntry(OUString const& url, Data const& data);
+
+ Data getEntry(std::u16string_view url);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/package/dp_package.cxx b/desktop/source/deployment/registry/package/dp_package.cxx
new file mode 100644
index 000000000..36b761b54
--- /dev/null
+++ b/desktop/source/deployment/registry/package/dp_package.cxx
@@ -0,0 +1,1594 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include <dp_package.hxx>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <dp_interact.h>
+#include <dp_dependencies.hxx>
+#include <dp_platform.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_resource.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/graphic/GraphicProvider.hpp>
+#include <com/sun/star/graphic/XGraphicProvider.hpp>
+#include <com/sun/star/io/Pipe.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/task/InteractionClassification.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XInteractionReplaceExistingData.hpp>
+#include <com/sun/star/ucb/NameClashResolveRequest.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.hpp>
+#include <com/sun/star/packages/manifest/ManifestReader.hpp>
+#include <com/sun/star/packages/manifest/ManifestWriter.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/ExtensionRemovedException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <optional>
+#include <tools/diagnose_ex.h>
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include "dp_extbackenddb.hxx"
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::bundle {
+namespace {
+
+typedef cppu::ImplInheritanceHelper<PackageRegistryBackend> ImplBaseT;
+
+
+class BackendImpl : public ImplBaseT
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+ /** contains the old tooltip description for the Extension Manager GUI in OOo v.2.x
+ We keep it for backward compatibility.
+ */
+ OUString m_oldDescription;
+ OUString m_url_expanded;
+ const bool m_legacyBundle;
+ Sequence< Reference<deployment::XPackage> > m_bundle;
+ Sequence< Reference<deployment::XPackage> > * m_pBundle;
+
+ ExtensionBackendDb::Data m_dbData;
+
+ Reference<deployment::XPackage> bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, //that is, using data base information
+ OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError = true );
+
+ typedef std::vector< Reference<deployment::XPackage> > t_packagevec;
+ void scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv );
+ void scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration = false );
+ std::vector<Reference<deployment::XPackage> > getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv);
+ bool checkPlatform(
+ Reference<ucb::XCommandEnvironment > const & environment);
+
+ bool checkDependencies(
+ Reference<ucb::XCommandEnvironment > const &
+ environment,
+ DescriptionInfoset const & description);
+ // throws css::uno::RuntimeException,
+ // css::deployment::DeploymentException
+
+ /// @throws deployment::DeploymentException
+ /// @throws ucb::CommandFailedException
+ /// @throws ucb::CommandAbortedException
+ /// @throws RuntimeException
+ bool checkLicense(
+ Reference< ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & description, bool bNoLicenseChecking);
+ // @throws DeploymentException
+ OUString getTextFromURL(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl);
+
+ DescriptionInfoset getDescriptionInfoset() const;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle,
+ bool bRemoved,
+ OUString const & identifier);
+
+ // XPackage
+ virtual sal_Bool SAL_CALL isBundle() override;
+
+ virtual Sequence< Reference<deployment::XPackage> > SAL_CALL getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ virtual OUString SAL_CALL getDescription() override;
+
+ virtual OUString SAL_CALL getLicenseText() override;
+
+ virtual void SAL_CALL exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Int32 SAL_CALL checkPrerequisites(
+ const Reference< task::XAbortChannel >& xAbortChannel,
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool noLicenseChecking) override;
+
+ virtual sal_Bool SAL_CALL checkDependencies(
+ const Reference< ucb::XCommandEnvironment >& xCmdEnv ) override;
+
+ virtual beans::Optional<OUString> SAL_CALL getIdentifier() override;
+
+ virtual OUString SAL_CALL getVersion() override;
+
+ virtual Sequence<OUString> SAL_CALL getUpdateInformationURLs() override;
+
+ virtual beans::StringPair SAL_CALL getPublisherInfo() override;
+
+ virtual OUString SAL_CALL getDisplayName() override;
+
+ virtual Reference< graphic::XGraphic > SAL_CALL
+ getIcon( sal_Bool bHighContrast ) override;
+ };
+ friend class PackageImpl;
+
+ Reference<deployment::XPackageRegistry> m_xRootRegistry;
+ const Reference<deployment::XPackageTypeInfo> m_xBundleTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xLegacyBundleTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+
+ std::unique_ptr<ExtensionBackendDb> m_backendDb;
+
+ void addDataToDb(OUString const & url, ExtensionBackendDb::Data const & data);
+ ExtensionBackendDb::Data readDataFromDb(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disposing() override;
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( OUString const& name ) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+ using ImplBaseT::disposing;
+};
+
+//Used to find a XPackage with a particular URL
+class XPackage_eq
+{
+ OUString m_URL;
+public:
+ explicit XPackage_eq(OUString s) : m_URL(std::move(s)) {}
+ bool operator() (const Reference<deployment::XPackage> & p) const
+ {
+ return m_URL == p->getURL();
+ }
+};
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext,
+ Reference<deployment::XPackageRegistry> const & xRootRegistry )
+ : ImplBaseT( args, xComponentContext ),
+ m_xRootRegistry( xRootRegistry ),
+ m_xBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.package-bundle",
+ "*.oxt;*.uno.pkg",
+ DpResId(RID_STR_PACKAGE_BUNDLE)
+ ) ),
+ m_xLegacyBundleTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.legacy-package-bundle",
+ "*.zip",
+ m_xBundleTypeInfo->getShortDescription()
+ ) ),
+ m_typeInfos{ m_xBundleTypeInfo, m_xLegacyBundleTypeInfo }
+{
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), getImplementationName());
+ dbFile = makeURL(dbFile, "backenddb.xml");
+ m_backendDb.reset(
+ new ExtensionBackendDb(getComponentContext(), dbFile));
+ }
+}
+
+
+void BackendImpl::disposing()
+{
+ m_xRootRegistry.clear();
+ PackageRegistryBackend::disposing();
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.bundle.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService(OUString const & ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence<OUString> BackendImpl::getSupportedServiceNames()
+{
+ return { OUString(BACKEND_SERVICE_NAME) };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ //Notify the backend responsible for processing the different media
+ //types that this extension was removed.
+ ExtensionBackendDb::Data data = readDataFromDb(url);
+ for (auto const& item : data.items)
+ {
+ m_xRootRegistry->packageRemoved(item.first, item.second);
+ }
+
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ))
+ {
+ if (ucbContent.isFolder())
+ {
+ //Every .oxt, uno.pkg file must contain a META-INF folder
+ ::ucbhelper::Content metaInfContent;
+ if (create_ucb_content(
+ &metaInfContent, makeURL( url, "META-INF" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.package-bundle";
+ }
+ //No support of legacy bundles, because every folder could be one.
+ }
+ else
+ {
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase(".oxt") ||
+ title.endsWithIgnoreAsciiCase(".uno.pkg"))
+ mediaType = "application/vnd.sun.star.package-bundle";
+ else if (title.endsWithIgnoreAsciiCase(".zip"))
+ mediaType = "application/vnd.sun.star.legacy-package-bundle";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+
+ //In case a XPackage is created for a removed extension, we cannot
+ //obtain the name
+ OUString name;
+ if (!bRemoved)
+ {
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getComponentContext() );
+ name = StrTitle::getTitle( ucbContent );
+ }
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xBundleTypeInfo, false, bRemoved,
+ identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase( "vnd.sun.star.legacy-package-bundle"))
+ {
+ return new PackageImpl(
+ this, url, name, m_xLegacyBundleTypeInfo, true, bRemoved,
+ identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+void BackendImpl::addDataToDb(
+ OUString const & url, ExtensionBackendDb::Data const & data)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url, data);
+}
+
+ExtensionBackendDb::Data BackendImpl::readDataFromDb(
+ std::u16string_view url)
+{
+ ExtensionBackendDb::Data data;
+ if (m_backendDb)
+ data = m_backendDb->getEntry(url);
+ return data;
+}
+
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<PackageRegistryBackend> const & myBackend,
+ OUString const & url,
+ OUString const & name,
+ Reference<deployment::XPackageTypeInfo> const & xPackageType,
+ bool legacyBundle, bool bRemoved, OUString const & identifier)
+ : Package( myBackend, url, name, name /* display-name */,
+ xPackageType, bRemoved, identifier),
+ m_url_expanded( expandUnoRcUrl( url ) ),
+ m_legacyBundle( legacyBundle ),
+ m_pBundle( nullptr )
+{
+ if (bRemoved)
+ m_dbData = getMyBackend()->readDataFromDb(url);
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+void BackendImpl::PackageImpl::disposing()
+{
+ sal_Int32 len = m_bundle.getLength();
+ Reference<deployment::XPackage> const * p = m_bundle.getConstArray();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ try_dispose( p[ pos ] );
+ m_bundle.realloc( 0 );
+
+ Package::disposing();
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ //In case the object was created for a removed extension (m_bRemoved = true)
+ //but the extension is not registered, then bundle will be empty. Then
+ //the return value will be Optional<...>.IsPresent= false. Although this is
+ //not true, this does not matter. Then registerPackage or revokePackage
+ //would never be called for the items. But since the extension is removed
+ //and not registered anyway, this does not matter.
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel, xCmdEnv ) );
+
+ bool reg = false;
+ bool present = false;
+ bool ambig = false;
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ beans::Optional< beans::Ambiguous<sal_Bool> > option(
+ xPackage->isRegistered( xSubAbortChannel, xCmdEnv ) );
+
+ //present = true if at least one bundle item has this value.
+ //reg = true if all bundle items have an option value (option.IsPresent == 1)
+ //and all have value of true (option.Value.Value == true)
+ //If not, then the bundle has the status of not registered and ambiguous.
+ if (option.IsPresent)
+ {
+ beans::Ambiguous<sal_Bool> const & status = option.Value;
+ if (present)
+ {
+ //we never come here in the first iteration
+ if (reg != bool(status.Value)) {
+
+ ambig = true;
+ reg = false;
+ break;
+ }
+ }
+ else
+ {
+ //we always come here in the first iteration
+ reg = status.Value;
+ present = true;
+ }
+ }
+ }
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ present, beans::Ambiguous<sal_Bool>(reg, ambig) );
+}
+
+OUString BackendImpl::PackageImpl::getTextFromURL(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ const OUString& licenseUrl)
+{
+ try
+ {
+ ::ucbhelper::Content descContent(
+ licenseUrl, xCmdEnv, getMyBackend()->getComponentContext());
+ std::vector<sal_Int8> seq = dp_misc::readFile(descContent);
+ return OUString( reinterpret_cast<char const *>(
+ seq.data()), seq.size(), RTL_TEXTENCODING_UTF8);
+ }
+ catch (const css::uno::Exception&)
+ {
+ Any exc( ::cppu::getCaughtException() );
+ throw css::deployment::DeploymentException(
+ "Could not read file " + licenseUrl, nullptr, exc);
+ }
+
+}
+
+DescriptionInfoset BackendImpl::PackageImpl::getDescriptionInfoset() const
+{
+ return dp_misc::getDescriptionInfoset(m_url_expanded);
+}
+
+bool BackendImpl::PackageImpl::checkPlatform(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment)
+{
+ bool ret = false;
+ DescriptionInfoset info(getDescriptionInfoset());
+ Sequence<OUString> platforms(info.getSupportedPlatforms());
+ if (hasValidPlatform(platforms))
+ {
+ ret = true;
+ }
+ else
+ {
+ ret = false;
+ OUString msg(
+ "unsupported platform");
+ Any e(
+ css::deployment::PlatformException(
+ msg, static_cast<OWeakObject *>(this), this));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ }
+ return ret;
+}
+
+
+bool BackendImpl::PackageImpl::checkDependencies(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & environment,
+ DescriptionInfoset const & description)
+{
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(dp_misc::Dependencies::check(description));
+
+ if (!unsatisfied.hasElements()) {
+ return true;
+ } else {
+ OUString msg(
+ "unsatisfied dependencies");
+ Any e(
+ css::deployment::DependencyException(
+ msg, static_cast<OWeakObject *>(this), unsatisfied));
+ if (!interactContinuation(
+ e, cppu::UnoType< css::task::XInteractionApprove >::get(),
+ environment, nullptr, nullptr))
+ {
+ throw css::deployment::DeploymentException(
+ msg, static_cast<OWeakObject *>(this), e);
+ }
+ return false;
+ }
+}
+
+bool BackendImpl::PackageImpl::checkLicense(
+ css::uno::Reference< css::ucb::XCommandEnvironment > const & xCmdEnv,
+ DescriptionInfoset const & info, bool alreadyInstalled)
+{
+ try
+ {
+ ::std::optional<SimpleLicenseAttributes> simplLicAttr
+ = info.getSimpleLicenseAttributes();
+ if (! simplLicAttr)
+ return true;
+ OUString sLic = info.getLocalizedLicenseURL();
+ //If we do not get a localized licence then there is an error in the description.xml
+ //This should be handled by using a validating parser. Therefore we assume that no
+ //license is available.
+ if (sLic.isEmpty())
+ throw css::deployment::DeploymentException(
+ "Could not obtain path to license. Possible error in description.xml", nullptr, Any());
+ OUString sHref = m_url_expanded + "/" + sLic;
+ OUString sLicense = getTextFromURL(xCmdEnv, sHref);
+ ////determine who has to agree to the license
+ //check correct value for attribute
+ if ( simplLicAttr->acceptBy != "user" && simplLicAttr->acceptBy != "admin")
+ throw css::deployment::DeploymentException(
+ "Could not obtain attribute simple-license@accept-by or it has no valid value", nullptr, Any());
+
+
+ //Only use interaction if there is no version of this extension already installed
+ //and the suppress-on-update flag is not set for the new extension
+ // alreadyInstalled | bSuppressOnUpdate | show license
+
+ // 0 | 0 | 1
+ // 0 | 1 | 1
+ // 1 | 0 | 1
+ // 1 | 1 | 0
+
+ if ( !(alreadyInstalled && simplLicAttr->suppressOnUpdate))
+ {
+ css::deployment::LicenseException licExc(
+ OUString(), nullptr, getDisplayName(), sLicense,
+ simplLicAttr->acceptBy);
+ bool approve = false;
+ bool abort = false;
+ if (! interactContinuation(
+ Any(licExc), cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, &approve, &abort ))
+ throw css::deployment::DeploymentException(
+ "Could not interact with user.", nullptr, Any());
+
+ return approve;
+ }
+ return true;
+ } catch (const css::ucb::CommandFailedException&) {
+ throw;
+ } catch (const css::ucb::CommandAbortedException&) {
+ throw;
+ } catch (const css::deployment::DeploymentException&) {
+ throw;
+ } catch (const css::uno::RuntimeException&) {
+ throw;
+ } catch (const css::uno::Exception&) {
+ Any anyExc = cppu::getCaughtException();
+ throw css::deployment::DeploymentException("Unexpected exception", nullptr, anyExc);
+ }
+}
+
+::sal_Int32 BackendImpl::PackageImpl::checkPrerequisites(
+ const css::uno::Reference< css::task::XAbortChannel >&,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv,
+ sal_Bool alreadyInstalled)
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return 0;
+
+ //always return LICENSE as long as the user did not accept the license
+ //so that XExtensionManager::checkPrerequisitesAndEnable will again
+ //check the license
+ if (!checkPlatform(xCmdEnv))
+ return deployment::Prerequisites::PLATFORM |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkDependencies(xCmdEnv, info))
+ return deployment::Prerequisites::DEPENDENCIES |
+ deployment::Prerequisites::LICENSE;
+ else if(!checkLicense(xCmdEnv, info, alreadyInstalled))
+ return deployment::Prerequisites::LICENSE;
+ else
+ return 0;
+}
+
+sal_Bool BackendImpl::PackageImpl::checkDependencies(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ DescriptionInfoset info = getDescriptionInfoset();
+ if (!info.hasDescription())
+ return true;
+
+ return checkDependencies(xCmdEnv, info);
+}
+
+beans::Optional<OUString> BackendImpl::PackageImpl::getIdentifier()
+{
+ OUString identifier;
+ if (m_bRemoved)
+ identifier = m_identifier;
+ else
+ identifier = dp_misc::generateIdentifier(
+ getDescriptionInfoset().getIdentifier(), m_name);
+
+ return beans::Optional<OUString>(
+ true, identifier);
+}
+
+OUString BackendImpl::PackageImpl::getVersion()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getVersion();
+}
+
+Sequence<OUString> BackendImpl::PackageImpl::getUpdateInformationURLs()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ return getDescriptionInfoset().getUpdateInformationUrls();
+}
+
+beans::StringPair BackendImpl::PackageImpl::getPublisherInfo()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+ std::pair< OUString, OUString > aInfo = getDescriptionInfoset().getLocalizedPublisherNameAndURL();
+ beans::StringPair aStrPair( aInfo.first, aInfo.second );
+ return aStrPair;
+}
+
+
+uno::Reference< graphic::XGraphic > BackendImpl::PackageImpl::getIcon( sal_Bool bHighContrast )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ uno::Reference< graphic::XGraphic > xGraphic;
+
+ OUString aIconURL = getDescriptionInfoset().getIconURL( bHighContrast );
+ if ( !aIconURL.isEmpty() )
+ {
+ OUString aFullIconURL = m_url_expanded + "/" + aIconURL;
+
+ uno::Reference< XComponentContext > xContext( getMyBackend()->getComponentContext() );
+ uno::Reference< graphic::XGraphicProvider > xGraphProvider( graphic::GraphicProvider::create(xContext) );
+
+ uno::Sequence< beans::PropertyValue > aMediaProps{ comphelper::makePropertyValue(
+ "URL", aFullIconURL) };
+ xGraphic = xGraphProvider->queryGraphic( aMediaProps );
+ }
+
+ return xGraphic;
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ const Sequence< Reference<deployment::XPackage> > bundle(
+ getBundle( abortChannel, xCmdEnv ) );
+
+ if (doRegisterPackage)
+ {
+ ExtensionBackendDb::Data data;
+ const sal_Int32 len = bundle.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ xPackage->registerPackage( startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ //We even try a rollback if the user cancelled the action (CommandAbortedException)
+ //in order to prevent invalid database entries.
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item registration error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow;
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ if (approve && !abort) // ignore error, just continue
+ continue;
+
+ {
+ ProgressLevel progress( xCmdEnv, "rollback..." );
+ // try rollback
+ for ( ; pos--; )
+ {
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const Exception &)
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ // ignore any errors of rollback
+ }
+ }
+ progress.update( "rollback finished." );
+ }
+
+ deployment::DeploymentException dpExc;
+ if (exc >>= dpExc) {
+ throw ucb::CommandFailedException(
+ dpExc.Message, dpExc.Context, dpExc.Cause );
+ }
+ else {
+ // rethrow CommandFailedException
+ ::cppu::throwException(exc);
+ }
+ }
+ data.items.emplace_back(xPackage->getURL(),
+ xPackage->getPackageType()->getMediaType());
+ }
+ getMyBackend()->addDataToDb(getURL(), data);
+ }
+ else
+ {
+ // revoke in reverse order:
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ checkAborted(abortChannel);
+ Reference<deployment::XPackage> const & xPackage = bundle[ pos ];
+ Reference<task::XAbortChannel> xSubAbortChannel(
+ xPackage->createAbortChannel() );
+ AbortChannel::Chain chain( abortChannel, xSubAbortChannel );
+ try {
+ bundle[ pos ]->revokePackage(
+ startup, xSubAbortChannel, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ // CommandFailedException, DeploymentException:
+ Any exc( ::cppu::getCaughtException() );
+ // try to handle exception, notify:
+ bool approve = false, abort = false;
+ if (! interactContinuation(
+ Any( lang::WrappedTargetException(
+ "bundle item revocation error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv,
+ &approve, &abort )) {
+ OSL_ASSERT( !approve && !abort );
+ if (m_legacyBundle) // default for legacy packages: ignore
+ continue;
+ // no selection at all, so rethrow
+ // no C++ rethrow after getCaughtException(),
+ // see cppuhelper/exc_hlp.hxx:
+ ::cppu::throwException(exc);
+ }
+ // ignore errors when revoking, although abort may have been
+ // selected
+ }
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ }
+}
+
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ const OUString sRelativeURL(getDescriptionInfoset().getLocalizedDescriptionURL());
+ OUString sDescription;
+ if (!sRelativeURL.isEmpty())
+ {
+ OUString sURL = m_url_expanded + "/" + sRelativeURL;
+
+ try
+ {
+ sDescription = getTextFromURL( css::uno::Reference< css::ucb::XCommandEnvironment >(), sURL );
+ }
+ catch ( const css::deployment::DeploymentException& )
+ {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+
+ if (!sDescription.isEmpty())
+ return sDescription;
+ return m_oldDescription;
+}
+
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sLicense;
+ DescriptionInfoset aInfo = getDescriptionInfoset();
+
+ ::std::optional< SimpleLicenseAttributes > aSimplLicAttr = aInfo.getSimpleLicenseAttributes();
+ if ( aSimplLicAttr )
+ {
+ OUString aLicenseURL = aInfo.getLocalizedLicenseURL();
+
+ if ( !aLicenseURL.isEmpty() )
+ {
+ OUString aFullURL = m_url_expanded + "/" + aLicenseURL;
+ sLicense = getTextFromURL( Reference< ucb::XCommandEnvironment >(), aFullURL);
+ }
+ }
+
+ return sLicense;
+}
+
+
+void BackendImpl::PackageImpl::exportTo(
+ OUString const & destFolderURL, OUString const & newTitle,
+ sal_Int32 nameClashAction, Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ ::ucbhelper::Content sourceContent(
+ m_url_expanded, xCmdEnv, getMyBackend()->getComponentContext() );
+ OUString title(newTitle);
+ if (title.isEmpty())
+ sourceContent.getPropertyValue( "Title" ) >>= title;
+ OUString destURL( makeURL( destFolderURL, ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) ) );
+
+ if (nameClashAction == ucb::NameClash::ASK)
+ {
+ if (create_ucb_content(
+ nullptr, destURL, xCmdEnv, false /* no throw */ )) {
+ bool replace = false, abort = false;
+ if (! interactContinuation(
+ Any( ucb::NameClashResolveRequest(
+ "file already exists: " + title,
+ static_cast<OWeakObject *>(this),
+ task::InteractionClassification_QUERY,
+ destFolderURL, title, OUString() ) ),
+ cppu::UnoType<ucb::XInteractionReplaceExistingData>::get(), xCmdEnv,
+ &replace, &abort ) || !replace) {
+ return;
+ }
+ }
+ }
+ else if (nameClashAction != ucb::NameClash::OVERWRITE) {
+ throw ucb::CommandFailedException("unsupported nameClashAction!",
+ static_cast<OWeakObject *>(this), Any() );
+ }
+ erase_path( destURL, xCmdEnv );
+
+ OUString destFolder =
+ "vnd.sun.star.zip://" +
+ ::rtl::Uri::encode( destURL,
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) +
+ "/";
+
+ ::ucbhelper::Content destFolderContent(
+ destFolder, xCmdEnv, getMyBackend()->getComponentContext() );
+ {
+ // transfer every item of folder into zip:
+ Reference<sdbc::XResultSet> xResultSet(
+ sourceContent.createCursor( Sequence<OUString>() ) );
+ ProgressLevel progress( xCmdEnv, OUString() );
+ while (xResultSet->next())
+ {
+ ::ucbhelper::Content subContent(
+ Reference<ucb::XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ destFolderContent.transferContent(
+ subContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ progress.update( Any() ); // animating progress bar
+ }
+ }
+
+ // assure META-INF folder:
+ ::ucbhelper::Content metainfFolderContent;
+ create_folder( &metainfFolderContent,
+ makeURL( destFolderContent.getURL(), "META-INF" ),
+ xCmdEnv );
+
+ if (m_legacyBundle)
+ {
+ // easy to migrate legacy bundles to new format:
+ // just export them once using a .oxt name!
+ // set detected media-types of any bundle item:
+
+ // collect all manifest entries:
+ Sequence< Reference<deployment::XPackage> > bundle;
+ try {
+ bundle = getBundle( Reference<task::XAbortChannel>(), xCmdEnv );
+ }
+ // xxx todo: think about exception specs:
+ catch (const deployment::DeploymentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+
+ std::vector< Sequence<beans::PropertyValue> > manifest;
+ manifest.reserve( bundle.getLength() );
+ sal_Int32 baseURLlen = m_url_expanded.getLength();
+ Reference<deployment::XPackage> const *pbundle = bundle.getConstArray();
+ static const OUStringLiteral strMediaType( u"MediaType" );
+ static const OUStringLiteral strFullPath( u"FullPath" );
+ static const OUStringLiteral strIsFolder( u"IsFolder" );
+ for ( sal_Int32 pos = bundle.getLength(); pos--; )
+ {
+ Reference<deployment::XPackage> const & xPackage = pbundle[ pos ];
+ OUString url_( expandUnoRcUrl( xPackage->getURL() ) );
+ OSL_ASSERT( url_.getLength() >= baseURLlen );
+ OUString fullPath;
+ if (url_.getLength() > baseURLlen)
+ fullPath = url_.copy( baseURLlen + 1 );
+ ::ucbhelper::Content ucbContent(
+ url_, xCmdEnv, getMyBackend()->getComponentContext() );
+ if (ucbContent.getPropertyValue(strIsFolder).get<bool>())
+ fullPath += "/";
+ Sequence<beans::PropertyValue> attribs( 2 );
+ beans::PropertyValue * pattribs = attribs.getArray();
+ pattribs[ 0 ].Name = strFullPath;
+ pattribs[ 0 ].Value <<= fullPath;
+ pattribs[ 1 ].Name = strMediaType;
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OUString mediaType;
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ else
+ mediaType = "unknown";
+ pattribs[ 1 ].Value <<= mediaType;
+ manifest.push_back( attribs );
+ }
+
+ // write into pipe:
+ Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestWriter> xManifestWriter =
+ packages::manifest::ManifestWriter::create( xContext );
+ Reference<io::XOutputStream> xPipe( io::Pipe::create(xContext), UNO_QUERY_THROW );
+ xManifestWriter->writeManifestSequence(
+ xPipe, comphelper::containerToSequence(manifest) );
+
+ // write buffered pipe data to content:
+ ::ucbhelper::Content manifestContent(
+ makeURL( metainfFolderContent.getURL(), "manifest.xml" ),
+ xCmdEnv, getMyBackend()->getComponentContext() );
+ manifestContent.writeStream(
+ Reference<io::XInputStream>( xPipe, UNO_QUERY_THROW ),
+ true /* replace existing */ );
+ }
+ else
+ {
+ bool bSuccess = false;
+ try
+ {
+ // overwrite manifest.xml:
+ ::ucbhelper::Content manifestContent;
+ if ( ! create_ucb_content(
+ &manifestContent,
+ makeURL( m_url_expanded, "META-INF/manifest.xml" ),
+ xCmdEnv, false ) )
+ {
+ OSL_FAIL( "### missing META-INF/manifest.xml file!" );
+ return;
+ }
+
+ metainfFolderContent.transferContent(
+ manifestContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(), ucb::NameClash::OVERWRITE );
+ bSuccess = true;
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "exception on overwriting manifest");
+ }
+
+ if (!bSuccess)
+ throw RuntimeException( "UCB transferContent() failed!",
+ static_cast<OWeakObject *>(this) );
+ }
+
+ // xxx todo: maybe obsolete in the future
+ try {
+ destFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const ucb::UnsupportedCommandException &) {
+ }
+}
+
+
+sal_Bool BackendImpl::PackageImpl::isBundle()
+{
+ return true;
+}
+
+
+Sequence< Reference<deployment::XPackage> > BackendImpl::PackageImpl::getBundle(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Sequence< Reference<deployment::XPackage> > * pBundle = m_pBundle;
+ if (pBundle == nullptr)
+ {
+ t_packagevec bundle;
+ if (m_bRemoved)
+ {
+ bundle = getPackagesFromDb(xCmdEnv);
+ }
+ else
+ {
+ try {
+ if (m_legacyBundle)
+ {
+ // .zip legacy packages allow script.xlb, dialog.xlb in bundle
+ // root folder:
+ OUString mediaType;
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "script.xlb" ),
+ xCmdEnv, false /* no throw */ )) {
+ mediaType = "application/vnd.sun.star.basic-library";
+ }
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( m_url_expanded, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+
+ if (!mediaType.isEmpty()) {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( getURL(), mediaType, false, OUString(),
+ xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ // continue scanning:
+ }
+ scanLegacyBundle( bundle, getURL(),
+ AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+ else
+ {
+ // .oxt:
+ scanBundle( bundle, AbortChannel::get(xAbortChannel), xCmdEnv );
+ }
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ throw;
+ }
+ catch (const ucb::CommandAbortedException &) {
+ throw;
+ }
+ catch (const deployment::DeploymentException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ throw deployment::DeploymentException(
+ "error scanning bundle: " + getURL(),
+ static_cast<OWeakObject *>(this), exc );
+ }
+ }
+
+ // sort: schema before config data, typelibs before components:
+ Sequence< Reference<deployment::XPackage> > ret( bundle.size() );
+ Reference<deployment::XPackage> * pret = ret.getArray();
+ sal_Int32 lower_end = 0;
+ sal_Int32 upper_end = ret.getLength();
+ for (auto const& elem : bundle)
+ {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ elem->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ {
+ const OUString mediaType( xPackageType->getMediaType() );
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ) &&
+ type.equalsIgnoreAsciiCase("application") &&
+ (subType.equalsIgnoreAsciiCase( "vnd.sun.star.uno-component") ||
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.configuration-data")))
+ {
+ --upper_end;
+ pret[ upper_end ] = elem;
+ continue;
+ }
+ }
+ pret[ lower_end ] = elem;
+ ++lower_end;
+ }
+ OSL_ASSERT( lower_end == upper_end );
+
+ const ::osl::MutexGuard guard( m_aMutex );
+ pBundle = m_pBundle;
+ if (pBundle == nullptr) {
+ m_bundle = ret;
+ pBundle = &m_bundle;
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ m_pBundle = pBundle;
+ }
+ }
+ else {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+ return *pBundle;
+}
+
+bool isBundle_( std::u16string_view mediaType )
+{
+ // xxx todo: additional parsing?
+ return !mediaType.empty() &&
+ (o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.package-bundle") ||
+ o3tl::matchIgnoreAsciiCase( mediaType, u"application/vnd.sun.star.legacy-package-bundle"));
+}
+
+
+Reference<deployment::XPackage> BackendImpl::PackageImpl::bindBundleItem(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool notifyDetectionError )
+{
+ // ignore any nested bundles:
+ if (isBundle_(mediaType))
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage>xPackage;
+ try {
+ try {
+ xPackage.set( getMyBackend()->m_xRootRegistry->bindPackage(
+ url, mediaType, bRemoved, identifier, xCmdEnv ) );
+ OSL_ASSERT( xPackage.is() );
+ } catch (css::lang::IllegalArgumentException & e) {
+ css::uno::Any exc(cppu::getCaughtException());
+ throw css::lang::WrappedTargetException(
+ "wrapped: " + e.Message, e.Context, exc);
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const ucb::CommandFailedException &) {
+ // ignore already handled error
+ }
+ catch (const Exception &) {
+ const Any exc( ::cppu::getCaughtException() );
+ if (notifyDetectionError ||
+ !exc.isExtractableTo( cppu::UnoType<lang::IllegalArgumentException>::get()) )
+ {
+ (void)interactContinuation(
+ Any( lang::WrappedTargetException("bundle item error!",
+ static_cast<OWeakObject *>(this), exc ) ),
+ cppu::UnoType<task::XInteractionApprove>::get(), xCmdEnv, nullptr, nullptr );
+ }
+ }
+
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ // ignore any nested bundles:
+ if (xPackageType.is() && isBundle_( xPackageType->getMediaType() ))
+ xPackage.clear();
+ }
+ return xPackage;
+}
+
+
+void BackendImpl::PackageImpl::scanBundle(
+ t_packagevec & bundle,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ OSL_ASSERT( !m_legacyBundle );
+
+ OUString mfUrl( makeURL( m_url_expanded, "META-INF/manifest.xml" ) );
+ ::ucbhelper::Content manifestContent;
+ if (! create_ucb_content(
+ &manifestContent, mfUrl, xCmdEnv, false /* no throw */ ))
+ {
+ SAL_WARN(
+ "desktop.deployment",
+ "cannot create UCB Content for <" << mfUrl << ">" );
+ return;
+ }
+
+
+ const LanguageTag& officeLocale = getOfficeLanguageTag();
+ const std::vector< OUString > officeFallbacks( officeLocale.getFallbackStrings( true));
+ const size_t nPenaltyMax = std::numeric_limits<size_t>::max();
+ size_t descrPenalty = nPenaltyMax;
+ OUString descrFile;
+
+ const Reference<XComponentContext> xContext(
+ getMyBackend()->getComponentContext() );
+ Reference<packages::manifest::XManifestReader> xManifestReader =
+ packages::manifest::ManifestReader::create( xContext );
+ const Sequence< Sequence<beans::PropertyValue> > manifestSeq(
+ xManifestReader->readManifestSequence( manifestContent.openStream() ) );
+ const OUString packageRootURL( getURL() );
+ for ( sal_Int32 pos = manifestSeq.getLength(); pos--; )
+ {
+ OUString fullPath, mediaType;
+ Sequence<beans::PropertyValue> const & attribs = manifestSeq[ pos ];
+ for ( sal_Int32 i = attribs.getLength(); i--; )
+ {
+ if (!(fullPath.isEmpty() || mediaType.isEmpty()))
+ break;
+ if ( attribs[i].Name == "FullPath" )
+ attribs[i].Value >>= fullPath;
+ else if ( attribs[i].Name == "MediaType" )
+ attribs[i].Value >>= mediaType;
+ }
+
+ if ( fullPath.isEmpty() || mediaType.isEmpty() || mediaType == "text/xml" )// opt: exclude common text/xml
+ continue;
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (! INetContentTypes::parse( mediaType, type, subType, &params ))
+ continue;
+
+ {
+ auto const iter = params.find("platform");
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ continue;
+ }
+ const OUString url( makeURL( packageRootURL, fullPath ) );
+
+ // check for bundle description:
+ if (type.equalsIgnoreAsciiCase("application") &&
+ subType.equalsIgnoreAsciiCase( "vnd.sun.star.package-bundle-description"))
+ {
+ // check locale:
+ auto const iter = params.find("locale");
+ if (iter == params.end())
+ {
+ if (descrFile.isEmpty())
+ descrFile = url;
+ }
+ else {
+ // match best locale:
+ LanguageTag descrTag(iter->second.m_sValue);
+ if (officeLocale.getLanguage() == descrTag.getLanguage())
+ {
+ size_t nPenalty = nPenaltyMax;
+ const std::vector< OUString > descrFallbacks( descrTag.getFallbackStrings( true));
+ for (size_t o=0; o < officeFallbacks.size() && nPenalty == nPenaltyMax; ++o)
+ {
+ for (size_t d=0; d < descrFallbacks.size() && nPenalty == nPenaltyMax; ++d)
+ {
+ if (officeFallbacks[o] == descrFallbacks[d])
+ {
+ // The last fallbacks are always language-only
+ // fallbacks, so we _will_ have _some_ match if
+ // we ever entered the overall if() condition.
+ nPenalty = o * 1000 + d;
+ if (descrPenalty > nPenalty)
+ {
+ descrPenalty = nPenalty;
+ descrFile = url;
+ }
+ }
+ }
+ }
+ }
+ // TODO: we could break here if descrPenalty==0 for an exact
+ // match of officeLocale, but the previous code didn't; are
+ // there side effects?
+ }
+ continue;
+ }
+
+ checkAborted( abortChannel );
+
+ //We make sure that we only create one XPackage for a particular URL.
+ //Sometime programmers insert the same URL several times in the manifest
+ //which may lead to DisposedExceptions.
+ if (std::none_of(bundle.begin(), bundle.end(), XPackage_eq(url)))
+ {
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( url, mediaType, false, OUString(), xCmdEnv ) );
+ if (xPackage.is())
+ bundle.push_back( xPackage );
+ }
+ else
+ {
+ SAL_WARN("desktop.deployment", "manifest.xml contains a duplicate entry (from " << url << ")");
+ }
+ }
+
+ if (descrFile.isEmpty())
+ return;
+
+ ::ucbhelper::Content descrFileContent;
+ if (!create_ucb_content( &descrFileContent, descrFile,
+ xCmdEnv, false /* no throw */ ))
+ return;
+
+ // patch description:
+ std::vector<sal_Int8> bytes( readFile( descrFileContent ) );
+ OUStringBuffer buf;
+ if ( !bytes.empty() )
+ {
+ buf.append( OUString( reinterpret_cast<char const *>(
+ bytes.data() ),
+ bytes.size(), RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ buf.append( Package::getDescription() );
+ }
+ m_oldDescription = buf.makeStringAndClear();
+}
+
+
+void BackendImpl::PackageImpl::scanLegacyBundle(
+ t_packagevec & bundle,
+ OUString const & url,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ bool skip_registration )
+{
+ ::ucbhelper::Content ucbContent(
+ url, xCmdEnv, getMyBackend()->getComponentContext() );
+
+ // check for platform paths:
+ const OUString title( StrTitle::getTitle( ucbContent ) );
+ if (title.endsWithIgnoreAsciiCase( ".plt" ) &&
+ !platform_fits( title.subView( 0, title.getLength() - 4 ) )) {
+ return;
+ }
+ if (title.endsWithIgnoreAsciiCase("skip_registration") )
+ skip_registration = true;
+
+ Sequence<OUString> ar { OUString("Title"), OUString("IsFolder") };
+ Reference<sdbc::XResultSet> xResultSet( ucbContent.createCursor( ar ) );
+ while (xResultSet->next())
+ {
+ checkAborted( abortChannel );
+
+ const Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ const OUString title_enc( ::rtl::Uri::encode(
+ xRow->getString( 1 /* Title */ ),
+ rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ const OUString path( makeURL( url, title_enc ) );
+
+ OUString mediaType;
+ const Reference<deployment::XPackage> xPackage(
+ bindBundleItem( path, OUString() /* detect */, false, OUString(),
+ xCmdEnv, false /* ignore detection errors */ ) );
+ if (xPackage.is()) {
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+
+ if (skip_registration &&
+ // xxx todo: additional parsing?
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.uno-component"))
+ continue;
+
+ bundle.push_back( xPackage );
+ }
+
+ if (mediaType.isEmpty() ||
+ // script.xlb, dialog.xlb can be met everywhere:
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.basic-library") ||
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.dialog-library"))
+ {
+ if (xRow->getBoolean( 2 /* IsFolder */ )) { // recurse into folder:
+ scanLegacyBundle(
+ bundle, path, abortChannel, xCmdEnv, skip_registration );
+ }
+ }
+ }
+}
+
+OUString BackendImpl::PackageImpl::getDisplayName()
+{
+ if (m_bRemoved)
+ throw deployment::ExtensionRemovedException();
+
+ OUString sName = getDescriptionInfoset().getLocalizedDisplayName();
+ if (sName.isEmpty())
+ return m_displayName;
+ else
+ return sName;
+}
+
+std::vector<Reference<deployment::XPackage> >
+BackendImpl::PackageImpl::getPackagesFromDb(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > retVector;
+
+ for (auto const& item : m_dbData.items)
+ {
+ Reference<deployment::XPackage> xExtension =
+ bindBundleItem(item.first, item.second, true, m_identifier, xCmdEnv);
+ OSL_ASSERT(xExtension.is());
+ if (xExtension.is())
+ retVector.push_back(xExtension);
+ }
+
+ return retVector;
+}
+
+} // anon namespace
+
+
+Reference<deployment::XPackageRegistry> create(
+ Reference<deployment::XPackageRegistry> const & xRootRegistry,
+ OUString const & context, OUString const & cachePath,
+ Reference<XComponentContext> const & xComponentContext )
+{
+ Sequence<Any> args(cachePath.isEmpty() ? 1 : 3 );
+ auto pArgs = args.getArray();
+ pArgs[ 0 ] <<= context;
+ if (!cachePath.isEmpty()) {
+ pArgs[ 1 ] <<= cachePath;
+ pArgs[ 2 ] <<= false; // readOnly
+ }
+ return new BackendImpl( args, xComponentContext, xRootRegistry );
+}
+
+} // namespace dp_registry
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.cxx b/desktop/source/deployment/registry/script/dp_lib_container.cxx
new file mode 100644
index 000000000..3a6f30253
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+#include <strings.hrc>
+#include <dp_resource.h>
+#include <dp_shared.hxx>
+#include <dp_xml.h>
+#include "dp_lib_container.h"
+
+#include <rtl/ustring.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xmllib_imexp.hxx>
+
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::script {
+
+namespace {
+ OUString StrCannotDetermineLibName() { return DpResId(RID_STR_CANNOT_DETERMINE_LIBNAME); }
+}
+
+OUString LibraryContainer::get_libname(
+ OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ Reference<XComponentContext> const & xContext )
+{
+ ::xmlscript::LibDescriptor import;
+ ::ucbhelper::Content ucb_content( url, xCmdEnv, xContext );
+ xml_parse( ::xmlscript::importLibrary( import ), ucb_content, xContext );
+
+ if (import.aName.isEmpty()) {
+ throw Exception( StrCannotDetermineLibName(),
+ Reference<XInterface>() );
+ }
+ return import.aName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_lib_container.h b/desktop/source/deployment/registry/script/dp_lib_container.h
new file mode 100644
index 000000000..fbbedf866
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_lib_container.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+namespace com::sun::star {
+ namespace uno {
+ class XComponentContext;
+ }
+ namespace ucb {
+ class XCommandEnvironment;
+ }
+}
+
+
+namespace dp_registry::backend::script {
+
+
+class LibraryContainer
+{
+public:
+ static OUString get_libname(
+ OUString const & url,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const & xContext );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_script.cxx b/desktop/source/deployment/registry/script/dp_script.cxx
new file mode 100644
index 000000000..47e41a364
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_script.cxx
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <strings.hrc>
+#include "dp_lib_container.h"
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <ucbhelper/content.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/script/XLibraryContainer3.hpp>
+#include <memory>
+#include <string_view>
+
+#include "dp_scriptbackenddb.hxx"
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_registry::backend::script {
+namespace {
+
+typedef ::cppu::ImplInheritanceHelper<
+ ::dp_registry::backend::PackageRegistryBackend, util::XUpdatable > t_helper;
+
+class BackendImpl : public t_helper
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ const OUString m_scriptURL;
+ const OUString m_dialogURL;
+ OUString m_dialogName;
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL,
+ bool bRemoved, OUString const & identifier);
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ void addDataToDb(OUString const & url);
+ bool hasActiveEntry(std::u16string_view url);
+ void revokeEntryFromDb(std::u16string_view url);
+
+ const Reference<deployment::XPackageTypeInfo> m_xBasicLibTypeInfo;
+ const Reference<deployment::XPackageTypeInfo> m_xDialogLibTypeInfo;
+ Sequence< Reference<deployment::XPackageTypeInfo> > m_typeInfos;
+ std::unique_ptr<ScriptBackendDb> m_backendDb;
+public:
+ BackendImpl( Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XUpdatable
+ virtual void SAL_CALL update() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+
+};
+
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url,
+ Reference<XCommandEnvironment> const &xCmdEnv,
+ OUString const & scriptURL, OUString const & dialogURL, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url,
+ OUString(), OUString(), // will be late-initialized
+ !scriptURL.isEmpty() ? myBackend->m_xBasicLibTypeInfo
+ : myBackend->m_xDialogLibTypeInfo, bRemoved, identifier),
+ m_scriptURL( scriptURL ),
+ m_dialogURL( dialogURL )
+{
+ // name, displayName:
+ if (!dialogURL.isEmpty()) {
+ m_dialogName = LibraryContainer::get_libname(
+ dialogURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ if (!scriptURL.isEmpty()) {
+ assert(m_name.pData);
+ m_name = LibraryContainer::get_libname(
+ scriptURL, xCmdEnv, myBackend->getComponentContext() );
+ }
+ else
+ m_name = m_dialogName;
+ m_displayName = m_name;
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : t_helper( args, xComponentContext ),
+ m_xBasicLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.basic-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_BASIC_LIB)
+ ) ),
+ m_xDialogLibTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.dialog-library",
+ OUString() /* no file filter */,
+ DpResId(RID_STR_DIALOG_LIB)
+ ) ),
+ m_typeInfos{ m_xBasicLibTypeInfo, m_xDialogLibTypeInfo }
+{
+ OSL_ASSERT( ! transientMode() );
+
+ if (!transientMode())
+ {
+ OUString dbFile = makeURL(getCachePath(), "backenddb.xml");
+ m_backendDb.reset(
+ new ScriptBackendDb(getComponentContext(), dbFile));
+ }
+
+}
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.script.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+void BackendImpl::addDataToDb(OUString const & url)
+{
+ if (m_backendDb)
+ m_backendDb->addEntry(url);
+}
+
+bool BackendImpl::hasActiveEntry(std::u16string_view url)
+{
+ if (m_backendDb)
+ return m_backendDb->hasActiveEntry(url);
+ return false;
+}
+
+// XUpdatable
+
+void BackendImpl::update()
+{
+ // Nothing to do here after fixing i70283!?
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return m_typeInfos;
+}
+void BackendImpl::revokeEntryFromDb(std::u16string_view url)
+{
+ if (m_backendDb)
+ m_backendDb->revokeEntry(url);
+}
+
+void BackendImpl::packageRemoved(OUString const & url, OUString const & /*mediaType*/)
+{
+ if (m_backendDb)
+ m_backendDb->removeEntry(url);
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for script.xlb:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "script.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.basic-library";
+ // probe for dialog.xlb:
+ else if (create_ucb_content(
+ nullptr, makeURL( url, "dialog.xlb" ),
+ xCmdEnv, false /* no throw */ ))
+ mediaType = "application/vnd.sun.star.dialog-library";
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ OUString dialogURL( makeURL( url, "dialog.xlb" ) );
+ if (! create_ucb_content(
+ nullptr, dialogURL, xCmdEnv, false /* no throw */ )) {
+ dialogURL.clear();
+ }
+
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.basic-library"))
+ {
+ OUString scriptURL( makeURL( url, "script.xlb"));
+ if (! create_ucb_content(
+ nullptr, scriptURL, xCmdEnv, false /* no throw */ )) {
+ scriptURL.clear();
+ }
+
+ return new PackageImpl(
+ this, url, xCmdEnv, scriptURL,
+ dialogURL, bRemoved, identifier);
+ }
+ else if (subType.equalsIgnoreAsciiCase(
+ "vnd.sun.star.dialog-library")) {
+ return new PackageImpl(
+ this, url, xCmdEnv,
+ OUString() /* no script lib */,
+ dialogURL,
+ bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+// Package
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException(
+ "Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+ Reference< deployment::XPackage > xThisPackage( this );
+
+ bool registered = that->hasActiveEntry(getURL());
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>( registered, false /* IsAmbiguous */ ) );
+}
+
+void
+lcl_maybeRemoveScript(
+ bool const bExists,
+ OUString const& rName,
+ std::u16string_view rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (bExists && xScriptLibs.is() && xScriptLibs->hasByName(rName))
+ {
+ const OUString sScriptUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ if (sScriptUrl == rScriptURL)
+ xScriptLibs->removeLibrary(rName);
+ }
+}
+
+bool
+lcl_maybeAddScript(
+ bool const bExists,
+ OUString const& rName,
+ OUString const& rScriptURL,
+ Reference<css::script::XLibraryContainer3> const& xScriptLibs)
+{
+ if (!bExists || !xScriptLibs)
+ return false;
+
+ bool bCanAdd = true;
+ if (xScriptLibs->hasByName(rName))
+ {
+ const OUString sOriginalUrl = xScriptLibs->getOriginalLibraryLinkURL(rName);
+ //We assume here that library names in extensions are unique, which may not be the case
+ //ToDo: If the script exist in another extension, then both extensions must have the
+ //same id
+ if (sOriginalUrl.match("vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE")
+ || sOriginalUrl.match("vnd.sun.star.expand:$BUNDLED_EXTENSIONS")
+ || sOriginalUrl.match("$(INST)/share/basic/Access2Base/"))
+ {
+ xScriptLibs->removeLibrary(rName);
+ bCanAdd = true;
+ }
+ else
+ {
+ bCanAdd = false;
+ }
+ }
+
+ if (bCanAdd)
+ {
+ xScriptLibs->createLibraryLink(rName, rScriptURL, false);
+ return xScriptLibs->hasByName(rName);
+ }
+
+ return false;
+}
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard & /* guard */,
+ bool doRegisterPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & /* abortChannel */,
+ Reference<XCommandEnvironment> const & /* xCmdEnv */ )
+{
+ BackendImpl * that = getMyBackend();
+
+ Reference< deployment::XPackage > xThisPackage( this );
+ Reference<XComponentContext> const & xComponentContext = that->getComponentContext();
+
+ bool bScript = !m_scriptURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xScriptLibs;
+
+ bool bDialog = !m_dialogURL.isEmpty();
+ Reference<css::script::XLibraryContainer3> xDialogLibs;
+
+ bool bRunning = !startup && office_is_running();
+ if( bRunning )
+ {
+ if( bScript )
+ {
+ xScriptLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationScriptLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+
+ if( bDialog )
+ {
+ xDialogLibs.set(
+ xComponentContext->getServiceManager()->createInstanceWithContext(
+ "com.sun.star.script.ApplicationDialogLibraryContainer",
+ xComponentContext ), UNO_QUERY_THROW );
+ }
+ }
+ bool bRegistered = getMyBackend()->hasActiveEntry(getURL());
+ if( !doRegisterPackage )
+ {
+ //We cannot just call removeLibrary(name) because this could remove a
+ //script which was added by an extension in a different repository. For
+ //example, extension foo is contained in the bundled repository and then
+ //the user adds it to the user repository. The extension manager will
+ //then register the new script and revoke the script from the bundled
+ //extension. removeLibrary(name) would now remove the script from the
+ //user repository. That is, the script of the newly added user extension does
+ //not work anymore. Therefore we must check if the currently active
+ //script comes in fact from the currently processed extension.
+
+ if (bRegistered)
+ {
+ //we also prevent and live deployment at startup
+ if (!isRemoved() && !startup)
+ {
+ lcl_maybeRemoveScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ lcl_maybeRemoveScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ getMyBackend()->revokeEntryFromDb(getURL());
+ return;
+ }
+ }
+ if (bRegistered)
+ return; // Already registered
+
+ // Update LibraryContainer
+ bool bScriptSuccess = false;
+ bool bDialogSuccess = false;
+ if (!startup)
+ {
+ //If there is a bundled extension, and the user installs the same extension
+ //then the script from the bundled extension must be removed. If this does not work
+ //then live deployment does not work for scripts.
+ bScriptSuccess = lcl_maybeAddScript(bScript, m_name, m_scriptURL, xScriptLibs);
+ bDialogSuccess = lcl_maybeAddScript(bDialog, m_dialogName, m_dialogURL, xDialogLibs);
+ }
+ bool bSuccess = bScript || bDialog; // Something must have happened
+ if( bRunning )
+ if( (bScript && !bScriptSuccess) || (bDialog && !bDialogSuccess) )
+ bSuccess = false;
+
+ if (bSuccess)
+ getMyBackend()->addDataToDb(getURL());
+}
+
+} // anon namespace
+
+} // namespace dp_registry::backend::script
+
+extern "C" 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)
+{
+ return cppu::acquire(new dp_registry::backend::script::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
new file mode 100644
index 000000000..476f43953
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.cxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include "dp_scriptbackenddb.hxx"
+
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUStringLiteral EXTENSION_REG_NS = u"http://openoffice.org/extensionmanager/script-registry/2010";
+constexpr OUStringLiteral NS_PREFIX = u"script";
+constexpr OUStringLiteral ROOT_ELEMENT_NAME = u"script-backend-db";
+constexpr OUStringLiteral KEY_ELEMENT_NAME = u"script";
+
+namespace dp_registry::backend::script {
+
+ScriptBackendDb::ScriptBackendDb(
+ Reference<XComponentContext> const & xContext,
+ OUString const & url):RegisteredDb(xContext, url)
+{
+
+}
+
+OUString ScriptBackendDb::getDbNSName()
+{
+ return EXTENSION_REG_NS;
+}
+
+OUString ScriptBackendDb::getNSPrefix()
+{
+ return NS_PREFIX;
+}
+
+OUString ScriptBackendDb::getRootElementName()
+{
+ return ROOT_ELEMENT_NAME;
+}
+
+OUString ScriptBackendDb::getKeyElementName()
+{
+ return KEY_ELEMENT_NAME;
+}
+
+
+} // namespace dp_registry::backend::script
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
new file mode 100644
index 000000000..21d4b1f6b
--- /dev/null
+++ b/desktop/source/deployment/registry/script/dp_scriptbackenddb.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <dp_backenddb.hxx>
+#include <optional>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace dp_registry::backend::script {
+
+/* The XML file stores the extensions which are currently registered.
+ They will be removed when they are revoked.
+ */
+class ScriptBackendDb: public dp_registry::backend::RegisteredDb
+{
+protected:
+ virtual OUString getDbNSName() override;
+
+ virtual OUString getNSPrefix() override;
+
+ virtual OUString getRootElementName() override;
+
+ virtual OUString getKeyElementName() override;
+
+
+public:
+
+ ScriptBackendDb( css::uno::Reference<css::uno::XComponentContext> const & xContext,
+ OUString const & url);
+};
+
+
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
new file mode 100644
index 000000000..530924a07
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.cxx
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dp_misc.h>
+#include "dp_parceldesc.hxx"
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+
+// XDocumentHandler
+void SAL_CALL
+ParcelDescDocHandler::startDocument()
+{
+ m_bIsParsed = false;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::endDocument()
+{
+ m_bIsParsed = true;
+}
+
+void SAL_CALL
+ParcelDescDocHandler::characters( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::ignorableWhitespace( const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::processingInstruction(
+ const OUString &, const OUString & )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::setDocumentLocator(
+ const Reference< xml::sax::XLocator >& )
+{
+}
+
+void SAL_CALL
+ParcelDescDocHandler::startElement( const OUString& aName,
+ const Reference< xml::sax::XAttributeList > & xAttribs )
+{
+
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() for " +
+ aName + "\n");
+ if ( !skipIndex )
+ {
+ if ( aName == "parcel" )
+ {
+ m_sLang = xAttribs->getValueByName( "language" );
+ }
+ ++skipIndex;
+ }
+ else
+ {
+ dp_misc::TRACE("ParcelDescDocHandler::startElement() skipping for "
+ + aName + "\n");
+ }
+
+}
+
+void SAL_CALL ParcelDescDocHandler::endElement( const OUString & aName )
+{
+ if ( skipIndex )
+ {
+ --skipIndex;
+ dp_misc::TRACE("ParcelDescDocHandler::endElement() skipping for "
+ + aName + "\n");
+ }
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
new file mode 100644
index 000000000..6b5bde8bd
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_parceldesc.hxx
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/xml/sax/XAttributeList.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+
+namespace dp_registry::backend::sfwk
+{
+
+class ParcelDescDocHandler : public ::cppu::WeakImplHelper< css::xml::sax::XDocumentHandler >
+{
+private:
+ bool m_bIsParsed;
+ OUString m_sLang;
+ sal_Int32 skipIndex;
+public:
+ ParcelDescDocHandler():m_bIsParsed( false ), skipIndex( 0 ){}
+ const OUString& getParcelLanguage() const { return m_sLang; }
+ bool isParsed() const { return m_bIsParsed; }
+ // XDocumentHandler
+ virtual void SAL_CALL startDocument() override;
+
+ virtual void SAL_CALL endDocument() override;
+
+ virtual void SAL_CALL startElement( const OUString& aName,
+ const css::uno::Reference< css::xml::sax::XAttributeList > & xAttribs ) override;
+
+ virtual void SAL_CALL endElement( const OUString & aName ) override;
+
+ virtual void SAL_CALL characters( const OUString & aChars ) override;
+
+ virtual void SAL_CALL ignorableWhitespace( const OUString & aWhitespaces ) override;
+
+ virtual void SAL_CALL processingInstruction(
+ const OUString & aTarget, const OUString & aData ) override;
+
+ virtual void SAL_CALL setDocumentLocator(
+ const css::uno::Reference< css::xml::sax::XLocator >& xLocator ) override;
+};
+}
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
new file mode 100644
index 000000000..b617d7fa4
--- /dev/null
+++ b/desktop/source/deployment/registry/sfwk/dp_sfwk.cxx
@@ -0,0 +1,378 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <strings.hrc>
+#include <dp_backend.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include "dp_parceldesc.hxx"
+#include <rtl/uri.hxx>
+#include <ucbhelper/content.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <cppuhelper/supportsservice.hxx>
+#include <utility>
+
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::script;
+
+
+namespace dp_registry::backend::sfwk
+{
+
+namespace {
+
+class BackendImpl : public ::dp_registry::backend::PackageRegistryBackend
+{
+ class PackageImpl : public ::dp_registry::backend::Package
+ {
+ BackendImpl * getMyBackend() const;
+
+ Reference< container::XNameContainer > m_xNameCntrPkgHandler;
+ OUString m_descr;
+
+ void initPackageHandler();
+
+ // Package
+ virtual beans::Optional< beans::Ambiguous<sal_Bool> > isRegistered_(
+ ::osl::ResettableMutexGuard & guard,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+ virtual void processPackage_(
+ ::osl::ResettableMutexGuard & guard,
+ bool registerPackage,
+ bool startup,
+ ::rtl::Reference<AbortChannel> const & abortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ public:
+ PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString libType, bool bRemoved,
+ OUString const & identifier);
+ // XPackage
+ virtual OUString SAL_CALL getDescription() override;
+ virtual OUString SAL_CALL getLicenseText() override;
+ };
+ friend class PackageImpl;
+
+ // PackageRegistryBackend
+ virtual Reference<deployment::XPackage> bindPackage_(
+ OUString const & url, OUString const & mediaType,
+ bool bRemoved, OUString const & identifier,
+ Reference<XCommandEnvironment> const & xCmdEnv ) override;
+
+ const Reference<deployment::XPackageTypeInfo> m_xTypeInfo;
+
+
+public:
+ BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext );
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XPackageRegistry
+ virtual Sequence< Reference<deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+ virtual void SAL_CALL packageRemoved(OUString const & url, OUString const & mediaType) override;
+};
+
+}
+
+BackendImpl * BackendImpl::PackageImpl::getMyBackend() const
+{
+ BackendImpl * pBackend = static_cast<BackendImpl *>(m_myBackend.get());
+ if (nullptr == pBackend)
+ {
+ //May throw a DisposedException
+ check();
+ //We should never get here...
+ throw RuntimeException("Failed to get the BackendImpl",
+ static_cast<OWeakObject*>(const_cast<PackageImpl *>(this)));
+ }
+ return pBackend;
+}
+
+OUString BackendImpl::PackageImpl::getDescription()
+{
+ if (m_descr.isEmpty())
+ return Package::getDescription();
+ else
+ return m_descr;
+}
+
+OUString BackendImpl::PackageImpl::getLicenseText()
+{
+ return Package::getDescription();
+}
+
+BackendImpl::PackageImpl::PackageImpl(
+ ::rtl::Reference<BackendImpl> const & myBackend,
+ OUString const & url, OUString libType, bool bRemoved,
+ OUString const & identifier)
+ : Package( myBackend, url, OUString(), OUString(),
+ myBackend->m_xTypeInfo, bRemoved, identifier),
+ m_descr(std::move(libType))
+{
+ initPackageHandler();
+
+ sal_Int32 segmEnd = url.getLength();
+ if ( url.endsWith("/") )
+ --segmEnd;
+ sal_Int32 segmStart = url.lastIndexOf( '/', segmEnd ) + 1;
+ if (segmStart < 0)
+ segmStart = 0;
+ // name and display name default the same:
+ m_displayName = ::rtl::Uri::decode(
+ url.copy( segmStart, segmEnd - segmStart ),
+ rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ m_name = m_displayName;
+
+ dp_misc::TRACE("PackageImpl displayName is " + m_displayName);
+}
+
+
+BackendImpl::BackendImpl(
+ Sequence<Any> const & args,
+ Reference<XComponentContext> const & xComponentContext )
+ : PackageRegistryBackend( args, xComponentContext ),
+ m_xTypeInfo( new Package::TypeInfo(
+ "application/vnd.sun.star.framework-script",
+ OUString() /* no file filter */,
+ "Scripting Framework Script Library"
+ ) )
+{
+}
+
+
+// XServiceInfo
+OUString BackendImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.sfwk.PackageRegistryBackend";
+}
+
+sal_Bool BackendImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > BackendImpl::getSupportedServiceNames()
+{
+ return { BACKEND_SERVICE_NAME };
+}
+
+// XPackageRegistry
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+BackendImpl::getSupportedPackageTypes()
+{
+ return Sequence< Reference<deployment::XPackageTypeInfo> >(&m_xTypeInfo, 1);
+}
+
+void BackendImpl::packageRemoved(OUString const & /*url*/, OUString const & /*mediaType*/)
+{
+}
+
+// PackageRegistryBackend
+
+Reference<deployment::XPackage> BackendImpl::bindPackage_(
+ OUString const & url, OUString const & mediaType_, bool bRemoved,
+ OUString const & identifier, Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ OUString mediaType( mediaType_ );
+ if (mediaType.isEmpty())
+ {
+ // detect media-type:
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, url, xCmdEnv ) &&
+ ucbContent.isFolder())
+ {
+ // probe for parcel-descriptor.xml:
+ if (create_ucb_content(
+ nullptr, makeURL( url, "parcel-descriptor.xml" ),
+ xCmdEnv, false /* no throw */ ))
+ {
+ mediaType = "application/vnd.sun.star.framework-script";
+ }
+ }
+ if (mediaType.isEmpty())
+ throw lang::IllegalArgumentException(
+ StrCannotDetectMediaType() + url,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+ }
+
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( mediaType, type, subType, &params ))
+ {
+ if (type.equalsIgnoreAsciiCase("application"))
+ {
+ if (subType.equalsIgnoreAsciiCase("vnd.sun.star.framework-script"))
+ {
+ OUString lang = "Script";
+ OUString sParcelDescURL = makeURL(
+ url, "parcel-descriptor.xml" );
+
+ ::ucbhelper::Content ucb_content;
+
+ if (create_ucb_content( &ucb_content, sParcelDescURL,
+ xCmdEnv, false /* no throw */ ))
+ {
+ rtl::Reference<ParcelDescDocHandler> pHandler =
+ new ParcelDescDocHandler();
+
+ Reference<XComponentContext>
+ xContext( getComponentContext() );
+
+ Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(xContext);
+
+ xParser->setDocumentHandler( pHandler );
+ xml::sax::InputSource source;
+ source.aInputStream = ucb_content.openStream();
+ source.sSystemId = ucb_content.getURL();
+ xParser->parseStream( source );
+
+ if ( pHandler->isParsed() )
+ {
+ lang = pHandler->getParcelLanguage();
+ }
+ }
+
+ OUString sfwkLibType = DpResId( RID_STR_SFWK_LIB );
+ // replace %MACRONAME placeholder with language name
+ OUString MACRONAME( "%MACROLANG" );
+ sal_Int32 startOfReplace = sfwkLibType.indexOf( MACRONAME );
+ sal_Int32 charsToReplace = MACRONAME.getLength();
+ sfwkLibType = sfwkLibType.replaceAt( startOfReplace, charsToReplace, lang );
+ dp_misc::TRACE("******************************\n");
+ dp_misc::TRACE(" BackEnd detected lang = " + lang + "\n");
+ dp_misc::TRACE(" for url " + sParcelDescURL + "\n");
+ dp_misc::TRACE("******************************\n");
+ return new PackageImpl( this, url, sfwkLibType, bRemoved, identifier);
+ }
+ }
+ }
+ throw lang::IllegalArgumentException(
+ StrUnsupportedMediaType() + mediaType,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+}
+
+
+void BackendImpl::PackageImpl:: initPackageHandler()
+{
+ if (m_xNameCntrPkgHandler.is())
+ return;
+
+ BackendImpl * that = getMyBackend();
+ Any aContext;
+
+ if ( that->m_eContext == Context::User )
+ {
+ aContext <<= OUString("user");
+ }
+ else if ( that->m_eContext == Context::Shared )
+ {
+ aContext <<= OUString("share");
+ }
+ else if ( that->m_eContext == Context::Bundled )
+ {
+ aContext <<= OUString("bundled");
+ }
+ else
+ {
+ OSL_ASSERT( false );
+ // NOT supported at the moment // TODO
+ }
+
+ Reference< provider::XScriptProviderFactory > xFac =
+ provider::theMasterScriptProviderFactory::get( that->getComponentContext() );
+
+ Reference< container::XNameContainer > xName( xFac->createScriptProvider( aContext ), UNO_QUERY );
+ if ( xName.is() )
+ {
+ m_xNameCntrPkgHandler.set( xName );
+ }
+ // TODO what happens if above fails??
+}
+
+// Package
+
+beans::Optional< beans::Ambiguous<sal_Bool> >
+BackendImpl::PackageImpl::isRegistered_(
+ ::osl::ResettableMutexGuard &,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ return beans::Optional< beans::Ambiguous<sal_Bool> >(
+ true /* IsPresent */,
+ beans::Ambiguous<sal_Bool>(
+ m_xNameCntrPkgHandler.is() && m_xNameCntrPkgHandler->hasByName(
+ m_url ),
+ false /* IsAmbiguous */ ) );
+}
+
+
+void BackendImpl::PackageImpl::processPackage_(
+ ::osl::ResettableMutexGuard &,
+ bool doRegisterPackage,
+ bool /* startup */,
+ ::rtl::Reference<AbortChannel> const &,
+ Reference<XCommandEnvironment> const & )
+{
+ if ( !m_xNameCntrPkgHandler.is() )
+ {
+ dp_misc::TRACE("no package handler!!!!\n");
+ throw RuntimeException( "No package Handler " );
+ }
+
+ if (doRegisterPackage)
+ {
+ // will throw if it fails
+ m_xNameCntrPkgHandler->insertByName( m_url, Any( Reference< XPackage >(this) ) );
+
+ }
+ else // revokePackage()
+ {
+ m_xNameCntrPkgHandler->removeByName( m_url );
+ }
+}
+
+} // namespace dp_registry::backend::sfwk
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_sfwk_PackageRegistryBackend_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& args)
+{
+ return cppu::acquire(new dp_registry::backend::sfwk::BackendImpl(args, context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */