summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment/registry/package
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/source/deployment/registry/package')
-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
3 files changed, 1774 insertions, 0 deletions
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: */