summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment/manager
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/manager
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.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/manager')
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.cxx217
-rw-r--r--desktop/source/deployment/manager/dp_activepackages.hxx95
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.cxx246
-rw-r--r--desktop/source/deployment/manager/dp_commandenvironments.hxx139
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.cxx1432
-rw-r--r--desktop/source/deployment/manager/dp_extensionmanager.hxx227
-rw-r--r--desktop/source/deployment/manager/dp_informationprovider.cxx338
-rw-r--r--desktop/source/deployment/manager/dp_manager.cxx1600
-rw-r--r--desktop/source/deployment/manager/dp_manager.h232
-rw-r--r--desktop/source/deployment/manager/dp_managerfac.cxx186
-rw-r--r--desktop/source/deployment/manager/dp_properties.cxx145
-rw-r--r--desktop/source/deployment/manager/dp_properties.hxx58
12 files changed, 4915 insertions, 0 deletions
diff --git a/desktop/source/deployment/manager/dp_activepackages.cxx b/desktop/source/deployment/manager/dp_activepackages.cxx
new file mode 100644
index 000000000..c1c5f2b28
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.cxx
@@ -0,0 +1,217 @@
+/* -*- 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 <config_extensions.h>
+
+#include <sal/config.h>
+
+#include <string_view>
+#include <utility>
+
+#include <osl/diagnose.h>
+#include <rtl/string.hxx>
+#include <rtl/textenc.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+#include "dp_activepackages.hxx"
+
+// Old format of database entry:
+// key: UTF8(filename)
+// value: UTF8(tempname ";" mediatype)
+// New format of database entry:
+// key: 0xFF UTF8(identifier)
+// value: UTF8(tempname) 0xFF UTF8(filename) 0xFF UTF8(mediatype)
+
+#if HAVE_FEATURE_EXTENSIONS
+
+namespace {
+
+constexpr const char separator[] = "\xff";
+
+OString oldKey(std::u16string_view fileName) {
+ return OUStringToOString(fileName, RTL_TEXTENCODING_UTF8);
+}
+
+OString newKey(std::u16string_view id) {
+ return separator + OUStringToOString(id, RTL_TEXTENCODING_UTF8);
+}
+
+::dp_manager::ActivePackages::Data decodeOldData(
+ OUString const & fileName, OString const & value)
+{
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i = value.indexOf(';');
+ OSL_ASSERT(i >= 0);
+ d.temporaryName = OUString(value.getStr(), i, RTL_TEXTENCODING_UTF8);
+ d.fileName = fileName;
+ d.mediaType = OUString(
+ value.getStr() + i + 1, value.getLength() - i - 1,
+ RTL_TEXTENCODING_UTF8);
+ return d;
+}
+
+::dp_manager::ActivePackages::Data decodeNewData(OString const & value) {
+ ::dp_manager::ActivePackages::Data d;
+ sal_Int32 i1 = value.indexOf(separator);
+ OSL_ASSERT(i1 >= 0);
+ d.temporaryName = OUString(
+ value.getStr(), i1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i2 = value.indexOf(separator, i1 + 1);
+ OSL_ASSERT(i2 >= 0);
+ d.fileName = OUString(
+ value.getStr() + i1 + 1, i2 - i1 - 1, RTL_TEXTENCODING_UTF8);
+ sal_Int32 i3 = value.indexOf(separator, i2 + 1);
+
+ if (i3 < 0)
+ {
+ //Before ActivePackages::Data::version was added
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, value.getLength() - i2 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ else
+ {
+ sal_Int32 i4 = value.indexOf(separator, i3 + 1);
+ d.mediaType = OUString(
+ value.getStr() + i2 + 1, i3 - i2 -1, RTL_TEXTENCODING_UTF8);
+ d.version = OUString(
+ value.getStr() + i3 + 1, i4 - i3 - 1,
+ RTL_TEXTENCODING_UTF8);
+ d.failedPrerequisites = OUString(
+ value.getStr() + i4 + 1, value.getLength() - i4 - 1,
+ RTL_TEXTENCODING_UTF8);
+ }
+ return d;
+}
+
+}
+#endif
+
+namespace dp_manager {
+
+ActivePackages::ActivePackages() {}
+
+ActivePackages::ActivePackages(OUString const & url)
+#if HAVE_FEATURE_EXTENSIONS
+ : m_map(url)
+#endif
+{
+#if !HAVE_FEATURE_EXTENSIONS
+ (void) url;
+#endif
+}
+
+ActivePackages::~ActivePackages() {}
+
+bool ActivePackages::has(
+ OUString const & id, OUString const & fileName) const
+{
+ return get(nullptr, id, fileName);
+}
+
+bool ActivePackages::get(
+ Data * data, OUString const & id, OUString const & fileName)
+ const
+{
+#if HAVE_FEATURE_EXTENSIONS
+ OString v;
+ if (m_map.get(&v, newKey(id))) {
+ if (data != nullptr) {
+ *data = decodeNewData(v);
+ }
+ return true;
+ } else if (m_map.get(&v, oldKey(fileName))) {
+ if (data != nullptr) {
+ *data = decodeOldData(fileName, v);
+ }
+ return true;
+ } else {
+ return false;
+ }
+#else
+ (void) data;
+ (void) id;
+ (void) fileName;
+ (void) this;
+ return false;
+#endif
+}
+
+ActivePackages::Entries ActivePackages::getEntries() const {
+ Entries es;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::t_string2string_map m(m_map.getEntries());
+ for (auto const& elem : m)
+ {
+ if (!elem.first.isEmpty() && elem.first[0] == separator[0]) {
+ es.emplace_back(
+ OUString(
+ elem.first.getStr() + 1, elem.first.getLength() - 1,
+ RTL_TEXTENCODING_UTF8),
+ decodeNewData(elem.second));
+ } else {
+ OUString fn(
+ OStringToOUString(elem.first, RTL_TEXTENCODING_UTF8));
+ es.emplace_back(
+ ::dp_misc::generateLegacyIdentifier(fn),
+ decodeOldData(fn, elem.second));
+ }
+ }
+#else
+ (void) this;
+#endif
+ return es;
+}
+
+void ActivePackages::put(OUString const & id, Data const & data) {
+#if HAVE_FEATURE_EXTENSIONS
+ OString b =
+ OUStringToOString(data.temporaryName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.fileName, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.mediaType, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.version, RTL_TEXTENCODING_UTF8) +
+ separator +
+ OUStringToOString(data.failedPrerequisites, RTL_TEXTENCODING_UTF8);
+ m_map.put(newKey(id), b);
+#else
+ (void) id;
+ (void) data;
+ (void) this;
+#endif
+}
+
+void ActivePackages::erase(
+ OUString const & id, OUString const & fileName)
+{
+#if HAVE_FEATURE_EXTENSIONS
+ m_map.erase(newKey(id)) || m_map.erase(oldKey(fileName));
+#else
+ (void) id;
+ (void) fileName;
+ (void) this;
+#endif
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_activepackages.hxx b/desktop/source/deployment/manager/dp_activepackages.hxx
new file mode 100644
index 000000000..fae938019
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_activepackages.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 <config_extensions.h>
+
+#include <sal/config.h>
+#include <rtl/ustring.hxx>
+
+#include <utility>
+#include <vector>
+
+#if HAVE_FEATURE_EXTENSIONS
+#include <dp_persmap.h>
+#endif
+
+
+namespace dp_manager {
+
+class ActivePackages {
+public:
+ struct Data {
+ Data(): failedPrerequisites("0")
+ {}
+ /* name of the temporary file (shared, user extension) or the name of
+ the folder of the bundled extension.
+ It does not contain the trailing '_' of the folder.
+ UTF-8 encoded
+ */
+ OUString temporaryName;
+ /* The file name (shared, user) or the folder name (bundled)
+ If the key is the file name, then file name is not encoded.
+ If the key is the identifier then the file name is UTF-8 encoded.
+ */
+ OUString fileName;
+ OUString mediaType;
+ OUString version;
+ /* If this string contains the value according to
+ css::deployment::Prerequisites or "0". That is, if
+ the value is > 0 then
+ the call to XPackage::checkPrerequisites failed.
+ In this case the extension must not be registered.
+ */
+ OUString failedPrerequisites;
+ };
+
+ typedef std::vector< std::pair< OUString, Data > > Entries;
+
+ ActivePackages();
+
+ explicit ActivePackages(OUString const & url);
+
+ ~ActivePackages();
+
+ bool has(OUString const & id, OUString const & fileName)
+ const;
+
+ bool get(
+ Data * data, OUString const & id,
+ OUString const & fileName) const;
+
+ Entries getEntries() const;
+
+ void put(OUString const & id, Data const & value);
+
+ void erase(OUString const & id, OUString const & fileName);
+
+private:
+ ActivePackages(ActivePackages const &) = delete;
+ ActivePackages& operator =(ActivePackages const &) = delete;
+#if HAVE_FEATURE_EXTENSIONS
+ ::dp_misc::PersistentMap m_map;
+#endif
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.cxx b/desktop/source/deployment/manager/dp_commandenvironments.cxx
new file mode 100644
index 000000000..0a25e042f
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.cxx
@@ -0,0 +1,246 @@
+/* -*- 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/deployment/VersionException.hpp>
+#include <com/sun/star/deployment/LicenseException.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/DependencyException.hpp>
+#include <com/sun/star/deployment/PlatformException.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <utility>
+#include "dp_commandenvironments.hxx"
+#include <osl/diagnose.h>
+
+namespace deployment = com::sun::star::deployment;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+using ::com::sun::star::uno::Reference;
+
+namespace dp_manager {
+
+BaseCommandEnv::BaseCommandEnv()
+{
+}
+
+BaseCommandEnv::BaseCommandEnv(
+ Reference< task::XInteractionHandler> const & handler)
+ : m_forwardHandler(handler)
+{
+}
+
+BaseCommandEnv::~BaseCommandEnv()
+{
+}
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler> BaseCommandEnv::getInteractionHandler()
+{
+ return this;
+}
+
+
+Reference<ucb::XProgressHandler> BaseCommandEnv::getProgressHandler()
+{
+ return this;
+}
+
+void BaseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & /*xRequest*/ )
+{
+}
+
+void BaseCommandEnv::handle_(bool approve,
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ if (!approve)
+ {
+ //not handled so far -> forwarding
+ if (m_forwardHandler.is())
+ m_forwardHandler->handle(xRequest);
+ else
+ return; //cannot handle
+ }
+ else
+ {
+ // select:
+ uno::Sequence< Reference< task::XInteractionContinuation > > conts(
+ xRequest->getContinuations() );
+ Reference< task::XInteractionContinuation > const * pConts =
+ conts.getConstArray();
+ sal_Int32 len = conts.getLength();
+ for ( sal_Int32 pos = 0; pos < len; ++pos )
+ {
+ if (approve) {
+ Reference< task::XInteractionApprove > xInteractionApprove(
+ pConts[ pos ], uno::UNO_QUERY );
+ if (xInteractionApprove.is()) {
+ xInteractionApprove->select();
+ // don't query again for ongoing continuations:
+ approve = false;
+ }
+ }
+ }
+ }
+
+}
+
+// XProgressHandler
+void BaseCommandEnv::push( uno::Any const & /*Status*/ )
+{
+}
+
+void BaseCommandEnv::update( uno::Any const & /*Status */)
+{
+}
+
+void BaseCommandEnv::pop()
+{
+}
+
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv()
+{
+}
+
+TmpRepositoryCommandEnv::TmpRepositoryCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void TmpRepositoryCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::VersionException verExc;
+ deployment::LicenseException licExc;
+ deployment::InstallException instExc;
+
+ bool approve = false;
+
+ if ((request >>= verExc)
+ || (request >>= licExc)
+ || (request >>= instExc))
+ {
+ approve = true;
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+LicenseCommandEnv::LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString repository):
+ BaseCommandEnv(handler), m_repository(std::move(repository)),
+ m_bSuppressLicense(bSuppressLicense)
+{
+}
+// XInteractionHandler
+void LicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ if (m_bSuppressLicense
+ || m_repository == "bundled"
+ || licExc.AcceptBy == "admin")
+ {
+ //always approve in bundled case, because we do not support
+ //showing licenses anyway.
+ //The "admin" already accepted the license when installing the
+ // shared extension
+ approve = true;
+ }
+ }
+
+ handle_(approve, xRequest);
+}
+
+
+NoLicenseCommandEnv::NoLicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler):
+ BaseCommandEnv(handler)
+{
+}
+// XInteractionHandler
+void NoLicenseCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+
+ bool approve = false;
+
+ if (request >>= licExc)
+ {
+ approve = true;
+ }
+ handle_(approve, xRequest);
+}
+
+SilentCheckPrerequisitesCommandEnv::SilentCheckPrerequisitesCommandEnv()
+{
+}
+
+void SilentCheckPrerequisitesCommandEnv::handle(
+ Reference< task::XInteractionRequest> const & xRequest )
+{
+ uno::Any request( xRequest->getRequest() );
+ OSL_ASSERT( request.getValueTypeClass() == uno::TypeClass_EXCEPTION );
+
+ deployment::LicenseException licExc;
+ deployment::PlatformException platformExc;
+ deployment::DependencyException depExc;
+
+ if (request >>= licExc)
+ {
+ handle_(true, xRequest); // approve = true
+ }
+ else if ((request >>= platformExc)
+ || (request >>= depExc))
+ {
+ m_Exception = request;
+ }
+ else
+ {
+ m_UnknownException = request;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_commandenvironments.hxx b/desktop/source/deployment/manager/dp_commandenvironments.hxx
new file mode 100644
index 000000000..6533d45b4
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_commandenvironments.hxx
@@ -0,0 +1,139 @@
+/* -*- 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/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/XInteractionRequest.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+
+
+namespace dp_manager {
+
+/**
+ This command environment is to be used when an extension is temporarily
+ stored in the "tmp" repository. It prevents all kind of user interaction.
+ */
+class BaseCommandEnv
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler,
+ css::ucb::XProgressHandler >
+{
+ css::uno::Reference< css::task::XInteractionHandler> m_forwardHandler;
+protected:
+ void handle_(bool approve,
+ css::uno::Reference< css::task::XInteractionRequest> const & xRequest );
+public:
+ virtual ~BaseCommandEnv() override;
+ BaseCommandEnv();
+ explicit BaseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler >
+ SAL_CALL getProgressHandler() override;
+
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+};
+
+class TmpRepositoryCommandEnv : public BaseCommandEnv
+{
+public:
+ TmpRepositoryCommandEnv();
+ explicit TmpRepositoryCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::synchronize.
+
+ It handles particular license cases.
+ */
+class LicenseCommandEnv : public BaseCommandEnv
+{
+private:
+ OUString m_repository;
+ bool m_bSuppressLicense;
+public:
+ LicenseCommandEnv(
+ css::uno::Reference< css::task::XInteractionHandler> const & handler,
+ bool bSuppressLicense,
+ OUString repository);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/** this class is for use in XPackageManager::checkPrerequisites
+
+ It always prohibits a license interaction
+ */
+class NoLicenseCommandEnv : public BaseCommandEnv
+{
+
+public:
+ explicit NoLicenseCommandEnv(css::uno::Reference< css::task::XInteractionHandler> const & handler);
+
+// XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+};
+
+/* For use in XExtensionManager::addExtension in the call to
+ XPackage::checkPrerequisites
+ It prevents all user interactions. The license is always accepted.
+ It remembers if there was a platform or a dependency exception in
+ the member m_bException. if there was any other exception then m_bUnknownException
+ is set.
+
+ */
+class SilentCheckPrerequisitesCommandEnv : public BaseCommandEnv
+{
+public:
+ SilentCheckPrerequisitesCommandEnv();
+ // XInteractionHandler
+ virtual void SAL_CALL handle(
+ css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
+
+ // Set to true if a PlatformException or a DependencyException were handled.
+ css::uno::Any m_Exception;
+ // Set to true if an unknown exception was handled.
+ css::uno::Any m_UnknownException;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.cxx b/desktop/source/deployment/manager/dp_extensionmanager.cxx
new file mode 100644
index 000000000..fc7816f6a
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.cxx
@@ -0,0 +1,1432 @@
+/* -*- 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/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/bootstrap.hxx>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/thePackageManagerFactory.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/InstallException.hpp>
+#include <com/sun/star/deployment/VersionException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/task/XInteractionApprove.hpp>
+#include <com/sun/star/beans/Ambiguous.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/util/XModifyBroadcaster.hpp>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <osl/diagnose.h>
+#include <dp_interact.h>
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include "dp_extensionmanager.hxx"
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+#include <set>
+#include <string_view>
+
+namespace lang = com::sun::star::lang;
+namespace task = com::sun::star::task;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+namespace beans = com::sun::star::beans;
+namespace util = com::sun::star::util;
+
+using ::com::sun::star::uno::Reference;
+
+namespace {
+
+struct CompIdentifiers
+{
+ bool operator() (std::vector<Reference<css::deployment::XPackage> > const & a,
+ std::vector<Reference<css::deployment::XPackage> > const & b)
+ {
+ return getName(a).compareTo(getName(b)) < 0;
+ }
+
+ static OUString getName(std::vector<Reference<css::deployment::XPackage> > const & a);
+};
+
+OUString CompIdentifiers::getName(std::vector<Reference<css::deployment::XPackage> > const & a)
+{
+ OSL_ASSERT(a.size() == 3);
+ //get the first non-null reference
+ Reference<css::deployment::XPackage> extension;
+ for (auto const& elem : a)
+ {
+ if (elem.is())
+ {
+ extension = elem;
+ break;
+ }
+ }
+ OSL_ASSERT(extension.is());
+ return extension->getDisplayName();
+}
+
+void writeLastModified(OUString & url, Reference<ucb::XCommandEnvironment> const & xCmdEnv, Reference< uno::XComponentContext > const & xContext)
+{
+ //Write the lastmodified file
+ try {
+ ::rtl::Bootstrap::expandMacros(url);
+ ::ucbhelper::Content ucbStamp(url, xCmdEnv, xContext);
+ dp_misc::erase_path( url, xCmdEnv );
+ OString stamp("1" );
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ ucbStamp.writeStream( xData, true /* replace existing */ );
+ }
+ catch(...)
+ {
+ uno::Any exc(::cppu::getCaughtException());
+ throw css::deployment::DeploymentException("Failed to update" + url, nullptr, exc);
+ }
+}
+
+class ExtensionRemoveGuard
+{
+ css::uno::Reference<css::deployment::XPackage> m_extension;
+ css::uno::Reference<css::deployment::XPackageManager> m_xPackageManager;
+
+public:
+ ExtensionRemoveGuard(){};
+ ExtensionRemoveGuard(
+ css::uno::Reference<css::deployment::XPackage> extension,
+ css::uno::Reference<css::deployment::XPackageManager> xPackageManager):
+ m_extension(std::move(extension)), m_xPackageManager(std::move(xPackageManager)) {}
+ ~ExtensionRemoveGuard();
+
+ void set(css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager) {
+ m_extension = extension;
+ m_xPackageManager = xPackageManager;
+ }
+};
+
+ExtensionRemoveGuard::~ExtensionRemoveGuard()
+{
+ try {
+ OSL_ASSERT(!(m_extension.is() && !m_xPackageManager.is()));
+ if (m_xPackageManager.is() && m_extension.is())
+ m_xPackageManager->removePackage(
+ dp_misc::getIdentifier(m_extension), OUString(),
+ css::uno::Reference<css::task::XAbortChannel>(),
+ css::uno::Reference<css::ucb::XCommandEnvironment>());
+ } catch (...) {
+ OSL_ASSERT(false);
+ }
+}
+
+}
+
+namespace dp_manager {
+
+//ToDo: bundled extension
+ExtensionManager::ExtensionManager( Reference< uno::XComponentContext > const& xContext) :
+ ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager, css::lang::XServiceInfo >(m_aMutex)
+ , m_xContext(xContext)
+{
+ m_xPackageManagerFactory = css::deployment::thePackageManagerFactory::get(m_xContext);
+ OSL_ASSERT(m_xPackageManagerFactory.is());
+
+ m_repositoryNames.emplace_back("user");
+ m_repositoryNames.emplace_back("shared");
+ m_repositoryNames.emplace_back("bundled");
+}
+
+ExtensionManager::~ExtensionManager()
+{
+}
+
+// XServiceInfo
+OUString ExtensionManager::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.ExtensionManager";
+}
+
+sal_Bool ExtensionManager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > ExtensionManager::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.ExtensionManager" };
+}
+
+Reference<css::deployment::XPackageManager> ExtensionManager::getUserRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("user");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getSharedRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("shared");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBundledRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bundled");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getTmpRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("tmp");
+}
+Reference<css::deployment::XPackageManager> ExtensionManager::getBakRepository()
+{
+ return m_xPackageManagerFactory->getPackageManager("bak");
+}
+
+Reference<task::XAbortChannel> ExtensionManager::createAbortChannel()
+{
+ return new dp_misc::AbortChannel;
+}
+
+css::uno::Reference<css::deployment::XPackageManager>
+ExtensionManager::getPackageManager(std::u16string_view repository)
+{
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == u"user")
+ xPackageManager = getUserRepository();
+ else if (repository == u"shared")
+ xPackageManager = getSharedRepository();
+ else if (repository == u"bundled")
+ xPackageManager = getBundledRepository();
+ else if (repository == u"tmp")
+ xPackageManager = getTmpRepository();
+ else if (repository == u"bak")
+ xPackageManager = getBakRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ return xPackageManager;
+}
+
+/*
+ Enters the XPackage objects into a map. They must be all from the
+ same repository. The value type of the map is a vector, where each vector
+ represents an extension with a particular identifier. The first member
+ represents the user extension, the second the shared extension and the
+ third the bundled extension.
+ */
+void ExtensionManager::addExtensionsToMap(
+ id2extensions & mapExt,
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ std::u16string_view repository)
+{
+ //Determine the index in the vector where these extensions are to be
+ //added.
+ int index = 0;
+ for (auto const& repositoryName : m_repositoryNames)
+ {
+ if (repositoryName == repository)
+ break;
+ ++index;
+ }
+
+ for (const Reference<css::deployment::XPackage>& xExtension : seqExt)
+ {
+ OUString id = dp_misc::getIdentifier(xExtension);
+ id2extensions::iterator ivec = mapExt.find(id);
+ if (ivec == mapExt.end())
+ {
+ std::vector<Reference<css::deployment::XPackage> > vec(3);
+ vec[index] = xExtension;
+ mapExt[id] = vec;
+ }
+ else
+ {
+ ivec->second[index] = xExtension;
+ }
+ }
+}
+
+/*
+ returns a list containing extensions with the same identifier from
+ all repositories (user, shared, bundled). If one repository does not
+ have this extension, then the list contains an empty Reference. The list
+ is ordered according to the priority of the repositories:
+ 1. user
+ 2. shared
+ 3. bundled
+
+ The number of elements is always three, unless the number of repository
+ changes.
+ */
+std::vector<Reference<css::deployment::XPackage> >
+ ExtensionManager::getExtensionsWithSameId(
+ OUString const & identifier, OUString const & fileName)
+
+{
+ std::vector<Reference<css::deployment::XPackage> > extensionList;
+ Reference<css::deployment::XPackageManager> lRepos[] = {
+ getUserRepository(), getSharedRepository(), getBundledRepository() };
+ for (int i(0); i != SAL_N_ELEMENTS(lRepos); ++i)
+ {
+ Reference<css::deployment::XPackage> xPackage;
+ try
+ {
+ xPackage = lRepos[i]->getDeployedPackage(
+ identifier, fileName, Reference<ucb::XCommandEnvironment>());
+ }
+ catch(const lang::IllegalArgumentException &)
+ {
+ // thrown if the extension does not exist in this repository
+ }
+ extensionList.push_back(xPackage);
+ }
+ OSL_ASSERT(extensionList.size() == 3);
+ return extensionList;
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> >
+ExtensionManager::getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & fileName,
+ Reference< ucb::XCommandEnvironment> const & /*xCmdEnv*/ )
+{
+ try
+ {
+ std::vector<Reference<css::deployment::XPackage> > listExtensions =
+ getExtensionsWithSameId(identifier, fileName);
+ bool bHasExtension = false;
+
+ //throw an IllegalArgumentException if there is no extension at all.
+ for (auto const& extension : listExtensions)
+ bHasExtension |= extension.is();
+ if (!bHasExtension)
+ throw lang::IllegalArgumentException(
+ "Could not find extension: " + identifier + ", " + fileName,
+ static_cast<cppu::OWeakObject*>(this), -1);
+
+ return comphelper::containerToSequence(listExtensions);
+ }
+ catch ( const css::deployment::DeploymentException & )
+ {
+ throw;
+ }
+ catch ( const ucb::CommandFailedException & )
+ {
+ throw;
+ }
+ catch (css::uno::RuntimeException &)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during getExtensionsWithSameIdentifier",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+bool ExtensionManager::isUserDisabled(
+ OUString const & identifier, OUString const & fileName)
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch ( const lang::IllegalArgumentException & ) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ return isUserDisabled( ::comphelper::containerToSequence(listExtensions) );
+}
+
+bool ExtensionManager::isUserDisabled(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExtSameId)
+{
+ OSL_ASSERT(seqExtSameId.getLength() == 3);
+ Reference<css::deployment::XPackage> const & userExtension = seqExtSameId[0];
+ if (userExtension.is())
+ {
+ beans::Optional<beans::Ambiguous<sal_Bool> > reg =
+ userExtension->isRegistered(Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ //If the value is ambiguous, then we assume that the extension
+ //is enabled, but something went wrong during enabling. We do not
+ //automatically disable user extensions.
+ if (reg.IsPresent &&
+ ! reg.Value.IsAmbiguous && ! reg.Value.Value)
+ return true;
+ }
+ return false;
+}
+
+/*
+ This method determines the active extension (XPackage.registerPackage) with a
+ particular identifier.
+
+ The parameter bUserDisabled determines if the user extension is disabled.
+
+ When the user repository contains an extension with the given identifier and
+ it is not disabled by the user, then it is always registered. Otherwise an
+ extension is only registered when there is no registered extension in one of
+ the repositories with a higher priority. That is, if the extension is from
+ the shared repository and an active extension with the same identifier is in
+ the user repository, then the extension is not registered. Similarly a
+ bundled extension is not registered if there is an active extension with the
+ same identifier in the shared or user repository.
+*/
+void ExtensionManager::activateExtension(
+ OUString const & identifier, OUString const & fileName,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector<Reference<css::deployment::XPackage> > listExtensions;
+ try {
+ listExtensions = getExtensionsWithSameId(identifier, fileName);
+ } catch (const lang::IllegalArgumentException &) {
+ }
+ OSL_ASSERT(listExtensions.size() == 3);
+
+ activateExtension(
+ ::comphelper::containerToSequence(listExtensions),
+ bUserDisabled, bStartup, xAbortChannel, xCmdEnv);
+
+ fireModified();
+}
+
+void ExtensionManager::activateExtension(
+ uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled,
+ bool bStartup,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ bool bActive = false;
+ sal_Int32 len = seqExt.getLength();
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ Reference<css::deployment::XPackage> const & aExt = seqExt[i];
+ if (aExt.is())
+ {
+ //get the registration value of the current iteration
+ beans::Optional<beans::Ambiguous<sal_Bool> > optReg =
+ aExt->isRegistered(xAbortChannel, xCmdEnv);
+ //If nothing can be registered then break
+ if (!optReg.IsPresent)
+ break;
+
+ //Check if this is a disabled user extension,
+ if (i == 0 && bUserDisabled)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ continue;
+ }
+
+ //If we have already determined an active extension then we must
+ //make sure to unregister all extensions with the same id in
+ //repositories with a lower priority
+ if (bActive)
+ {
+ aExt->revokePackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ else
+ {
+ //This is the first extension in the ordered list, which becomes
+ //the active extension
+ bActive = true;
+ //Register if not already done.
+ //reregister if the value is ambiguous, which indicates that
+ //something went wrong during last registration.
+ aExt->registerPackage(bStartup, xAbortChannel, xCmdEnv);
+ }
+ }
+ }
+}
+
+Reference<css::deployment::XPackage> ExtensionManager::backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ Reference<css::deployment::XPackageManager> const & xPackageManager,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xBackup;
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ Reference<css::deployment::XPackage> xOldExtension = xPackageManager->getDeployedPackage(
+ identifier, fileName, tmpCmdEnv);
+
+ if (xOldExtension.is())
+ {
+ xBackup = getTmpRepository()->addPackage(
+ xOldExtension->getURL(), uno::Sequence<beans::NamedValue>(),
+ OUString(), Reference<task::XAbortChannel>(), tmpCmdEnv);
+
+ OSL_ENSURE(xBackup.is(), "Failed to backup extension");
+ }
+ return xBackup;
+}
+
+//The supported package types are actually determined by the registry. However
+//creating a registry
+//(desktop/source/deployment/registry/dp_registry.cxx:PackageRegistryImpl) will
+//create all the backends, so that the registry can obtain from them the package
+//types. Creating the registry will also set up the registry folder containing
+//all the subfolders for the respective backends.
+//Because all repositories support the same backends, we can just delegate this
+//call to one of the repositories.
+uno::Sequence< Reference<css::deployment::XPackageTypeInfo> >
+ExtensionManager::getSupportedPackageTypes()
+{
+ return getUserRepository()->getSupportedPackageTypes();
+}
+//Do some necessary checks and user interaction. This function does not
+//acquire the extension manager mutex and that mutex must not be acquired
+//when this function is called. doChecksForAddExtension does synchronous
+//user interactions which may require acquiring the solar mutex.
+//Returns true if the extension can be installed.
+bool ExtensionManager::doChecksForAddExtension(
+ Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ uno::Sequence<beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<css::deployment::XPackage> & out_existingExtension )
+{
+ try
+ {
+ Reference<css::deployment::XPackage> xOldExtension;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ const OUString sDisplayName = xTmpExtension->getDisplayName();
+ const OUString sVersion = xTmpExtension->getVersion();
+
+ try
+ {
+ xOldExtension = xPackageMgr->getDeployedPackage(
+ sIdentifier, sFileName, xCmdEnv);
+ out_existingExtension = xOldExtension;
+ }
+ catch (const lang::IllegalArgumentException &)
+ {
+ }
+ bool bCanInstall = false;
+
+ //This part is not guarded against other threads removing, adding, disabling ...
+ //etc. the same extension.
+ //checkInstall is safe because it notifies the user if the extension is not yet
+ //installed in the same repository. Because addExtension has its own guard
+ //(m_addMutex), another thread cannot add the extension in the meantime.
+ //checkUpdate is called if the same extension exists in the same
+ //repository. The user is asked if they want to replace it. Another
+ //thread
+ //could already remove the extension. So asking the user was not
+ //necessary. No harm is done. The other thread may also ask the user
+ //if he wants to remove the extension. This depends on the
+ //XCommandEnvironment which it passes to removeExtension.
+ if (xOldExtension.is())
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkUpdate(sVersion, sDisplayName,xOldExtension, xCmdEnv);
+ }
+ else
+ {
+ //throws a CommandFailedException if the user cancels
+ //the action.
+ checkInstall(sDisplayName, xCmdEnv);
+ }
+ //Prevent showing the license if requested.
+ Reference<ucb::XCommandEnvironment> _xCmdEnv(xCmdEnv);
+ ExtensionProperties props(std::u16string_view(), properties, Reference<ucb::XCommandEnvironment>(), m_xContext);
+
+ dp_misc::DescriptionInfoset info(dp_misc::getDescriptionInfoset(xTmpExtension->getURL()));
+ const ::std::optional<dp_misc::SimpleLicenseAttributes> licenseAttributes =
+ info.getSimpleLicenseAttributes();
+
+ if (licenseAttributes && licenseAttributes->suppressIfRequired
+ && props.isSuppressedLicense())
+ _xCmdEnv.set(new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler()));
+
+ bCanInstall = xTmpExtension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, xOldExtension.is() || props.isExtensionUpdate()) == 0;
+
+ return bCanInstall;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: unexpected exception in doChecksForAddExtension",
+ static_cast<OWeakObject*>(this));
+ }
+}
+
+// Only add to shared and user repository
+Reference<css::deployment::XPackage> ExtensionManager::addExtension(
+ OUString const & url, uno::Sequence<beans::NamedValue> const & properties,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ Reference<css::deployment::XPackage> xNewExtension;
+ //Determine the repository to use
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+ //We must make sure that the xTmpExtension is not create twice, because this
+ //would remove the first one.
+ ::osl::MutexGuard addGuard(m_addMutex);
+
+ Reference<css::deployment::XPackageManager> xTmpRepository(getTmpRepository());
+ // make sure xTmpRepository is alive as long as xTmpExtension is; as
+ // the "tmp" manager is only held weakly by m_xPackageManagerFactory, it
+ // could otherwise be disposed early, which would in turn dispose
+ // xTmpExtension's PackageRegistryBackend behind its back
+ Reference<css::deployment::XPackage> xTmpExtension(
+ xTmpRepository->addPackage(
+ url, uno::Sequence<beans::NamedValue>(), OUString(), xAbortChannel,
+ new TmpRepositoryCommandEnv()));
+ if (!xTmpExtension.is()) {
+ throw css::deployment::DeploymentException(
+ ("Extension Manager: Failed to create temporary XPackage for url: "
+ + url),
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+
+ //Make sure the extension is removed from the tmp repository in case
+ //of an exception
+ ExtensionRemoveGuard tmpExtensionRemoveGuard(xTmpExtension, getTmpRepository());
+ ExtensionRemoveGuard bakExtensionRemoveGuard;
+ const OUString sIdentifier = dp_misc::getIdentifier(xTmpExtension);
+ const OUString sFileName = xTmpExtension->getName();
+ Reference<css::deployment::XPackage> xOldExtension;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+
+ uno::Any excOccurred2;
+ bool bCanInstall = doChecksForAddExtension(
+ xPackageManager,
+ properties,
+ xTmpExtension,
+ xAbortChannel,
+ xCmdEnv,
+ xOldExtension );
+
+ {
+ bool bUserDisabled = false;
+ // In this guarded section (getMutex) we must not use the argument xCmdEnv
+ // because it may bring up dialogs (XInteractionHandler::handle) this
+ // may potentially deadlock. See issue
+ // http://qa.openoffice.org/issues/show_bug.cgi?id=114933
+ // By not providing xCmdEnv the underlying APIs will throw an exception if
+ // the XInteractionRequest cannot be handled.
+ ::osl::MutexGuard guard(m_aMutex);
+
+ if (bCanInstall)
+ {
+ try
+ {
+ bUserDisabled = isUserDisabled(sIdentifier, sFileName);
+ if (xOldExtension.is())
+ {
+ try
+ {
+ xOldExtension->revokePackage(
+ false, xAbortChannel, Reference<ucb::XCommandEnvironment>());
+ //save the old user extension in case the user aborts
+ xExtensionBackup = getBakRepository()->importExtension(
+ xOldExtension, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ bakExtensionRemoveGuard.set(xExtensionBackup, getBakRepository());
+ }
+ catch (const lang::DisposedException &)
+ {
+ //Another thread might have removed the extension meanwhile
+ }
+ }
+ //check again dependencies but prevent user interaction,
+ //We can disregard the license, because the user must have already
+ //accepted it, when we called checkPrerequisites the first time
+ rtl::Reference<SilentCheckPrerequisitesCommandEnv> pSilentCommandEnv =
+ new SilentCheckPrerequisitesCommandEnv();
+
+ sal_Int32 failedPrereq = xTmpExtension->checkPrerequisites(
+ xAbortChannel, pSilentCommandEnv, true);
+ if (failedPrereq == 0)
+ {
+ xNewExtension = xPackageManager->addPackage(
+ url, properties, OUString(), xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+ //If we add a user extension and there is already one which was
+ //disabled by a user, then the newly installed one is enabled. If we
+ //add to another repository then the user extension remains
+ //disabled.
+ bool bUserDisabled2 = bUserDisabled;
+ if (repository == "user")
+ bUserDisabled2 = false;
+
+ // pass the two values via variables to workaround gcc-4.3.4 specific bug (bnc#655912)
+ OUString sNewExtensionIdentifier = dp_misc::getIdentifier(xNewExtension);
+ OUString sNewExtensionFileName = xNewExtension->getName();
+
+ activateExtension(
+ sNewExtensionIdentifier, sNewExtensionFileName,
+ bUserDisabled2, false, xAbortChannel,
+ Reference<ucb::XCommandEnvironment>());
+
+ // if reached this section,
+ // this means that either the licensedialog.ui didn't popup,
+ // or user accepted the license agreement. otherwise
+ // no need to call fireModified() because user declined
+ // the license agreement therefore no change made.
+ try
+ {
+ fireModified();
+
+ }catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const uno::Exception &) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: Exception on fireModified() "
+ "in the scope of 'if (failedPrereq == 0)'",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ } catch (...) {
+ throw uno::RuntimeException(
+ "Extension Manager: RuntimeException on fireModified() "
+ "in the scope of 'if (failedPrereq == 0)'",
+ static_cast<OWeakObject*>(this));
+ }
+ }
+ else
+ {
+ if (pSilentCommandEnv->m_Exception.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_Exception);
+ else if ( pSilentCommandEnv->m_UnknownException.hasValue())
+ ::cppu::throwException(pSilentCommandEnv->m_UnknownException);
+ else
+ throw css::deployment::DeploymentException (
+ "Extension Manager: exception during addExtension, ckeckPrerequisites failed",
+ static_cast<OWeakObject*>(this), uno::Any());
+ }
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred2 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred2 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during addExtension, url: "
+ + url, static_cast<OWeakObject*>(this), excOccurred2);
+ excOccurred2 <<= exc;
+ }
+ }
+
+ if (excOccurred2.hasValue())
+ {
+ //It does not matter what exception is thrown. We try to
+ //recover the original status.
+ //If the user aborted installation then a ucb::CommandAbortedException
+ //is thrown.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ Reference<ucb::XCommandEnvironment>());
+ }
+ activateExtension(
+ sIdentifier, sFileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred2);
+ }
+ } // leaving the guarded section (getMutex())
+
+ return xNewExtension;
+}
+
+void ExtensionManager::removeExtension(
+ OUString const & identifier, OUString const & fileName,
+ OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ uno::Any excOccurred1;
+ Reference<css::deployment::XPackage> xExtensionBackup;
+ Reference<css::deployment::XPackageManager> xPackageManager;
+ bool bUserDisabled = false;
+ ::osl::MutexGuard guard(m_aMutex);
+ try
+ {
+//Determine the repository to use
+ if (repository == "user")
+ xPackageManager = getUserRepository();
+ else if (repository == "shared")
+ xPackageManager = getSharedRepository();
+ else
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(identifier, fileName);
+ //Backup the extension, in case the user cancels the action
+ xExtensionBackup = backupExtension(
+ identifier, fileName, xPackageManager, xCmdEnv);
+
+ //revoke the extension if it is active
+ Reference<css::deployment::XPackage> xOldExtension =
+ xPackageManager->getDeployedPackage(
+ identifier, fileName, xCmdEnv);
+ xOldExtension->revokePackage(false, xAbortChannel, xCmdEnv);
+
+ xPackageManager->removePackage(
+ identifier, fileName, xAbortChannel, xCmdEnv);
+ activateExtension(identifier, fileName, bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred1 = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred1 = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during removeExtension",
+ static_cast<OWeakObject*>(this), excOccurred1);
+ excOccurred1 <<= exc;
+ }
+
+ if (excOccurred1.hasValue())
+ {
+ //User aborted installation, restore the previous situation.
+ //Use a private AbortChannel so the user cannot interrupt.
+ try
+ {
+ Reference<ucb::XCommandEnvironment> tmpCmdEnv(
+ new TmpRepositoryCommandEnv(xCmdEnv->getInteractionHandler()));
+ if (xExtensionBackup.is())
+ {
+ xPackageManager->importExtension(
+ xExtensionBackup, Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+ activateExtension(
+ identifier, fileName, bUserDisabled, false,
+ Reference<task::XAbortChannel>(),
+ tmpCmdEnv);
+
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+ fireModified();
+ }
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred1);
+ }
+
+ if (xExtensionBackup.is())
+ getTmpRepository()->removePackage(
+ dp_misc::getIdentifier(xExtensionBackup),
+ xExtensionBackup->getName(), xAbortChannel, xCmdEnv);
+}
+
+// Only enable extensions from shared and user repository
+void ExtensionManager::enableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ ::osl::MutexGuard guard(m_aMutex);
+ bool bUserDisabled = false;
+ uno::Any excOccurred;
+ try
+ {
+ if (!extension.is())
+ return;
+ OUString repository = extension->getRepositoryName();
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ bUserDisabled = isUserDisabled(dp_misc::getIdentifier(extension),
+ extension->getName());
+
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), false, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+sal_Int32 ExtensionManager::checkPrerequisitesAndEnable(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ ::osl::MutexGuard guard(m_aMutex);
+ sal_Int32 ret = 0;
+ Reference<css::deployment::XPackageManager> mgr =
+ getPackageManager(extension->getRepositoryName());
+ ret = mgr->checkPrerequisites(extension, xAbortChannel, xCmdEnv);
+ if (ret)
+ {
+ //There are some unfulfilled prerequisites, try to revoke
+ extension->revokePackage(false, xAbortChannel, xCmdEnv);
+ }
+ const OUString id(dp_misc::getIdentifier(extension));
+ activateExtension(id, extension->getName(),
+ isUserDisabled(id, extension->getName()), false,
+ xAbortChannel, xCmdEnv);
+ return ret;
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+void ExtensionManager::disableExtension(
+ Reference<css::deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ ::osl::MutexGuard guard(m_aMutex);
+ uno::Any excOccurred;
+ bool bUserDisabled = false;
+ try
+ {
+ if (!extension.is())
+ return;
+ const OUString repository( extension->getRepositoryName());
+ if (repository != "user")
+ throw lang::IllegalArgumentException(
+ "No valid repository name provided.",
+ static_cast<cppu::OWeakObject*>(this), 0);
+
+ const OUString id(dp_misc::getIdentifier(extension));
+ bUserDisabled = isUserDisabled(id, extension->getName());
+
+ activateExtension(id, extension->getName(), true, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch ( const css::deployment::DeploymentException& ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandFailedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch ( const ucb::CommandAbortedException & ) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const lang::IllegalArgumentException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (const uno::RuntimeException &) {
+ excOccurred = ::cppu::getCaughtException();
+ } catch (...) {
+ excOccurred = ::cppu::getCaughtException();
+ css::deployment::DeploymentException exc(
+ "Extension Manager: exception during disableExtension",
+ static_cast<OWeakObject*>(this), excOccurred);
+ excOccurred <<= exc;
+ }
+
+ if (!excOccurred.hasValue())
+ return;
+
+ try
+ {
+ activateExtension(dp_misc::getIdentifier(extension),
+ extension->getName(), bUserDisabled, false,
+ xAbortChannel, xCmdEnv);
+ }
+ catch (...)
+ {
+ }
+ ::cppu::throwException(excOccurred);
+}
+
+uno::Sequence< Reference<css::deployment::XPackage> >
+ ExtensionManager::getDeployedExtensions(
+ OUString const & repository,
+ Reference<task::XAbortChannel> const &xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackages(
+ xAbort, xCmdEnv);
+}
+
+Reference<css::deployment::XPackage>
+ ExtensionManager::getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ return getPackageManager(repository)->getDeployedPackage(
+ identifier, filename, xCmdEnv);
+}
+
+uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > >
+ ExtensionManager::getAllExtensions(
+ Reference<task::XAbortChannel> const & xAbort,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ id2extensions mapExt;
+
+ uno::Sequence<Reference<css::deployment::XPackage> > userExt =
+ getUserRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, userExt, u"user");
+ uno::Sequence<Reference<css::deployment::XPackage> > sharedExt =
+ getSharedRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, sharedExt, u"shared");
+ uno::Sequence<Reference<css::deployment::XPackage> > bundledExt =
+ getBundledRepository()->getDeployedPackages(xAbort, xCmdEnv);
+ addExtensionsToMap(mapExt, bundledExt, u"bundled");
+
+ // Create the tmp repository to trigger its clean up (deletion
+ // of old temporary data.)
+ getTmpRepository();
+
+ //copy the values of the map to a vector for sorting
+ std::vector< std::vector<Reference<css::deployment::XPackage> > >
+ vecExtensions;
+ for (auto const& elem : mapExt)
+ vecExtensions.push_back(elem.second);
+
+ //sort the element according to the identifier
+ std::sort(vecExtensions.begin(), vecExtensions.end(), CompIdentifiers());
+
+ sal_Int32 j = 0;
+ uno::Sequence< uno::Sequence<Reference<css::deployment::XPackage> > > seqSeq(vecExtensions.size());
+ auto seqSeqRange = asNonConstRange(seqSeq);
+ for (auto const& elem : vecExtensions)
+ {
+ seqSeqRange[j++] = comphelper::containerToSequence(elem);
+ }
+ return seqSeq;
+
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Only to be called from unopkg or soffice bootstrap (with force=true in the
+// latter case):
+void ExtensionManager::reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+
+ std::set< OUString > disabledExts;
+ {
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ beans::Optional< beans::Ambiguous< sal_Bool > > registered(
+ package->isRegistered(xAbortChannel, xCmdEnv));
+ if (registered.IsPresent &&
+ !(registered.Value.IsAmbiguous ||
+ registered.Value.Value))
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ OSL_ASSERT(!id.isEmpty());
+ disabledExts.insert(id);
+ }
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ }
+
+ ::osl::MutexGuard guard(m_aMutex);
+ xPackageManager->reinstallDeployedPackages(
+ force, xAbortChannel, xCmdEnv);
+ //We must sync here, otherwise we will get exceptions when extensions
+ //are removed.
+ dp_misc::syncRepositories(force, xCmdEnv);
+ const uno::Sequence< Reference<css::deployment::XPackage> > extensions(
+ xPackageManager->getDeployedPackages(xAbortChannel, xCmdEnv));
+
+ for ( const Reference<css::deployment::XPackage>& package : extensions )
+ {
+ try
+ {
+ const OUString id = dp_misc::getIdentifier(package);
+ const OUString fileName = package->getName();
+ OSL_ASSERT(!id.isEmpty());
+ activateExtension(
+ id, fileName, disabledExts.find(id) != disabledExts.end(),
+ true, xAbortChannel, xCmdEnv );
+ }
+ catch (const lang::DisposedException &)
+ {
+ }
+ }
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception during enableExtension",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+sal_Bool ExtensionManager::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ ::osl::MutexGuard guard(m_aMutex);
+ OUString sSynchronizingShared(StrSyncRepository());
+ sSynchronizingShared = sSynchronizingShared.replaceAll("%NAME", "shared");
+ dp_misc::ProgressLevel progressShared(xCmdEnv, sSynchronizingShared);
+ bool bModified = getSharedRepository()->synchronize(xAbortChannel, xCmdEnv);
+ progressShared.update("\n\n");
+
+ OUString sSynchronizingBundled(StrSyncRepository());
+ sSynchronizingBundled = sSynchronizingBundled.replaceAll("%NAME", "bundled");
+ dp_misc::ProgressLevel progressBundled(xCmdEnv, sSynchronizingBundled);
+ bModified |= static_cast<bool>(getBundledRepository()->synchronize(xAbortChannel, xCmdEnv));
+ progressBundled.update("\n\n");
+
+ //Always determine the active extension.
+ //TODO: Is this still necessary? (It used to be necessary for the
+ // first-start optimization: The setup created the registration data
+ // for the bundled extensions (share/prereg/bundled) which was copied to
+ // the user installation when a user started OOo for the first time
+ // after running setup. All bundled extensions were registered at that
+ // moment. However, extensions with the same identifier could be in the
+ // shared or user repository, in which case the respective bundled
+ // extensions had to be revoked.)
+ try
+ {
+ const uno::Sequence<uno::Sequence<Reference<css::deployment::XPackage> > >
+ seqSeqExt = getAllExtensions(xAbortChannel, xCmdEnv);
+ for (uno::Sequence<Reference<css::deployment::XPackage> > const & seqExt : seqSeqExt)
+ {
+ activateExtension(seqExt, isUserDisabled(seqExt), true,
+ xAbortChannel, xCmdEnv);
+ }
+ }
+ catch (...)
+ {
+ //We catch the exception, so we can write the lastmodified file
+ //so we will no repeat this every time OOo starts.
+ OSL_FAIL("Extensions Manager: synchronize");
+ }
+ OUString lastSyncBundled("$BUNDLED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncBundled, xCmdEnv, m_xContext);
+ OUString lastSyncShared("$SHARED_EXTENSIONS_USER/lastsynchronized");
+ writeLastModified(lastSyncShared, xCmdEnv, m_xContext);
+ return bModified;
+ } catch ( const css::deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any exc = ::cppu::getCaughtException();
+ throw css::deployment::DeploymentException(
+ "Extension Manager: exception in synchronize",
+ static_cast<OWeakObject*>(this), exc);
+ }
+}
+
+// Notify the user when a new extension is to be installed. This is only the
+// case when one uses the system integration to install an extension (double
+// clicking on .oxt file etc.)). The function must only be called if there is no
+// extension with the same identifier already deployed. Then the checkUpdate
+// function will inform the user that the extension is about to be installed In
+// case the user cancels the installation a CommandFailed exception is
+// thrown.
+void ExtensionManager::checkInstall(
+ OUString const & displayName,
+ Reference<ucb::XCommandEnvironment> const & cmdEnv)
+{
+ uno::Any request(
+ css::deployment::InstallException(
+ "Extension " + displayName +
+ " is about to be installed.",
+ static_cast<OWeakObject *>(this), displayName));
+ bool approve = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ cmdEnv, &approve, &abort ))
+ {
+ OSL_ASSERT( !approve && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !approve)
+ throw ucb::CommandFailedException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + displayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+/* The function will make the user interaction in case there is an extension
+installed with the same id. This function may only be called if there is already
+an extension.
+*/
+void ExtensionManager::checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ Reference<css::deployment::XPackage> const & oldExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ // package already deployed, interact --force:
+ uno::Any request(
+ (css::deployment::VersionException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED ) + newDisplayName,
+ static_cast<OWeakObject *>(this), newVersion, newDisplayName,
+ oldExtension ) ) );
+ bool replace = false, abort = false;
+ if (! dp_misc::interactContinuation(
+ request, cppu::UnoType<task::XInteractionApprove>::get(),
+ xCmdEnv, &replace, &abort )) {
+ OSL_ASSERT( !replace && !abort );
+ throw css::deployment::DeploymentException(
+ DpResId(
+ RID_STR_ERROR_WHILE_ADDING) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+ }
+ if (abort || !replace)
+ throw ucb::CommandFailedException(
+ DpResId(
+ RID_STR_PACKAGE_ALREADY_ADDED) + newDisplayName,
+ static_cast<OWeakObject *>(this), request );
+}
+
+uno::Sequence<Reference<css::deployment::XPackage> > SAL_CALL
+ExtensionManager::getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ Reference<css::deployment::XPackageManager>
+ xPackageManager = getPackageManager(repository);
+ ::osl::MutexGuard guard(m_aMutex);
+ return xPackageManager->getExtensionsWithUnacceptedLicenses(xCmdEnv);
+}
+
+sal_Bool ExtensionManager::isReadOnlyRepository(OUString const & repository)
+{
+ return getPackageManager(repository)->isReadOnly();
+}
+
+
+// XModifyBroadcaster
+
+void ExtensionManager::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void ExtensionManager::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void ExtensionManager::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed) {
+ throw lang::DisposedException(
+ "ExtensionManager instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+void ExtensionManager::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+} // namespace dp_manager
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_ExtensionManager_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_manager::ExtensionManager(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_extensionmanager.hxx b/desktop/source/deployment/manager/dp_extensionmanager.hxx
new file mode 100644
index 000000000..0e08314c3
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_extensionmanager.hxx
@@ -0,0 +1,227 @@
+/* -*- 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 <strings.hrc>
+#include <dp_shared.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <com/sun/star/deployment/XExtensionManager.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <osl/mutex.hxx>
+#include <vector>
+#include <unordered_map>
+
+namespace dp_manager {
+
+typedef std::unordered_map<
+ OUString,
+ std::vector<css::uno::Reference<css::deployment::XPackage> > > id2extensions;
+
+class ExtensionManager : private cppu::BaseMutex,
+ public ::cppu::WeakComponentImplHelper< css::deployment::XExtensionManager, css::lang::XServiceInfo >
+{
+public:
+ explicit ExtensionManager( css::uno::Reference< css::uno::XComponentContext >const& xContext);
+ virtual ~ExtensionManager() override;
+
+ void check();
+ void fireModified();
+
+public:
+
+ // 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;
+
+// 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;
+
+//XExtensionManager
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addExtension(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removeExtension(
+ OUString const & identifier,
+ OUString const & filename,
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL enableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL disableExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisitesAndEnable(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedExtensions(
+ OUString const & repository,
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference< css::deployment::XPackage>
+ SAL_CALL getDeployedExtension(
+ OUString const & repository,
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getExtensionsWithSameIdentifier(
+ OUString const & identifier,
+ OUString const & filename,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence< css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > >
+ SAL_CALL getAllExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const &,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedExtensions(
+ sal_Bool force, OUString const & repository,
+ css::uno::Reference< css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference< css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ OUString const & repository,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Bool SAL_CALL isReadOnlyRepository(OUString const & repository) override;
+
+private:
+
+ static OUString StrSyncRepository() { return DpResId(RID_STR_SYNCHRONIZING_REPOSITORY); }
+
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ css::uno::Reference<css::deployment::XPackageManagerFactory> m_xPackageManagerFactory;
+
+ //only to be used within addExtension
+ ::osl::Mutex m_addMutex;
+ /* contains the names of all repositories (except tmp) in order of there
+ priority. That is, the first element is "user" followed by "shared" and
+ then "bundled"
+ */
+ std::vector< OUString > m_repositoryNames;
+
+ css::uno::Reference<css::deployment::XPackageManager> getUserRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getSharedRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBundledRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getTmpRepository();
+ css::uno::Reference<css::deployment::XPackageManager> getBakRepository();
+
+ bool isUserDisabled(OUString const & identifier,
+ OUString const & filename);
+
+ static bool isUserDisabled(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExtSameId);
+
+ void activateExtension(
+ OUString const & identifier,
+ OUString const & fileName,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ static void activateExtension(
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ bool bUserDisabled, bool bStartup,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+
+ std::vector<css::uno::Reference<css::deployment::XPackage> >
+ getExtensionsWithSameId(OUString const & identifier,
+ OUString const & fileName);
+
+ css::uno::Reference<css::deployment::XPackage> backupExtension(
+ OUString const & identifier, OUString const & fileName,
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageManager,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void checkInstall(
+ OUString const & displayName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & cmdEnv);
+
+ void checkUpdate(
+ OUString const & newVersion,
+ OUString const & newDisplayName,
+ css::uno::Reference<css::deployment::XPackage> const & oldExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ void addExtensionsToMap(
+ id2extensions & mapExt,
+ css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > const & seqExt,
+ std::u16string_view repository);
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ css::uno::Reference<css::deployment::XPackageManager>
+ getPackageManager(std::u16string_view repository);
+
+ /// @throws css::deployment::DeploymentException
+ /// @throws css::ucb::CommandFailedException
+ /// @throws css::ucb::CommandAbortedException
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::uno::RuntimeException
+ bool doChecksForAddExtension(
+ css::uno::Reference<css::deployment::XPackageManager> const & xPackageMgr,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ css::uno::Reference<css::deployment::XPackage> const & xTmpExtension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ css::uno::Reference<css::deployment::XPackage> & out_existingExtension );
+
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_informationprovider.cxx b/desktop/source/deployment/manager/dp_informationprovider.cxx
new file mode 100644
index 000000000..1e7beea34
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_informationprovider.cxx
@@ -0,0 +1,338 @@
+/* -*- 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/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <com/sun/star/deployment/UpdateInformationProvider.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/deployment/XPackageInformationProvider.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/deployment/XUpdateInformationProvider.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/task/XAbortChannel.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/xml/dom/XElement.hpp>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+#include <tools/diagnose_ex.h>
+#include <ucbhelper/content.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_identifier.hxx>
+#include <dp_version.hxx>
+#include <dp_update.hxx>
+
+namespace beans = com::sun::star::beans ;
+namespace deployment = com::sun::star::deployment ;
+namespace lang = com::sun::star::lang ;
+namespace task = com::sun::star::task ;
+namespace css_ucb = com::sun::star::ucb ;
+namespace uno = com::sun::star::uno ;
+namespace xml = com::sun::star::xml ;
+
+
+namespace dp_info {
+
+namespace {
+
+class PackageInformationProvider :
+ public ::cppu::WeakImplHelper< deployment::XPackageInformationProvider, lang::XServiceInfo >
+
+{
+ public:
+ explicit PackageInformationProvider( uno::Reference< uno::XComponentContext >const& xContext);
+
+ // 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;
+
+ // XPackageInformationProvider
+ virtual OUString SAL_CALL getPackageLocation( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL isUpdateAvailable( const OUString& extensionId ) override;
+ virtual uno::Sequence< uno::Sequence< OUString > > SAL_CALL getExtensionList() override;
+
+private:
+
+ uno::Reference< uno::XComponentContext> mxContext;
+
+ OUString getPackageLocation( const OUString& repository,
+ std::u16string_view _sExtensionId );
+
+ uno::Reference< deployment::XUpdateInformationProvider > mxUpdateInformation;
+};
+
+}
+
+PackageInformationProvider::PackageInformationProvider( uno::Reference< uno::XComponentContext > const& xContext) :
+ mxContext( xContext ),
+ mxUpdateInformation( deployment::UpdateInformationProvider::create( xContext ) )
+{
+}
+
+// XServiceInfo
+OUString PackageInformationProvider::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.PackageInformationProvider";
+}
+
+sal_Bool PackageInformationProvider::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > PackageInformationProvider::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.PackageInformationProvider" };
+}
+
+OUString PackageInformationProvider::getPackageLocation(
+ const OUString & repository,
+ std::u16string_view _rExtensionId )
+{
+ OUString aLocationURL;
+ uno::Reference<deployment::XExtensionManager> xManager =
+ deployment::ExtensionManager::get(mxContext);
+
+ if ( xManager.is() )
+ {
+ const uno::Sequence< uno::Reference< deployment::XPackage > > packages(
+ xManager->getDeployedExtensions(
+ repository,
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () ) );
+
+ for ( int pos = packages.getLength(); pos--; )
+ {
+ try
+ {
+ const beans::Optional< OUString > aID = packages[ pos ]->getIdentifier();
+ if ( aID.IsPresent && (aID.Value == _rExtensionId ) )
+ {
+ aLocationURL = packages[ pos ]->getURL();
+ break;
+ }
+ }
+ catch ( uno::RuntimeException & ) {}
+ }
+ }
+
+ return aLocationURL;
+}
+
+
+OUString SAL_CALL
+PackageInformationProvider::getPackageLocation( const OUString& _sExtensionId )
+{
+ OUString aLocationURL = getPackageLocation( "user", _sExtensionId );
+
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "shared", _sExtensionId );
+ }
+ if ( aLocationURL.isEmpty() )
+ {
+ aLocationURL = getPackageLocation( "bundled", _sExtensionId );
+ }
+ if ( !aLocationURL.isEmpty() )
+ {
+ try
+ {
+ ::ucbhelper::Content aContent( aLocationURL, nullptr, mxContext );
+ aLocationURL = aContent.getURL();
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ }
+ }
+ return aLocationURL;
+}
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL
+PackageInformationProvider::isUpdateAvailable( const OUString& _sExtensionId )
+{
+ uno::Sequence< uno::Sequence< OUString > > aList;
+
+ uno::Reference<deployment::XExtensionManager> extMgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!extMgr.is())
+ {
+ OSL_ASSERT(false);
+ return aList;
+ }
+ std::vector<std::pair<uno::Reference<deployment::XPackage>, uno::Any > > errors;
+ dp_misc::UpdateInfoMap updateInfoMap;
+ if (!_sExtensionId.isEmpty())
+ {
+ std::vector<uno::Reference<deployment::XPackage> > vecExtensions;
+ uno::Reference<deployment::XPackage> extension;
+ try
+ {
+ extension = dp_misc::getExtensionWithHighestVersion(
+ extMgr->getExtensionsWithSameIdentifier(
+ _sExtensionId, _sExtensionId, uno::Reference<css_ucb::XCommandEnvironment>()));
+ vecExtensions.push_back(extension);
+ }
+ catch (lang::IllegalArgumentException &)
+ {
+ OSL_ASSERT(false);
+ }
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, &vecExtensions, errors);
+ }
+ else
+ {
+ updateInfoMap = dp_misc::getOnlineUpdateInfos(
+ mxContext, extMgr, mxUpdateInformation, nullptr, errors);
+ }
+
+ int nCount = 0;
+ for (auto const& updateInfo : updateInfoMap)
+ {
+ dp_misc::UpdateInfo const & info = updateInfo.second;
+
+ OUString sOnlineVersion;
+ if (info.info.is())
+ {
+ // check, if there are unsatisfied dependencies and ignore this online update
+ dp_misc::DescriptionInfoset infoset(mxContext, info.info);
+ uno::Sequence< uno::Reference< xml::dom::XElement > >
+ ds( dp_misc::Dependencies::check( infoset ) );
+ if ( ! ds.hasElements() )
+ sOnlineVersion = info.version;
+ }
+
+ OUString sVersionUser;
+ OUString sVersionShared;
+ OUString sVersionBundled;
+ uno::Sequence< uno::Reference< deployment::XPackage> > extensions;
+ try {
+ extensions = extMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(info.extension), info.extension->getName(),
+ uno::Reference<css_ucb::XCommandEnvironment>());
+ } catch (const lang::IllegalArgumentException&) {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "ignoring");
+ continue;
+ }
+ OSL_ASSERT(extensions.getLength() == 3);
+ if (extensions[0].is() )
+ sVersionUser = extensions[0]->getVersion();
+ if (extensions[1].is() )
+ sVersionShared = extensions[1]->getVersion();
+ if (extensions[2].is() )
+ sVersionBundled = extensions[2]->getVersion();
+
+ bool bSharedReadOnly = extMgr->isReadOnlyRepository("shared");
+
+ dp_misc::UPDATE_SOURCE sourceUser = dp_misc::isUpdateUserExtension(
+ bSharedReadOnly, sVersionUser, sVersionShared, sVersionBundled, sOnlineVersion);
+ dp_misc::UPDATE_SOURCE sourceShared = dp_misc::isUpdateSharedExtension(
+ bSharedReadOnly, sVersionShared, sVersionBundled, sOnlineVersion);
+
+ OUString updateVersionUser;
+ OUString updateVersionShared;
+ if (sourceUser != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionUser = dp_misc::getHighestVersion(
+ sVersionShared, sVersionBundled, sOnlineVersion);
+ if (sourceShared != dp_misc::UPDATE_SOURCE_NONE)
+ updateVersionShared = dp_misc::getHighestVersion(
+ OUString(), sVersionBundled, sOnlineVersion);
+ OUString updateVersion;
+ if (dp_misc::compareVersions(updateVersionUser, updateVersionShared) == dp_misc::GREATER)
+ updateVersion = updateVersionUser;
+ else
+ updateVersion = updateVersionShared;
+ if (!updateVersion.isEmpty())
+ {
+
+ OUString aNewEntry[2];
+ aNewEntry[0] = updateInfo.first;
+ aNewEntry[1] = updateVersion;
+ aList.realloc( ++nCount );
+ aList.getArray()[ nCount-1 ] = ::uno::Sequence< OUString >( aNewEntry, 2 );
+ }
+ }
+ return aList;
+}
+
+
+uno::Sequence< uno::Sequence< OUString > > SAL_CALL PackageInformationProvider::getExtensionList()
+{
+ const uno::Reference<deployment::XExtensionManager> mgr =
+ deployment::ExtensionManager::get(mxContext);
+
+ if (!mgr.is())
+ return uno::Sequence< uno::Sequence< OUString > >();
+
+ const uno::Sequence< uno::Sequence< uno::Reference<deployment::XPackage > > >
+ allExt = mgr->getAllExtensions(
+ uno::Reference< task::XAbortChannel >(),
+ uno::Reference< css_ucb::XCommandEnvironment > () );
+
+ uno::Sequence< uno::Sequence< OUString > > retList;
+
+ sal_Int32 cAllIds = allExt.getLength();
+ retList.realloc(cAllIds);
+ auto pretList = retList.getArray();
+
+ for (sal_Int32 i = 0; i < cAllIds; i++)
+ {
+ //The inner sequence contains extensions with the same identifier from
+ //all the different repositories, that is user, share, bundled.
+ const uno::Sequence< uno::Reference< deployment::XPackage > > &
+ seqExtension = allExt[i];
+ sal_Int32 cExt = seqExtension.getLength();
+ OSL_ASSERT(cExt == 3);
+ for (sal_Int32 j = 0; j < cExt; j++)
+ {
+ //ToDo according to the old code the first found extension is used
+ //even if another one with the same id has a better version.
+ uno::Reference< deployment::XPackage > const & xExtension( seqExtension[j] );
+ if (xExtension.is())
+ {
+ pretList[i] = { dp_misc::getIdentifier(xExtension), xExtension->getVersion() };
+ break;
+ }
+ }
+ }
+ return retList;
+}
+
+
+} // namespace dp_info
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_PackageInformationProvider_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_info::PackageInformationProvider(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.cxx b/desktop/source/deployment/manager/dp_manager.cxx
new file mode 100644
index 000000000..68765a63f
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.cxx
@@ -0,0 +1,1600 @@
+/* -*- 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 <config_features.h>
+
+#include <dp_interact.h>
+#include <dp_misc.h>
+#include <dp_registry.hxx>
+#include <dp_shared.hxx>
+#include <strings.hrc>
+#include <dp_ucb.h>
+#include <dp_platform.hxx>
+#include "dp_manager.h"
+#include <dp_identifier.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/string.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/diagnose_ex.h>
+#include <osl/diagnose.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/logging.hxx>
+#include <comphelper/sequence.hxx>
+#include <utility>
+#include <xmlscript/xml_helper.hxx>
+#include <svl/inettype.hxx>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/beans/UnknownPropertyException.hpp>
+#include <com/sun/star/logging/LogLevel.hpp>
+#include <com/sun/star/logging/FileHandler.hpp>
+#include <com/sun/star/logging/SimpleTextFormatter.hpp>
+#include <com/sun/star/logging/XLogger.hpp>
+#include <com/sun/star/util/XUpdatable.hpp>
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/sdbc/XRow.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/XContentAccess.hpp>
+#include <com/sun/star/ucb/NameClash.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/deployment/InvalidRemovedParameterException.hpp>
+#include <com/sun/star/deployment/Prerequisites.hpp>
+#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
+#include <unotools/tempfile.hxx>
+
+#include <dp_descriptioninfoset.hxx>
+#include "dp_commandenvironments.hxx"
+#include "dp_properties.hxx"
+
+#include <vector>
+#include <algorithm>
+
+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::logging;
+
+
+namespace dp_manager {
+
+namespace {
+
+struct MatchTempDir
+{
+ OUString m_str;
+ explicit MatchTempDir( OUString str ) : m_str(std::move( str )) {}
+ bool operator () ( ActivePackages::Entries::value_type const & v ) const {
+ return v.second.temporaryName.equalsIgnoreAsciiCase( m_str );
+ }
+};
+
+OUString getExtensionFolder(OUString const & parentFolder,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext)
+{
+ ::ucbhelper::Content tempFolder( parentFolder, xCmdEnv, xContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder, ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ OUString title;
+ if (xResultSet->next())
+ {
+ title = Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(1 /* Title */ ) ;
+ }
+ return title;
+}
+}
+
+void PackageManagerImpl::initActivationLayer(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ if (m_activePackages.isEmpty())
+ {
+ OSL_ASSERT( m_registryCache.isEmpty() );
+ // documents temp activation:
+ m_activePackagesDB.reset( new ActivePackages );
+ ::ucbhelper::Content ucbContent;
+ if (create_ucb_content( &ucbContent, m_context, xCmdEnv,
+ false /* no throw */ ))
+ {
+ // scan for all entries in m_packagesDir:
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (ucbContent, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS ) );
+
+ while (xResultSet->next())
+ {
+ Reference<sdbc::XRow> xRow( xResultSet, UNO_QUERY_THROW );
+ OUString title( xRow->getString( 1 /* Title */ ) );
+ // xxx todo: remove workaround for tdoc
+ if ( title == "this_is_a_dummy_stream_just_there_as_a_workaround_for_a_temporary_limitation_of_the_storage_api_implementation" )
+ continue;
+ if ( title == "META-INF" )
+ continue;
+
+ ::ucbhelper::Content sourceContent(
+ Reference<XContentAccess>(
+ xResultSet, UNO_QUERY_THROW )->queryContent(),
+ xCmdEnv, m_xComponentContext );
+
+ OUString mediaType( detectMediaType( sourceContent,
+ false /* no throw */) );
+ if (!mediaType.isEmpty())
+ {
+ ActivePackages::Data dbData;
+ insertToActivationLayer(
+ Sequence<css::beans::NamedValue>(),mediaType, sourceContent,
+ title, &dbData );
+
+ insertToActivationLayerDB( title, dbData );
+ //TODO #i73136#: insertToActivationLayerDB needs id not
+ // title, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently
+ // does not work, anyway.
+ }
+ }
+ }
+ }
+ else
+ {
+ // user|share:
+ OSL_ASSERT( !m_activePackages.isEmpty() );
+ m_activePackages_expanded = expandUnoRcUrl( m_activePackages );
+ m_registrationData_expanded = expandUnoRcUrl(m_registrationData);
+ if (!m_readOnly)
+ create_folder( nullptr, m_activePackages_expanded, xCmdEnv);
+
+ OUString dbName;
+ if (m_context == "user")
+ dbName = m_activePackages_expanded + ".pmap";
+ else
+ {
+ // Create the extension data base in the user installation
+ create_folder( nullptr, m_registrationData_expanded, xCmdEnv);
+ dbName = m_registrationData_expanded + "/extensions.pmap";
+ }
+ // The data base can always be written because it is always in the user installation
+ m_activePackagesDB.reset( new ActivePackages( dbName ) );
+
+ if (! m_readOnly && m_context != "bundled")
+ {
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor (tempFolder,
+ ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ) );
+
+ // get all temp directories:
+ std::vector<OUString> tempEntries;
+ std::vector<OUString> removedEntries;
+ while (xResultSet->next())
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ if (title.endsWith("removed", &title))
+ {
+ //save the file name without the "removed" part
+ removedEntries.push_back(::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ tempEntries.push_back( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ }
+
+ bool bShared = (m_context == "shared");
+ for (const OUString & tempEntry : tempEntries)
+ {
+ const MatchTempDir match( tempEntry );
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+ const OUString url(
+ makeURL(m_activePackages_expanded, tempEntry ) );
+
+ //In case of shared extensions, new entries are regarded as
+ //added extensions if there is no xxx.tmpremoved file.
+ if (bShared)
+ {
+ if (std::find(removedEntries.begin(), removedEntries.end(), tempEntry) ==
+ removedEntries.end())
+ {
+ continue;
+ }
+ else
+ {
+ //Make sure only the same user removes the extension, who
+ //previously unregistered it. This is avoid races if multiple instances
+ //of OOo are running which all have write access to the shared installation.
+ //For example, a user removes the extension, but keeps OOo
+ //running. Parts of the extension may still be loaded and used by OOo.
+ //Therefore the extension is only deleted the next time the extension manager is
+ //run after restarting OOo. While OOo is still running, another user starts OOo
+ //which would deleted the extension files. If the same user starts another
+ //instance of OOo then the lock file will prevent this.
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ ucbhelper::Content remFileContent(
+ url + "removed", Reference<XCommandEnvironment>(), m_xComponentContext);
+ std::vector<sal_Int8> data = dp_misc::readFile(remFileContent);
+ OString osData(reinterpret_cast<const char*>(data.data()),
+ data.size());
+ OUString sData = OStringToOUString(
+ osData, RTL_TEXTENCODING_UTF8);
+ if (sData != aUserName)
+ continue;
+ }
+ }
+ // temp entry not needed anymore:
+ erase_path( url + "_",
+ Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //delete the xxx.tmpremoved file
+ erase_path(url + "removed",
+ Reference<XCommandEnvironment>(), false);
+ }
+ }
+ }
+ }
+}
+
+
+void PackageManagerImpl::initRegistryBackends()
+{
+ if (!m_registryCache.isEmpty())
+ create_folder( nullptr, m_registryCache,
+ Reference<XCommandEnvironment>(), false);
+ m_xRegistry.set( ::dp_registry::create(
+ m_context, m_registryCache,
+ m_xComponentContext ) );
+}
+
+namespace {
+
+osl::FileBase::RC createDirectory(OUString const & url) {
+ auto e = osl::Directory::create(url);
+ if (e != osl::FileBase::E_NOENT) {
+ return e;
+ }
+ INetURLObject o(url);
+ if (!o.removeSegment()) {
+ return osl::FileBase::E_INVAL; // anything but E_None/E_EXIST
+ }
+ e = createDirectory(o.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ if (e != osl::FileBase::E_None && e != osl::FileBase::E_EXIST) {
+ return e;
+ }
+ return osl::Directory::create(url);
+}
+
+bool isMacroURLReadOnly( const OUString &rMacro )
+{
+ OUString aDirURL( rMacro );
+ ::rtl::Bootstrap::expandMacros( aDirURL );
+
+ ::osl::FileBase::RC aErr = createDirectory( aDirURL );
+ if ( aErr == ::osl::FileBase::E_None )
+ return false; // it will be writeable
+ if ( aErr != ::osl::FileBase::E_EXIST )
+ return true; // some serious problem creating it
+
+ bool bError;
+ sal_uInt64 nWritten = 0;
+ OUString aFileURL( aDirURL + "/stamp.sys" );
+ ::osl::File aFile( aFileURL );
+
+ bError = aFile.open( osl_File_OpenFlag_Read |
+ osl_File_OpenFlag_Write |
+ osl_File_OpenFlag_Create ) != ::osl::FileBase::E_None;
+ if (!bError)
+ bError = aFile.write( "1", 1, nWritten ) != ::osl::FileBase::E_None;
+ if (aFile.close() != ::osl::FileBase::E_None)
+ bError = true;
+ if (osl::File::remove( aFileURL ) != ::osl::FileBase::E_None)
+ bError = true;
+
+ SAL_INFO(
+ "desktop.deployment",
+ "local url '" << rMacro << "' -> '" << aFileURL << "' "
+ << (bError ? "is" : "is not") << " readonly\n");
+ return bError;
+}
+
+}
+
+Reference<deployment::XPackageManager> PackageManagerImpl::create(
+ Reference<XComponentContext> const & xComponentContext,
+ OUString const & context )
+{
+ rtl::Reference<PackageManagerImpl> that = new PackageManagerImpl(
+ xComponentContext, context );
+
+ OUString logFile, stamp;
+ if ( context == "user" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE";
+ that->m_registryCache = "vnd.sun.star.expand:$UNO_USER_PACKAGES_CACHE/registry";
+ logFile = "$UNO_USER_PACKAGES_CACHE/log.txt";
+ //We use the extension .sys for the file because on Windows Vista a sys
+ //(as well as exe and dll) file
+ //will not be written in the VirtualStore. For example if the process has no
+ //admin right once cannot write to the %programfiles% folder. However, when
+ //virtualization is used, the file will be written into the VirtualStore and
+ //it appears as if one could write to %programfiles%. When we test for write
+ //access to the office/shared folder for shared extensions then this typically
+ //fails because a normal user typically cannot write to this folder. However,
+ //using virtualization it appears that he/she can. Then a shared extension can
+ //be installed but is only visible for the user (because the extension is in
+ //the virtual store).
+ stamp = "$UNO_USER_PACKAGES_CACHE";
+ }
+ else if ( context == "shared" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ that->m_registrationData = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$SHARED_EXTENSIONS_USER/registry";
+ logFile = "$SHARED_EXTENSIONS_USER/log.txt";
+#if !HAVE_FEATURE_READONLY_INSTALLSET
+ // The "shared" extensions are read-only when we have a
+ // read-only installset.
+ stamp = "$UNO_SHARED_PACKAGES_CACHE";
+#endif
+ }
+ else if ( context == "bundled" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS";
+ that->m_registrationData = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER";
+ that->m_registryCache = "vnd.sun.star.expand:$BUNDLED_EXTENSIONS_USER/registry";
+ logFile = "$BUNDLED_EXTENSIONS_USER/log.txt";
+ //No stamp file. We assume that bundled is always readonly. It must not be
+ //modified from ExtensionManager but only by the installer
+ }
+ else if ( context == "tmp" ) {
+ that->m_activePackages = "vnd.sun.star.expand:$TMP_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$TMP_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$TMP_EXTENSIONS/registry";
+ stamp = "$TMP_EXTENSIONS";
+ }
+ else if (context == "bak") {
+ that->m_activePackages = "vnd.sun.star.expand:$BAK_EXTENSIONS/extensions";
+ that->m_registrationData = "vnd.sun.star.expand:$BAK_EXTENSIONS";
+ that->m_registryCache = "vnd.sun.star.expand:$BAK_EXTENSIONS/registry";
+ stamp = "$BAK_EXTENSIONS";
+ }
+
+ else if (! context.match("vnd.sun.star.tdoc:/")) {
+ throw lang::IllegalArgumentException(
+ "invalid context given: " + context,
+ Reference<XInterface>(), static_cast<sal_Int16>(-1) );
+ }
+
+ Reference<XCommandEnvironment> xCmdEnv;
+
+ try {
+ // There is no stamp for the bundled folder:
+ if (!stamp.isEmpty())
+ that->m_readOnly = isMacroURLReadOnly( stamp );
+
+ if (!that->m_readOnly && !logFile.isEmpty())
+ {
+ // Initialize logger which will be used in ProgressLogImpl (created below)
+ rtl::Bootstrap::expandMacros(logFile);
+ comphelper::EventLogger logger(xComponentContext, "unopkg");
+ const Reference<XLogger> xLogger(logger.getLogger());
+ Reference<XLogFormatter> xLogFormatter(SimpleTextFormatter::create(xComponentContext));
+ Sequence < beans::NamedValue > aSeq2 { { "Formatter", Any(xLogFormatter) }, {"FileURL", Any(logFile)} };
+ Reference<XLogHandler> xFileHandler(css::logging::FileHandler::createWithSettings(xComponentContext, aSeq2));
+ xFileHandler->setLevel(LogLevel::WARNING);
+ xLogger->addLogHandler(xFileHandler);
+
+ that->m_xLogFile.set(
+ that->m_xComponentContext->getServiceManager()
+ ->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.deployment.ProgressLog",
+ Sequence<Any>(),
+ that->m_xComponentContext ),
+ UNO_QUERY_THROW );
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv, that->m_xLogFile ) );
+ }
+
+ that->initRegistryBackends();
+ that->initActivationLayer( xCmdEnv );
+
+ return that;
+
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception & e) {
+ Any exc( ::cppu::getCaughtException() );
+ throw lang::WrappedTargetRuntimeException(
+ ("[context=\"" + context + "\"] caught unexpected "
+ + exc.getValueType().getTypeName() + ": " + e.Message),
+ Reference<XInterface>(), exc );
+ }
+}
+
+
+PackageManagerImpl::~PackageManagerImpl()
+{
+}
+
+
+void PackageManagerImpl::fireModified()
+{
+ ::cppu::OInterfaceContainerHelper * pContainer = rBHelper.getContainer(
+ cppu::UnoType<util::XModifyListener>::get() );
+ if (pContainer != nullptr) {
+ pContainer->forEach<util::XModifyListener>(
+ [this] (uno::Reference<util::XModifyListener> const& xListener)
+ { return xListener->modified(lang::EventObject(static_cast<OWeakObject *>(this))); });
+ }
+}
+
+
+void PackageManagerImpl::disposing()
+{
+ try {
+// // xxx todo: guarding?
+// ::osl::MutexGuard guard( getMutex() );
+ try_dispose( m_xLogFile );
+ m_xLogFile.clear();
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ m_activePackagesDB.reset();
+ m_xComponentContext.clear();
+
+ t_pm_helper::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 );
+ }
+}
+
+// XComponent
+
+void PackageManagerImpl::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 PackageManagerImpl::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 PackageManagerImpl::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 );
+}
+
+// XPackageManager
+
+OUString PackageManagerImpl::getContext()
+{
+ check();
+ return m_context;
+}
+
+
+Sequence< Reference<deployment::XPackageTypeInfo> >
+PackageManagerImpl::getSupportedPackageTypes()
+{
+ OSL_ASSERT( m_xRegistry.is() );
+ return m_xRegistry->getSupportedPackageTypes();
+}
+
+
+Reference<task::XAbortChannel> PackageManagerImpl::createAbortChannel()
+{
+ check();
+ return new AbortChannel;
+}
+
+// XModifyBroadcaster
+
+void PackageManagerImpl::addModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.addListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void PackageManagerImpl::removeModifyListener(
+ Reference<util::XModifyListener> const & xListener )
+{
+ check();
+ rBHelper.removeListener( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+OUString PackageManagerImpl::detectMediaType(
+ ::ucbhelper::Content const & ucbContent_, bool throw_exc )
+{
+ ::ucbhelper::Content ucbContent(ucbContent_);
+ OUString url( ucbContent.getURL() );
+ OUString mediaType;
+ if (url.match( "vnd.sun.star.tdoc:" ) || url.match( "vnd.sun.star.pkg:" ))
+ {
+ try {
+ ucbContent.getPropertyValue( "MediaType" ) >>= mediaType;
+ }
+ catch (const beans::UnknownPropertyException &) {
+ }
+ OSL_ENSURE( !mediaType.isEmpty(), "### no media-type?!" );
+ }
+ if (mediaType.isEmpty())
+ {
+ try {
+ Reference<deployment::XPackage> xPackage(
+ m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), ucbContent.getCommandEnvironment() ) );
+ const Reference<deployment::XPackageTypeInfo> xPackageType(
+ xPackage->getPackageType() );
+ OSL_ASSERT( xPackageType.is() );
+ if (xPackageType.is())
+ mediaType = xPackageType->getMediaType();
+ }
+ catch (const lang::IllegalArgumentException &) {
+ if (throw_exc)
+ throw;
+ css::uno::Any ex( cppu::getCaughtException() );
+ SAL_WARN( "desktop", exceptionToString(ex) );
+ }
+ }
+ return mediaType;
+}
+
+
+OUString PackageManagerImpl::insertToActivationLayer(
+ Sequence<beans::NamedValue> const & properties,
+ OUString const & mediaType, ::ucbhelper::Content const & sourceContent_,
+ OUString const & title, ActivePackages::Data * dbData )
+{
+ ::ucbhelper::Content sourceContent(sourceContent_);
+ Reference<XCommandEnvironment> xCmdEnv(
+ sourceContent.getCommandEnvironment() );
+
+ OUString baseDir(m_activePackages_expanded);
+ ::utl::TempFile aTemp(&baseDir, false);
+ OUString tempEntry = aTemp.GetURL();
+ tempEntry = tempEntry.copy(tempEntry.lastIndexOf('/') + 1);
+ OUString destFolder = makeURL( m_activePackages, tempEntry) + "_";
+
+ // prepare activation folder:
+ ::ucbhelper::Content destFolderContent;
+ create_folder( &destFolderContent, destFolder, xCmdEnv );
+
+ // copy content into activation temp dir:
+ if (mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.package-bundle") ||
+ // xxx todo: more sophisticated parsing
+ mediaType.matchIgnoreAsciiCase("application/vnd.sun.star.legacy-package-bundle"))
+ {
+ // inflate content:
+ OUStringBuffer buf;
+ if (!sourceContent.isFolder())
+ {
+ buf.append( "vnd.sun.star.zip://" );
+ buf.append( ::rtl::Uri::encode( sourceContent.getURL(),
+ rtl_UriCharClassRegName,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ else
+ {
+ //Folder. No need to unzip, just copy
+ buf.append(sourceContent.getURL());
+ }
+ buf.append( '/' );
+ sourceContent = ::ucbhelper::Content(
+ buf.makeStringAndClear(), xCmdEnv, m_xComponentContext );
+ }
+ destFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ title, NameClash::OVERWRITE );
+
+
+ // write to DB:
+ //bundled extensions should only be added by the synchronizeAddedExtensions
+ //functions. Moreover, there is no "temporary folder" for bundled extensions.
+ OSL_ASSERT(!(m_context == "bundled"));
+ OUString sFolderUrl = makeURLAppendSysPathSegment(destFolderContent.getURL(), title);
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(sFolderUrl);
+ dbData->temporaryName = tempEntry;
+ dbData->fileName = title;
+ dbData->mediaType = mediaType;
+ dbData->version = info.getVersion();
+
+ //No write the properties file next to the extension
+ ExtensionProperties props(sFolderUrl, properties, xCmdEnv, m_xComponentContext);
+ props.write();
+ return destFolder;
+}
+
+
+void PackageManagerImpl::insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData )
+{
+ //access to the database must be guarded. See removePackage
+ const ::osl::MutexGuard guard( m_aMutex );
+ m_activePackagesDB->put( id, dbData );
+}
+
+
+/* The function returns true if there is an extension with the same id already
+ installed which needs to be uninstalled, before the new extension can be installed.
+*/
+bool PackageManagerImpl::isInstalled(
+ Reference<deployment::XPackage> const & package)
+{
+ OUString id(dp_misc::getIdentifier(package));
+ OUString fn(package->getName());
+ bool bInstalled = false;
+ if (m_activePackagesDB->has( id, fn ))
+ {
+ bInstalled = true;
+ }
+ return bInstalled;
+}
+
+// XPackageManager
+
+Reference<deployment::XPackage> PackageManagerImpl::importExtension(
+ Reference<deployment::XPackage> const & extension,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ return addPackage(extension->getURL(), Sequence<beans::NamedValue>(),
+ OUString(), xAbortChannel, xCmdEnv_);
+}
+
+/* The function adds an extension but does not register it!!!
+ It may not do any user interaction. This is done in XExtensionManager::addExtension
+*/
+Reference<deployment::XPackage> PackageManagerImpl::addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType_,
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (m_readOnly)
+ {
+ OUString message;
+ if (m_context == "shared")
+ message = "You need write permissions to install a shared extension!";
+ else
+ message = "You need write permissions to install this extension!";
+ throw deployment::DeploymentException(
+ message, static_cast<OWeakObject *>(this), Any() );
+ }
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ::ucbhelper::Content sourceContent;
+ (void)create_ucb_content( &sourceContent, url, xCmdEnv ); // throws exc
+ const OUString title( StrTitle::getTitle( sourceContent ) );
+ const OUString title_enc( ::rtl::Uri::encode(
+ title, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ OUString destFolder;
+
+ OUString mediaType(mediaType_);
+ if (mediaType.isEmpty())
+ mediaType = detectMediaType( sourceContent );
+
+ Reference<deployment::XPackage> xPackage;
+ // copy file:
+ progressUpdate(
+ DpResId(RID_STR_COPYING_PACKAGE) + title, xCmdEnv );
+ if (m_activePackages.isEmpty())
+ {
+ ::ucbhelper::Content docFolderContent;
+ create_folder( &docFolderContent, m_context, xCmdEnv );
+ // copy into document, first:
+ docFolderContent.transferContent(
+ sourceContent, ::ucbhelper::InsertOperation::Copy,
+ OUString(),
+ NameClash::ASK /* xxx todo: ASK not needed? */);
+ // set media-type:
+ ::ucbhelper::Content docContent(
+ makeURL( m_context, title_enc ), xCmdEnv, m_xComponentContext );
+ //TODO #i73136#: using title instead of id can lead to
+ // clashes, but the whole m_activePackages.getLength()==0
+ // case (i.e., document-relative deployment) currently does
+ // not work, anyway.
+ docContent.setPropertyValue("MediaType", Any(mediaType) );
+
+ // xxx todo: obsolete in the future
+ try {
+ docFolderContent.executeCommand( "flush", Any() );
+ }
+ catch (const UnsupportedCommandException &) {
+ }
+ }
+ ActivePackages::Data dbData;
+ destFolder = insertToActivationLayer(
+ properties, mediaType, sourceContent, title, &dbData );
+
+
+ // bind activation package:
+ //Because every shared/user extension will be unpacked in a folder,
+ //which was created with a unique name we will always have two different
+ //XPackage objects, even if the second extension is the same.
+ //Therefore bindPackage does not need a guard here.
+ xPackage = m_xRegistry->bindPackage(
+ makeURL( destFolder, title_enc ), mediaType, false, OUString(), xCmdEnv );
+
+ OSL_ASSERT( xPackage.is() );
+ if (xPackage.is())
+ {
+ bool install = false;
+ try
+ {
+ OUString const id = dp_misc::getIdentifier( xPackage );
+
+ ::osl::MutexGuard g(m_addMutex);
+ if (isInstalled(xPackage))
+ {
+ //Do not guard the complete function with the getMutex
+ removePackage(id, xPackage->getName(), xAbortChannel,
+ xCmdEnv);
+ }
+ install = true;
+ insertToActivationLayerDB(id, dbData);
+ }
+ catch (...)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ throw;
+ }
+ if (!install)
+ {
+ deletePackageFromCache( xPackage, destFolder );
+ }
+ //ToDo: We should notify only if the extension is registered
+ fireModified();
+ }
+ return xPackage;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_ADDING) + url,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+void PackageManagerImpl::deletePackageFromCache(
+ Reference<deployment::XPackage> const & xPackage,
+ OUString const & destFolder)
+{
+ try_dispose( xPackage );
+
+ //we remove the package from the uno cache
+ //no service from the package may be loaded at this time!!!
+ erase_path( destFolder, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+ //rm last character '_'
+ OUString url = destFolder.copy(0, destFolder.getLength() - 1);
+ erase_path( url, Reference<XCommandEnvironment>(),
+ false /* no throw: ignore errors */ );
+
+}
+
+void PackageManagerImpl::removePackage(
+ OUString const & id, OUString const & fileName,
+ Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ Reference<deployment::XPackage> xPackage;
+ {
+ const ::osl::MutexGuard guard(m_aMutex);
+ //Check if this extension exist and throw an IllegalArgumentException
+ //if it does not
+ //If the files of the extension are already removed, or there is a
+ //different extension at the same place, for example after updating the
+ //extension, then the returned object is that which uses the database data.
+ xPackage = getDeployedPackage_(id, fileName, xCmdEnv );
+
+
+ //Because the extension is only removed the next time the extension
+ //manager runs after restarting OOo, we need to indicate that a
+ //shared extension was "deleted". When a user starts OOo, then it
+ //will check if something changed in the shared repository. Based on
+ //the flag file it will then recognize, that the extension was
+ //deleted and can then update the extension database of the shared
+ //extensions in the user installation.
+ if ( xPackage.is() && !m_readOnly && !xPackage->isRemoved() && (m_context == "shared"))
+ {
+ ActivePackages::Data val;
+ m_activePackagesDB->get( & val, id, fileName);
+ OSL_ASSERT(!val.temporaryName.isEmpty());
+ OUString url(makeURL(m_activePackages_expanded,
+ val.temporaryName + "removed"));
+ ::ucbhelper::Content contentRemoved(url, xCmdEnv, m_xComponentContext);
+ OUString aUserName;
+ ::osl::Security aSecurity;
+ aSecurity.getUserName( aUserName );
+
+ OString stamp = OUStringToOString(aUserName, RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentRemoved.writeStream( xData, true /* replace existing */ );
+ }
+ m_activePackagesDB->erase( id, fileName ); // to be removed upon next start
+ //remove any cached data hold by the backend
+ m_xRegistry->packageRemoved(xPackage->getURL(), xPackage->getPackageType()->getMediaType());
+ }
+ try_dispose( xPackage );
+
+ fireModified();
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ DpResId(RID_STR_ERROR_WHILE_REMOVING) + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+OUString PackageManagerImpl::getDeployPath( ActivePackages::Data const & data )
+{
+ OUStringBuffer buf;
+ buf.append( data.temporaryName );
+ //The bundled extensions are not contained in an additional folder
+ //with a unique name. data.temporaryName contains already the
+ //UTF8 encoded folder name. See PackageManagerImpl::synchronize
+ if (m_context != "bundled")
+ {
+ buf.append( "_/" );
+ buf.append( ::rtl::Uri::encode( data.fileName, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 ) );
+ }
+ return makeURL( m_activePackages, buf.makeStringAndClear() );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ ActivePackages::Data val;
+ if (m_activePackagesDB->get( &val, id, fileName ))
+ {
+ return getDeployedPackage_( id, val, xCmdEnv );
+ }
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this), static_cast<sal_Int16>(-1) );
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage_(
+ std::u16string_view id, ActivePackages::Data const & data,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool ignoreAlienPlatforms )
+{
+ if (ignoreAlienPlatforms)
+ {
+ OUString type, subType;
+ INetContentTypeParameterList params;
+ if (INetContentTypes::parse( data.mediaType, type, subType, &params ))
+ {
+ auto const iter = params.find(OString("platform"));
+ if (iter != params.end() && !platform_fits(iter->second.m_sValue))
+ throw lang::IllegalArgumentException(
+ DpResId(RID_STR_NO_SUCH_PACKAGE) + id,
+ static_cast<OWeakObject *>(this),
+ static_cast<sal_Int16>(-1) );
+ }
+ }
+ Reference<deployment::XPackage> xExtension;
+ try
+ {
+ //Ignore extensions where XPackage::checkPrerequisites failed.
+ //They must not be usable for this user.
+ if (data.failedPrerequisites == "0")
+ {
+ xExtension = m_xRegistry->bindPackage(
+ getDeployPath( data ), data.mediaType, false, OUString(), xCmdEnv );
+ }
+ }
+ catch (const deployment::InvalidRemovedParameterException& e)
+ {
+ xExtension = e.Extension;
+ }
+ return xExtension;
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages_(
+ Reference<XCommandEnvironment> const & xCmdEnv )
+{
+ std::vector< Reference<deployment::XPackage> > packages;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ for (auto const& elem : id2temp)
+ {
+ if (elem.second.failedPrerequisites != "0")
+ continue;
+ try {
+ packages.push_back(
+ getDeployedPackage_(
+ elem.first, elem.second, xCmdEnv,
+ true /* xxx todo: think of GUI:
+ ignore other platforms than the current one */ ) );
+ }
+ catch (const lang::IllegalArgumentException &) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ catch (const deployment::DeploymentException&) {
+ // ignore
+ TOOLS_WARN_EXCEPTION( "desktop", "" );
+ }
+ }
+ return comphelper::containerToSequence(packages);
+}
+
+
+Reference<deployment::XPackage> PackageManagerImpl::getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( m_aMutex );
+ return getDeployedPackage_( id, fileName, xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while accessing deployed package: " + id,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+Sequence< Reference<deployment::XPackage> >
+PackageManagerImpl::getDeployedPackages(
+ Reference<task::XAbortChannel> const &,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ const ::osl::MutexGuard guard( m_aMutex );
+ return getDeployedPackages_( xCmdEnv );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ // ought never occur...
+ "error while getting all deployed packages: " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+//ToDo: the function must not call registerPackage, do this in
+//XExtensionManager.reinstallDeployedExtensions
+void PackageManagerImpl::reinstallDeployedPackages(
+ sal_Bool force, Reference<task::XAbortChannel> const & /*xAbortChannel*/,
+ Reference<XCommandEnvironment> const & xCmdEnv_ )
+{
+ check();
+ if (!force && office_is_running())
+ throw RuntimeException(
+ "You must close any running Office process before reinstalling packages!",
+ static_cast<OWeakObject *>(this) );
+
+ Reference<XCommandEnvironment> xCmdEnv;
+ if (m_xLogFile.is())
+ xCmdEnv.set( new CmdEnvWrapperImpl( xCmdEnv_, m_xLogFile ) );
+ else
+ xCmdEnv.set( xCmdEnv_ );
+
+ try {
+ ProgressLevel progress(
+ xCmdEnv, "Reinstalling all deployed packages..." );
+
+ try_dispose( m_xRegistry );
+ m_xRegistry.clear();
+ if (!m_registryCache.isEmpty())
+ erase_path( m_registryCache, xCmdEnv );
+ initRegistryBackends();
+ Reference<util::XUpdatable> xUpdatable( m_xRegistry, UNO_QUERY );
+ if (xUpdatable.is())
+ xUpdatable->update();
+
+ //registering is done by the ExtensionManager service.
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const CommandAbortedException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const deployment::DeploymentException & exc) {
+ logIntern( Any(exc) );
+ throw;
+ }
+ catch (const Exception &) {
+ Any exc( ::cppu::getCaughtException() );
+ logIntern( exc );
+ throw deployment::DeploymentException(
+ "Error while reinstalling all previously deployed packages of context " + m_context,
+ static_cast<OWeakObject *>(this), exc );
+ }
+}
+
+
+sal_Bool SAL_CALL PackageManagerImpl::isReadOnly( )
+{
+ return m_readOnly;
+}
+bool PackageManagerImpl::synchronizeRemovedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+
+ //find all which are in the extension data base but which
+ //are removed already.
+ OSL_ASSERT(!(m_context == "user"));
+ bool bModified = false;
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ try
+ {
+ //Get the URL to the extensions folder, first make the url for the
+ //shared repository including the temporary name
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ bool bRemoved = false;
+ //Check if the URL to the extension is still the same
+ ::ucbhelper::Content contentExtension;
+
+ if (!create_ucb_content(
+ &contentExtension, url,
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+
+ //The folder is in the extension database, but it can still be deleted.
+ //look for the xxx.tmpremoved file
+ //There can also be the case that a different extension was installed
+ //in a "temp" folder with name that is already used.
+ if (!bRemoved && bShared)
+ {
+ ::ucbhelper::Content contentRemoved;
+
+ if (create_ucb_content(
+ &contentRemoved,
+ m_activePackages_expanded + "/" +
+ elem.second.temporaryName + "removed",
+ Reference<XCommandEnvironment>(), false))
+ {
+ bRemoved = true;
+ }
+ }
+
+ if (!bRemoved)
+ {
+ //There may be another extensions at the same place
+ dp_misc::DescriptionInfoset infoset =
+ dp_misc::getDescriptionInfoset(url);
+ OSL_ENSURE(infoset.hasDescription() && infoset.getIdentifier(),
+ "Extension Manager: bundled and shared extensions "
+ "must have an identifier and a version");
+ if (infoset.hasDescription() &&
+ infoset.getIdentifier() &&
+ ( elem.first != *(infoset.getIdentifier())
+ || elem.second.version != infoset.getVersion()))
+ {
+ bRemoved = true;
+ }
+
+ }
+ if (bRemoved)
+ {
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, elem.second.mediaType, true, elem.first, xCmdEnv );
+ OSL_ASSERT(xPackage.is()); //Even if the files are removed, we must get the object.
+ xPackage->revokePackage(true, xAbortChannel, xCmdEnv);
+ removePackage(xPackage->getIdentifier().Value, xPackage->getName(),
+ xAbortChannel, xCmdEnv);
+ bModified = true;
+ }
+ }
+ catch( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+
+bool PackageManagerImpl::synchronizeAddedExtensions(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ bool bModified = false;
+ OSL_ASSERT(!(m_context == "user"));
+
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+ //check if the folder exist at all. The shared extension folder
+ //may not exist for a normal user.
+ bool bOk=true;
+ try
+ {
+ bOk = create_ucb_content(
+ nullptr, m_activePackages_expanded, Reference<css::ucb::XCommandEnvironment>(), false);
+ }
+ catch (const css::ucb::ContentCreationException&)
+ {
+ bOk = false;
+ }
+
+ if (!bOk)
+ return bModified;
+
+ ::ucbhelper::Content tempFolder( m_activePackages_expanded, xCmdEnv, m_xComponentContext );
+ Reference<sdbc::XResultSet> xResultSet(
+ StrTitle::createCursor( tempFolder,
+ ::ucbhelper::INCLUDE_FOLDERS_ONLY ) );
+
+ while (xResultSet->next())
+ {
+ try
+ {
+ OUString title(
+ Reference<sdbc::XRow>(
+ xResultSet, UNO_QUERY_THROW )->getString(
+ 1 /* Title */ ) );
+ //The temporary folders of user and shared have an '_' at then end.
+ //But the name in ActivePackages.temporaryName is saved without.
+ OUString title2 = title;
+ bool bShared = (m_context == "shared");
+ if (bShared)
+ {
+ OSL_ASSERT(title2.endsWith("_"));
+ title2 = title2.copy(0, title2.getLength() -1);
+ }
+ OUString titleEncoded = ::rtl::Uri::encode(
+ title2, rtl_UriCharClassPchar,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+
+ //It is sufficient to check for the folder name, because when the administrator
+ //installed the extension it was already checked if there is one with the
+ //same identifier.
+ const MatchTempDir match(titleEncoded);
+ if (std::none_of( id2temp.begin(), id2temp.end(), match ))
+ {
+
+ // The folder was not found in the data base, so it must be
+ // an added extension
+ OUString url(m_activePackages_expanded + "/" + titleEncoded);
+ OUString sExtFolder;
+ if (bShared) //that is, shared
+ {
+ //Check if the extension was not "deleted" already which is indicated
+ //by a xxx.tmpremoved file
+ ::ucbhelper::Content contentRemoved;
+ if (create_ucb_content(&contentRemoved, url + "removed",
+ Reference<XCommandEnvironment>(), false))
+ continue;
+ sExtFolder = getExtensionFolder(
+ m_activePackages_expanded + "/" + titleEncoded + "_",
+ xCmdEnv, m_xComponentContext);
+ url = makeURLAppendSysPathSegment(m_activePackages_expanded, title);
+ url = makeURLAppendSysPathSegment(url, sExtFolder);
+ }
+ Reference<deployment::XPackage> xPackage = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+ if (xPackage.is())
+ {
+ OUString id = dp_misc::getIdentifier( xPackage );
+
+ //Prepare the database entry
+ ActivePackages::Data dbData;
+
+ dbData.temporaryName = titleEncoded;
+ if (bShared)
+ dbData.fileName = sExtFolder;
+ else
+ dbData.fileName = title;
+ dbData.mediaType = xPackage->getPackageType()->getMediaType();
+ dbData.version = xPackage->getVersion();
+ SAL_WARN_IF(
+ dbData.version.isEmpty(), "desktop.deployment",
+ "bundled/shared extension " << id << " at <" << url
+ << "> has no explicit version");
+
+ //We provide a special command environment that will prevent
+ //showing a license if simple-license/@accept-by = "admin"
+ //It will also prevent showing the license for bundled extensions
+ //which is not supported.
+ OSL_ASSERT(!(m_context == "user"));
+
+ // shall the license be suppressed?
+ DescriptionInfoset info =
+ dp_misc::getDescriptionInfoset(url);
+ ::std::optional<dp_misc::SimpleLicenseAttributes>
+ attr = info.getSimpleLicenseAttributes();
+ ExtensionProperties props(url, xCmdEnv, m_xComponentContext);
+ bool bNoLicense = false;
+ if (attr && attr->suppressIfRequired && props.isSuppressedLicense())
+ bNoLicense = true;
+
+ Reference<ucb::XCommandEnvironment> licCmdEnv(
+ new LicenseCommandEnv(xCmdEnv->getInteractionHandler(),
+ bNoLicense, m_context));
+ sal_Int32 failedPrereq = xPackage->checkPrerequisites(
+ xAbortChannel, licCmdEnv, false);
+ //Remember that this failed. For example, the user
+ //could have declined the license. Then the next time the
+ //extension folder is investigated we do not want to
+ //try to install the extension again.
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ bModified = true;
+ }
+ }
+ }
+ catch (const uno::Exception &)
+ {
+ // Looks like exceptions being caught here is not an uncommon case.
+ TOOLS_WARN_EXCEPTION("desktop.deployment", "");
+ }
+ }
+ return bModified;
+}
+
+sal_Bool PackageManagerImpl::synchronize(
+ Reference<task::XAbortChannel> const & xAbortChannel,
+ Reference<css::ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ check();
+ bool bModified = false;
+ if (m_context == "user")
+ return bModified;
+ bModified |=
+ synchronizeRemovedExtensions(xAbortChannel, xCmdEnv);
+ bModified |= synchronizeAddedExtensions(xAbortChannel, xCmdEnv);
+
+ return bModified;
+}
+
+Sequence< Reference<deployment::XPackage> > PackageManagerImpl::getExtensionsWithUnacceptedLicenses(
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ std::vector<Reference<deployment::XPackage> > vec;
+
+ try
+ {
+ const ::osl::MutexGuard guard( m_aMutex );
+ // clean up activation layer, scan for zombie temp dirs:
+ ActivePackages::Entries id2temp( m_activePackagesDB->getEntries() );
+
+ bool bShared = (m_context == "shared");
+
+ for (auto const& elem : id2temp)
+ {
+ //Get the database entry
+ ActivePackages::Data const & dbData = elem.second;
+ sal_Int32 failedPrereq = dbData.failedPrerequisites.toInt32();
+ //If the installation failed for other reason then the license then we
+ //ignore it.
+ if (failedPrereq ^ deployment::Prerequisites::LICENSE)
+ continue;
+
+ //Prepare the URL to the extension
+ OUString url = makeURL(m_activePackages, elem.second.temporaryName);
+ if (bShared)
+ url = makeURLAppendSysPathSegment( url + "_", elem.second.fileName);
+
+ Reference<deployment::XPackage> p = m_xRegistry->bindPackage(
+ url, OUString(), false, OUString(), xCmdEnv );
+
+ if (p.is())
+ vec.push_back(p);
+
+ }
+ return ::comphelper::containerToSequence(vec);
+ }
+ catch (const deployment::DeploymentException &)
+ {
+ throw;
+ }
+ catch (const RuntimeException&)
+ {
+ throw;
+ }
+ catch (...)
+ {
+ Any exc = ::cppu::getCaughtException();
+ deployment::DeploymentException de(
+ "PackageManagerImpl::getExtensionsWithUnacceptedLicenses",
+ static_cast<OWeakObject*>(this), exc);
+ exc <<= de;
+ ::cppu::throwException(exc);
+ }
+
+ return ::comphelper::containerToSequence(vec);
+}
+
+sal_Int32 PackageManagerImpl::checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv )
+{
+ try
+ {
+ if (!extension.is())
+ return 0;
+ if (m_context != extension->getRepositoryName())
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: extension is not from this repository.",
+ nullptr, 0);
+
+ ActivePackages::Data dbData;
+ OUString id = dp_misc::getIdentifier(extension);
+ if (!m_activePackagesDB->get( &dbData, id, OUString()))
+ {
+ throw lang::IllegalArgumentException(
+ "PackageManagerImpl::checkPrerequisites: unknown extension",
+ nullptr, 0);
+
+ }
+ //If the license was already displayed, then do not show it again
+ Reference<ucb::XCommandEnvironment> _xCmdEnv = xCmdEnv;
+ sal_Int32 prereq = dbData.failedPrerequisites.toInt32();
+ if ( !(prereq & deployment::Prerequisites::LICENSE))
+ _xCmdEnv = new NoLicenseCommandEnv(xCmdEnv->getInteractionHandler());
+
+ sal_Int32 failedPrereq = extension->checkPrerequisites(
+ xAbortChannel, _xCmdEnv, false);
+ dbData.failedPrerequisites = OUString::number(failedPrereq);
+ insertToActivationLayerDB(id, dbData);
+ return 0;
+ }
+ catch ( const deployment::DeploymentException& ) {
+ throw;
+ } catch ( const ucb::CommandFailedException & ) {
+ throw;
+ } catch ( const ucb::CommandAbortedException & ) {
+ throw;
+ } catch (const lang::IllegalArgumentException &) {
+ throw;
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (...) {
+ uno::Any excOccurred = ::cppu::getCaughtException();
+ deployment::DeploymentException exc(
+ "PackageManagerImpl::checkPrerequisites: exception ",
+ static_cast<OWeakObject*>(this), excOccurred);
+ throw exc;
+ }
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::~CmdEnvWrapperImpl()
+{
+}
+
+
+PackageManagerImpl::CmdEnvWrapperImpl::CmdEnvWrapperImpl(
+ Reference<XCommandEnvironment> const & xUserCmdEnv,
+ Reference<XProgressHandler> const & xLogFile )
+ : m_xLogFile( xLogFile )
+{
+ if (xUserCmdEnv.is()) {
+ m_xUserProgress.set( xUserCmdEnv->getProgressHandler() );
+ m_xUserInteractionHandler.set( xUserCmdEnv->getInteractionHandler() );
+ }
+}
+
+// XCommandEnvironment
+
+Reference<task::XInteractionHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getInteractionHandler()
+{
+ return m_xUserInteractionHandler;
+}
+
+
+Reference<XProgressHandler>
+PackageManagerImpl::CmdEnvWrapperImpl::getProgressHandler()
+{
+ return this;
+}
+
+// XProgressHandler
+
+void PackageManagerImpl::CmdEnvWrapperImpl::push( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->push( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->push( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::update( Any const & Status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( Status );
+ if (m_xUserProgress.is())
+ m_xUserProgress->update( Status );
+}
+
+
+void PackageManagerImpl::CmdEnvWrapperImpl::pop()
+{
+ if (m_xLogFile.is())
+ m_xLogFile->pop();
+ if (m_xUserProgress.is())
+ m_xUserProgress->pop();
+}
+
+} // namespace dp_manager
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_manager.h b/desktop/source/deployment/manager/dp_manager.h
new file mode 100644
index 000000000..a2079da23
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_manager.h
@@ -0,0 +1,232 @@
+/* -*- 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_activepackages.hxx"
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <ucbhelper/content.hxx>
+#include <com/sun/star/deployment/XPackageRegistry.hpp>
+#include <com/sun/star/deployment/XPackageManager.hpp>
+#include <memory>
+#include <string_view>
+#include <utility>
+
+namespace dp_manager {
+
+typedef ::cppu::WeakComponentImplHelper<
+ css::deployment::XPackageManager > t_pm_helper;
+
+
+class PackageManagerImpl final : private cppu::BaseMutex, public t_pm_helper
+{
+ css::uno::Reference<css::uno::XComponentContext> m_xComponentContext;
+ OUString m_context;
+ OUString m_registrationData;
+ OUString m_registrationData_expanded;
+ OUString m_registryCache;
+ bool m_readOnly;
+
+ OUString m_activePackages;
+ OUString m_activePackages_expanded;
+ std::unique_ptr< ActivePackages > m_activePackagesDB;
+ //This mutex is only used for synchronization in addPackage
+ ::osl::Mutex m_addMutex;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ inline void logIntern( css::uno::Any const & status );
+ void fireModified();
+
+ css::uno::Reference<css::deployment::XPackageRegistry> m_xRegistry;
+
+ void initRegistryBackends();
+ void initActivationLayer(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ OUString detectMediaType(
+ ::ucbhelper::Content const & ucbContent, bool throw_exc = true );
+ OUString insertToActivationLayer(
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ ::ucbhelper::Content const & sourceContent,
+ OUString const & title, ActivePackages::Data * dbData );
+ void insertToActivationLayerDB(
+ OUString const & id, ActivePackages::Data const & dbData );
+
+ static void deletePackageFromCache(
+ css::uno::Reference<css::deployment::XPackage> const & xPackage,
+ OUString const & destFolder );
+
+ bool isInstalled(
+ css::uno::Reference<css::deployment::XPackage> const & package);
+
+ bool synchronizeRemovedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ bool synchronizeAddedExtensions(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv);
+
+ class CmdEnvWrapperImpl
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::ucb::XProgressHandler >
+ {
+ css::uno::Reference<css::ucb::XProgressHandler> m_xLogFile;
+ css::uno::Reference<css::ucb::XProgressHandler> m_xUserProgress;
+ css::uno::Reference<css::task::XInteractionHandler>
+ m_xUserInteractionHandler;
+
+ public:
+ virtual ~CmdEnvWrapperImpl() override;
+ CmdEnvWrapperImpl(
+ css::uno::Reference<css::ucb::XCommandEnvironment>
+ const & xUserCmdEnv,
+ css::uno::Reference<css::ucb::XProgressHandler> const & xLogFile );
+
+ // XCommandEnvironment
+ virtual css::uno::Reference<css::task::XInteractionHandler> SAL_CALL
+ getInteractionHandler() override;
+ virtual css::uno::Reference<css::ucb::XProgressHandler> SAL_CALL
+ getProgressHandler() override;
+
+ // XProgressHandler
+ virtual void SAL_CALL push( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL update( css::uno::Any const & Status ) override;
+ virtual void SAL_CALL pop() override;
+ };
+
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+ virtual ~PackageManagerImpl() override;
+ PackageManagerImpl(
+ css::uno::Reference<css::uno::XComponentContext> xComponentContext, OUString context )
+ : t_pm_helper( m_aMutex ),
+ m_xComponentContext(std::move( xComponentContext )),
+ m_context(std::move( context )),
+ m_readOnly( true )
+ {}
+
+public:
+ static css::uno::Reference<css::deployment::XPackageManager> create(
+ css::uno::Reference<css::uno::XComponentContext>
+ const & xComponentContext, OUString const & context );
+
+ // 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;
+
+ // XPackageManager
+ virtual OUString SAL_CALL getContext() override;
+ virtual css::uno::Sequence<
+ css::uno::Reference<css::deployment::XPackageTypeInfo> > SAL_CALL
+ getSupportedPackageTypes() override;
+
+ virtual css::uno::Reference<css::task::XAbortChannel> SAL_CALL
+ createAbortChannel() override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL addPackage(
+ OUString const & url,
+ css::uno::Sequence<css::beans::NamedValue> const & properties,
+ OUString const & mediaType,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL importExtension(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL removePackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ OUString getDeployPath( ActivePackages::Data const & data );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ css::uno::Reference<css::deployment::XPackage> getDeployedPackage_(
+ std::u16string_view id, ActivePackages::Data const & data,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv,
+ bool ignoreAlienPlatforms = false );
+ virtual css::uno::Reference<css::deployment::XPackage> SAL_CALL
+ getDeployedPackage(
+ OUString const & id, OUString const & fileName,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ getDeployedPackages_(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv );
+ virtual css::uno::Sequence< css::uno::Reference<css::deployment::XPackage> >
+ SAL_CALL getDeployedPackages(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual void SAL_CALL reinstallDeployedPackages(
+ sal_Bool force,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual ::sal_Bool SAL_CALL isReadOnly( ) override;
+
+ virtual ::sal_Bool SAL_CALL synchronize(
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+
+ virtual css::uno::Sequence<css::uno::Reference<css::deployment::XPackage> > SAL_CALL
+ getExtensionsWithUnacceptedLicenses(
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv) override;
+
+ virtual sal_Int32 SAL_CALL checkPrerequisites(
+ css::uno::Reference<css::deployment::XPackage> const & extension,
+ css::uno::Reference<css::task::XAbortChannel> const & xAbortChannel,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const & xCmdEnv ) override;
+ };
+
+
+inline void PackageManagerImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ throw css::lang::DisposedException(
+ "PackageManager instance has already been disposed!",
+ static_cast< ::cppu::OWeakObject * >(this) );
+}
+
+
+inline void PackageManagerImpl::logIntern( css::uno::Any const & status )
+{
+ if (m_xLogFile.is())
+ m_xLogFile->update( status );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_managerfac.cxx b/desktop/source/deployment/manager/dp_managerfac.cxx
new file mode 100644
index 000000000..79e0ea358
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_managerfac.cxx
@@ -0,0 +1,186 @@
+/* -*- 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_manager.h"
+#include <dp_misc.h>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/deployment/XPackageManagerFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <unordered_map>
+
+using namespace ::dp_misc;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_manager::factory {
+
+typedef ::cppu::WeakComponentImplHelper<
+ deployment::XPackageManagerFactory, lang::XServiceInfo > t_pmfac_helper;
+
+namespace {
+
+class PackageManagerFactoryImpl : private cppu::BaseMutex, public t_pmfac_helper
+{
+ Reference<XComponentContext> m_xComponentContext;
+
+ Reference<deployment::XPackageManager> m_xUserMgr;
+ Reference<deployment::XPackageManager> m_xSharedMgr;
+ Reference<deployment::XPackageManager> m_xBundledMgr;
+ Reference<deployment::XPackageManager> m_xTmpMgr;
+ Reference<deployment::XPackageManager> m_xBakMgr;
+ typedef std::unordered_map<
+ OUString, WeakReference<deployment::XPackageManager> > t_string2weakref;
+ t_string2weakref m_managers;
+
+protected:
+ inline void check();
+ virtual void SAL_CALL disposing() override;
+
+public:
+ explicit PackageManagerFactoryImpl(
+ 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;
+
+ // XPackageManagerFactory
+ virtual Reference<deployment::XPackageManager> SAL_CALL getPackageManager(
+ OUString const & context ) override;
+};
+
+}
+
+PackageManagerFactoryImpl::PackageManagerFactoryImpl(
+ Reference<XComponentContext> const & xComponentContext )
+ : t_pmfac_helper( m_aMutex ),
+ m_xComponentContext( xComponentContext )
+{
+}
+
+// XServiceInfo
+OUString PackageManagerFactoryImpl::getImplementationName()
+{
+ return "com.sun.star.comp.deployment.PackageManagerFactory";
+}
+
+sal_Bool PackageManagerFactoryImpl::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence< OUString > PackageManagerFactoryImpl::getSupportedServiceNames()
+{
+ // a private one:
+ return { "com.sun.star.comp.deployment.PackageManagerFactory" };
+}
+
+inline void PackageManagerFactoryImpl::check()
+{
+ ::osl::MutexGuard guard( m_aMutex );
+ if (rBHelper.bInDispose || rBHelper.bDisposed)
+ {
+ throw lang::DisposedException(
+ "PackageManagerFactory instance has already been disposed!",
+ static_cast<OWeakObject *>(this) );
+ }
+}
+
+
+void PackageManagerFactoryImpl::disposing()
+{
+ // dispose all managers:
+ ::osl::MutexGuard guard( m_aMutex );
+ for (auto const& elem : m_managers)
+ try_dispose( elem.second );
+ m_managers = t_string2weakref();
+ // the below are already disposed:
+ m_xUserMgr.clear();
+ m_xSharedMgr.clear();
+ m_xBundledMgr.clear();
+ m_xTmpMgr.clear();
+ m_xBakMgr.clear();
+}
+
+// XPackageManagerFactory
+
+Reference<deployment::XPackageManager>
+PackageManagerFactoryImpl::getPackageManager( OUString const & context )
+{
+ Reference< deployment::XPackageManager > xRet;
+ ::osl::ResettableMutexGuard guard( m_aMutex );
+ check();
+ t_string2weakref::const_iterator const iFind( m_managers.find( context ) );
+ if (iFind != m_managers.end()) {
+ xRet = iFind->second;
+ if (xRet.is())
+ return xRet;
+ }
+
+ guard.clear();
+ xRet.set( PackageManagerImpl::create( m_xComponentContext, context ) );
+ guard.reset();
+ std::pair< t_string2weakref::iterator, bool > insertion(
+ m_managers.emplace( context, xRet ) );
+ if (insertion.second)
+ {
+ OSL_ASSERT( insertion.first->second.get() == xRet );
+ // hold user, shared mgrs for whole process: live deployment
+ if ( context == "user" )
+ m_xUserMgr = xRet;
+ else if ( context == "shared" )
+ m_xSharedMgr = xRet;
+ else if ( context == "bundled" )
+ m_xBundledMgr = xRet;
+ else if ( context == "tmp" )
+ m_xTmpMgr = xRet;
+ else if ( context == "bak" )
+ m_xBakMgr = xRet;
+ }
+ else
+ {
+ Reference< deployment::XPackageManager > xAlreadyIn(
+ insertion.first->second );
+ if (xAlreadyIn.is())
+ {
+ guard.clear();
+ try_dispose( xRet );
+ xRet = xAlreadyIn;
+ }
+ else
+ {
+ insertion.first->second = xRet;
+ }
+ }
+ return xRet;
+}
+
+} // namespace dp_manager::factory
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_deployment_PackageManagerFactory_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new dp_manager::factory::PackageManagerFactoryImpl(context));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.cxx b/desktop/source/deployment/manager/dp_properties.cxx
new file mode 100644
index 000000000..b0d2245e8
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.cxx
@@ -0,0 +1,145 @@
+/* -*- 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/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <xmlscript/xml_helper.hxx>
+#include <ucbhelper/content.hxx>
+
+#include <dp_ucb.h>
+#include "dp_properties.hxx"
+
+namespace lang = com::sun::star::lang;
+namespace ucb = com::sun::star::ucb;
+namespace uno = com::sun::star::uno;
+
+
+using ::com::sun::star::uno::Reference;
+
+constexpr OUStringLiteral PROP_SUPPRESS_LICENSE = u"SUPPRESS_LICENSE";
+constexpr OUStringLiteral PROP_EXTENSION_UPDATE = u"EXTENSION_UPDATE";
+
+namespace dp_manager {
+
+//Reading the file
+ExtensionProperties::ExtensionProperties(
+ std::u16string_view urlExtension,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = OUString::Concat(urlExtension) + "properties";
+
+ std::vector< std::pair< OUString, OUString> > props;
+ if (! dp_misc::create_ucb_content(nullptr, m_propFileUrl, nullptr, false))
+ return;
+
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ dp_misc::readProperties(props, contentProps);
+
+ for (auto const& prop : props)
+ {
+ if (prop.first == PROP_SUPPRESS_LICENSE)
+ m_prop_suppress_license = prop.second;
+ }
+}
+
+//Writing the file
+ExtensionProperties::ExtensionProperties(
+ std::u16string_view urlExtension,
+ uno::Sequence<css::beans::NamedValue> const & properties,
+ Reference<ucb::XCommandEnvironment> const & xCmdEnv,
+ Reference<uno::XComponentContext> const & xContext) :
+ m_xCmdEnv(xCmdEnv), m_xContext(xContext)
+{
+ m_propFileUrl = OUString::Concat(urlExtension) + "properties";
+
+ for (css::beans::NamedValue const & v : properties)
+ {
+ if (v.Name == PROP_SUPPRESS_LICENSE)
+ {
+ m_prop_suppress_license = getPropertyValue(v);
+ }
+ else if (v.Name == PROP_EXTENSION_UPDATE)
+ {
+ m_prop_extension_update = getPropertyValue(v);
+ }
+ else
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: unknown property", nullptr, -1);
+ }
+ }
+}
+
+OUString ExtensionProperties::getPropertyValue(css::beans::NamedValue const & v)
+{
+ OUString value("0");
+ if (! (v.Value >>= value) )
+ {
+ throw lang::IllegalArgumentException(
+ "Extension Manager: wrong property value", nullptr, -1);
+ }
+ return value;
+}
+void ExtensionProperties::write()
+{
+ ::ucbhelper::Content contentProps(m_propFileUrl, m_xCmdEnv, m_xContext);
+ OUString buf;
+
+ if (m_prop_suppress_license)
+ {
+ buf = OUString::Concat(PROP_SUPPRESS_LICENSE) + "=" + *m_prop_suppress_license;
+ }
+
+ OString stamp = OUStringToOString(buf, RTL_TEXTENCODING_UTF8);
+ Reference<css::io::XInputStream> xData(
+ ::xmlscript::createInputStream(
+ reinterpret_cast<sal_Int8 const *>(stamp.getStr()),
+ stamp.getLength() ) );
+ contentProps.writeStream( xData, true /* replace existing */ );
+}
+
+bool ExtensionProperties::isSuppressedLicense() const
+{
+ bool ret = false;
+ if (m_prop_suppress_license)
+ {
+ if (*m_prop_suppress_license == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+bool ExtensionProperties::isExtensionUpdate() const
+{
+ bool ret = false;
+ if (m_prop_extension_update)
+ {
+ if (*m_prop_extension_update == "1")
+ ret = true;
+ }
+ return ret;
+}
+
+} // namespace dp_manager
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/manager/dp_properties.hxx b/desktop/source/deployment/manager/dp_properties.hxx
new file mode 100644
index 000000000..06139ece3
--- /dev/null
+++ b/desktop/source/deployment/manager/dp_properties.hxx
@@ -0,0 +1,58 @@
+/* -*- 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/beans/NamedValue.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <optional>
+#include <string_view>
+
+namespace dp_manager
+{
+class ExtensionProperties final
+{
+ OUString m_propFileUrl;
+ const css::uno::Reference<css::ucb::XCommandEnvironment> m_xCmdEnv;
+ const css::uno::Reference<css::uno::XComponentContext> m_xContext;
+ ::std::optional<OUString> m_prop_suppress_license;
+ ::std::optional<OUString> m_prop_extension_update;
+
+ static OUString getPropertyValue(css::beans::NamedValue const& v);
+
+public:
+ ExtensionProperties(std::u16string_view urlExtension,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const& xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+
+ ExtensionProperties(std::u16string_view urlExtension,
+ css::uno::Sequence<css::beans::NamedValue> const& properties,
+ css::uno::Reference<css::ucb::XCommandEnvironment> const& xCmdEnv,
+ css::uno::Reference<css::uno::XComponentContext> const& xContext);
+
+ void write();
+
+ bool isSuppressedLicense() const;
+
+ bool isExtensionUpdate() const;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */