summaryrefslogtreecommitdiffstats
path: root/desktop/source/deployment/misc
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/misc
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/misc')
-rw-r--r--desktop/source/deployment/misc/dp_dependencies.cxx196
-rw-r--r--desktop/source/deployment/misc/dp_descriptioninfoset.cxx809
-rw-r--r--desktop/source/deployment/misc/dp_identifier.cxx56
-rw-r--r--desktop/source/deployment/misc/dp_interact.cxx137
-rw-r--r--desktop/source/deployment/misc/dp_misc.cxx561
-rw-r--r--desktop/source/deployment/misc/dp_platform.cxx207
-rw-r--r--desktop/source/deployment/misc/dp_resource.cxx42
-rw-r--r--desktop/source/deployment/misc/dp_ucb.cxx304
-rw-r--r--desktop/source/deployment/misc/dp_update.cxx408
-rw-r--r--desktop/source/deployment/misc/dp_version.cxx63
-rw-r--r--desktop/source/deployment/misc/lockfile.cxx211
11 files changed, 2994 insertions, 0 deletions
diff --git a/desktop/source/deployment/misc/dp_dependencies.cxx b/desktop/source/deployment/misc/dp_dependencies.cxx
new file mode 100644
index 000000000..e187a6974
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_dependencies.cxx
@@ -0,0 +1,196 @@
+/* -*- 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_folders.h>
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/xml/dom/XElement.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <unotools/configmgr.hxx>
+
+#include <strings.hrc>
+#include <dp_shared.hxx>
+
+#include <dp_dependencies.hxx>
+#include <dp_descriptioninfoset.hxx>
+#include <dp_version.hxx>
+
+namespace {
+
+char const namespaceLibreOffice[] =
+ "http://libreoffice.org/extensions/description/2011";
+
+constexpr OUStringLiteral namespaceOpenOfficeOrg =
+ u"http://openoffice.org/extensions/description/2006";
+
+char const minimalVersionLibreOffice[] = "LibreOffice-minimal-version";
+char const maximalVersionLibreOffice[] = "LibreOffice-maximal-version";
+
+constexpr OUStringLiteral minimalVersionOpenOfficeOrg =
+ u"OpenOffice.org-minimal-version";
+
+char const maximalVersionOpenOfficeOrg[] =
+ "OpenOffice.org-maximal-version";
+
+OUString getLibreOfficeMajorMinorMicro() {
+ return utl::ConfigManager::getAboutBoxProductVersion();
+}
+
+OUString getReferenceOpenOfficeOrgMajorMinor() {
+#ifdef ANDROID
+ // just hardcode the version
+ OUString v("4.1");
+#else
+ OUString v(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ReferenceOOoMajorMinor}");
+ rtl::Bootstrap::expandMacros(v); //TODO: check for failure
+#endif
+ return v;
+}
+
+bool satisfiesMinimalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::LESS;
+}
+
+bool satisfiesMaximalVersion(
+ OUString const & actual, OUString const & specified)
+{
+ return dp_misc::compareVersions(actual, specified) != dp_misc::GREATER;
+}
+
+OUString produceErrorText(
+ OUString const & reason, OUString const & version)
+{
+ return reason.replaceFirst("%VERSION",
+ (version.isEmpty()
+ ? DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN)
+ : version));
+}
+
+}
+
+namespace dp_misc::Dependencies {
+
+css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+check(dp_misc::DescriptionInfoset const & infoset) {
+ css::uno::Reference< css::xml::dom::XNodeList > deps(
+ infoset.getDependencies());
+ sal_Int32 n = deps->getLength();
+ css::uno::Sequence< css::uno::Reference< css::xml::dom::XElement > >
+ unsatisfied(n);
+ auto unsatisfiedRange = asNonConstRange(unsatisfied);
+ sal_Int32 unsat = 0;
+ // check first if minimalVersionLibreOffice is specified -- in that case ignore the legacy OOo dependencies
+ bool bIgnoreOoo = false;
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ if ( e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice)
+ {
+ bIgnoreOoo = true;
+ break;
+ }
+ }
+ for (sal_Int32 i = 0; i < n; ++i) {
+ css::uno::Reference< css::xml::dom::XElement > e(
+ deps->item(i), css::uno::UNO_QUERY_THROW);
+ bool sat = false;
+ if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if ( e->getNamespaceURI() == namespaceOpenOfficeOrg && e->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ sat = bIgnoreOoo || satisfiesMaximalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == minimalVersionLibreOffice )
+ {
+ sat = satisfiesMinimalVersion(
+ getLibreOfficeMajorMinorMicro(),
+ e->getAttribute("value"));
+ } else if (e->getNamespaceURI() == namespaceLibreOffice && e->getTagName() == maximalVersionLibreOffice )
+ {
+ sat = satisfiesMaximalVersion(getLibreOfficeMajorMinorMicro(), e->getAttribute("value"));
+ } else if (e->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ sat = satisfiesMinimalVersion(
+ getReferenceOpenOfficeOrgMajorMinor(),
+ e->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ }
+ if (!sat) {
+ unsatisfiedRange[unsat++] = e;
+ }
+ }
+ unsatisfied.realloc(unsat);
+ return unsatisfied;
+}
+
+OUString getErrorText(
+ css::uno::Reference< css::xml::dom::XElement > const & dependency)
+{
+ OSL_ASSERT(dependency.is());
+ if ( dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == minimalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceOpenOfficeOrg && dependency->getTagName() == maximalVersionOpenOfficeOrg )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == minimalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MIN),
+ dependency->getAttribute("value"));
+ } else if (dependency->getNamespaceURI() == namespaceLibreOffice && dependency->getTagName() == maximalVersionLibreOffice )
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_LO_MAX),
+ dependency->getAttribute("value"));
+ } else if (dependency->hasAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg))
+ {
+ return produceErrorText(
+ DpResId(RID_DEPLOYMENT_DEPENDENCIES_OOO_MIN),
+ dependency->getAttributeNS(namespaceOpenOfficeOrg,
+ minimalVersionOpenOfficeOrg));
+ } else {
+ return DpResId(RID_DEPLOYMENT_DEPENDENCIES_UNKNOWN);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_descriptioninfoset.cxx b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
new file mode 100644
index 000000000..00b32c04f
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
@@ -0,0 +1,809 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <dp_descriptioninfoset.hxx>
+
+#include <dp_resource.h>
+
+#include <comphelper/sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <optional>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/deployment/DeploymentException.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/SequenceInputStream.hpp>
+#include <com/sun/star/lang/XMultiComponentFactory.hpp>
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XProgressHandler.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/xml/dom/DOMException.hpp>
+#include <com/sun/star/xml/dom/XNode.hpp>
+#include <com/sun/star/xml/dom/XNodeList.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/xpath/XPathAPI.hpp>
+#include <com/sun/star/xml/xpath/XPathException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <ucbhelper/content.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace {
+
+using css::uno::Reference;
+
+class EmptyNodeList:
+ public cppu::WeakImplHelper<css::xml::dom::XNodeList>
+{
+public:
+ EmptyNodeList();
+
+ EmptyNodeList(const EmptyNodeList&) = delete;
+ const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
+
+ virtual ::sal_Int32 SAL_CALL getLength() override;
+
+ virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
+ item(::sal_Int32 index) override;
+};
+
+EmptyNodeList::EmptyNodeList() {}
+
+::sal_Int32 EmptyNodeList::getLength() {
+ return 0;
+}
+
+css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
+{
+ throw css::uno::RuntimeException("bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call",
+ static_cast< ::cppu::OWeakObject * >(this));
+}
+
+OUString getNodeValue(
+ css::uno::Reference< css::xml::dom::XNode > const & node)
+{
+ OSL_ASSERT(node.is());
+ try {
+ return node->getNodeValue();
+ } catch (const css::xml::dom::DOMException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.xml.dom.DOMException: " + e.Message,
+ nullptr, anyEx );
+ }
+}
+
+/**The class uses the UCB to access the description.xml file in an
+ extension. The UCB must have been initialized already. It also
+ requires that the extension has already be unzipped to a particular
+ location.
+ */
+class ExtensionDescription
+{
+public:
+ /**throws an exception if the description.xml is not
+ available, cannot be read, does not contain the expected data,
+ or any other error occurred. Therefore it should only be used with
+ new extensions.
+
+ Throws css::uno::RuntimeException,
+ css::deployment::DeploymentException,
+ dp_registry::backend::bundle::NoDescriptionException.
+ */
+ ExtensionDescription(
+ const css::uno::Reference<css::uno::XComponentContext>& xContext,
+ std::u16string_view installDir,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
+ {
+ return m_xRoot;
+ }
+
+private:
+ css::uno::Reference<css::xml::dom::XNode> m_xRoot;
+};
+
+class NoDescriptionException
+{
+};
+
+class FileDoesNotExistFilter
+ : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
+ css::task::XInteractionHandler >
+
+{
+ bool m_bExist;
+ css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
+
+public:
+ explicit FileDoesNotExistFilter(
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
+
+ bool exist() { return m_bExist;}
+ // 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;
+};
+
+ExtensionDescription::ExtensionDescription(
+ const Reference<css::uno::XComponentContext>& xContext,
+ std::u16string_view installDir,
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
+{
+ try {
+ //may throw css::ucb::ContentCreationException
+ //If there is no description.xml then ucb will start an interaction which
+ //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
+ //and filter the respective exception out.
+ OUString sDescriptionUri(OUString::Concat(installDir) + "/description.xml");
+ Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
+ ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
+
+ //throws a css::uno::Exception if the file is not available
+ Reference<css::io::XInputStream> xIn;
+ try
+ { //throws com.sun.star.ucb.InteractiveIOException
+ xIn = descContent.openStream();
+ }
+ catch ( const css::uno::Exception& )
+ {
+ if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
+ throw NoDescriptionException();
+ throw;
+ }
+ if (!xIn.is())
+ {
+ throw css::uno::Exception(
+ "Could not get XInputStream for description.xml of extension " +
+ sDescriptionUri, nullptr);
+ }
+
+ //get root node of description.xml
+ Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
+ css::xml::dom::DocumentBuilder::create(xContext) );
+
+ if (!xDocBuilder->isNamespaceAware())
+ {
+ throw css::uno::Exception(
+ "Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware.", nullptr);
+ }
+
+ Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
+ if (!xDoc.is())
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
+ }
+
+ //check for proper root element and namespace
+ Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
+ if (!xRoot.is())
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " contains no root element.", nullptr);
+ }
+
+ if ( xRoot->getTagName() != "description")
+ {
+ throw css::uno::Exception(
+ sDescriptionUri + " does not contain the root element <description>.", nullptr);
+ }
+
+ m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
+ OUString nsDescription = xRoot->getNamespaceURI();
+
+ //check if this namespace is supported
+ if ( nsDescription != "http://openoffice.org/extensions/description/2006")
+ {
+ throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
+ }
+ } catch (const css::uno::RuntimeException &) {
+ throw;
+ } catch (const css::deployment::DeploymentException &) {
+ throw;
+ } catch (const css::uno::Exception & e) {
+ css::uno::Any a(cppu::getCaughtException());
+ throw css::deployment::DeploymentException(
+ e.Message, Reference< css::uno::XInterface >(), a);
+ }
+}
+
+FileDoesNotExistFilter::FileDoesNotExistFilter(
+ const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
+ m_bExist(true), m_xCommandEnv(xCmdEnv)
+{}
+
+ // XCommandEnvironment
+Reference<css::task::XInteractionHandler >
+ FileDoesNotExistFilter::getInteractionHandler()
+{
+ return static_cast<css::task::XInteractionHandler*>(this);
+}
+
+Reference<css::ucb::XProgressHandler >
+ FileDoesNotExistFilter::getProgressHandler()
+{
+ return m_xCommandEnv.is()
+ ? m_xCommandEnv->getProgressHandler()
+ : Reference<css::ucb::XProgressHandler>();
+}
+
+// XInteractionHandler
+//If the interaction was caused by a non-existing file which is specified in the ctor
+//of FileDoesNotExistFilter, then we do nothing
+void FileDoesNotExistFilter::handle(
+ Reference<css::task::XInteractionRequest > const & xRequest )
+{
+ css::uno::Any request( xRequest->getRequest() );
+
+ css::ucb::InteractiveIOException ioexc;
+ if ((request>>= ioexc)
+ && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
+ || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
+ {
+ m_bExist = false;
+ return;
+ }
+ Reference<css::task::XInteractionHandler> xInteraction;
+ if (m_xCommandEnv.is()) {
+ xInteraction = m_xCommandEnv->getInteractionHandler();
+ }
+ if (xInteraction.is()) {
+ xInteraction->handle(xRequest);
+ }
+}
+
+}
+
+namespace dp_misc {
+
+DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL)
+{
+ Reference< css::xml::dom::XNode > root;
+ Reference<css::uno::XComponentContext> context(
+ comphelper::getProcessComponentContext());
+ try {
+ root =
+ ExtensionDescription(
+ context, sExtensionFolderURL,
+ Reference< css::ucb::XCommandEnvironment >()).
+ getRootElement();
+ } catch (const NoDescriptionException &) {
+ } catch (const css::deployment::DeploymentException & e) {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException(
+ "com.sun.star.deployment.DeploymentException: " + e.Message,
+ nullptr, anyEx );
+ }
+ return DescriptionInfoset(context, root);
+}
+
+DescriptionInfoset::DescriptionInfoset(
+ css::uno::Reference< css::uno::XComponentContext > const & context,
+ css::uno::Reference< css::xml::dom::XNode > const & element):
+ m_context(context),
+ m_element(element)
+{
+ if (m_element.is()) {
+ m_xpath = css::xml::xpath::XPathAPI::create(context);
+ m_xpath->registerNS("desc", element->getNamespaceURI());
+ m_xpath->registerNS("xlink", "http://www.w3.org/1999/xlink");
+ }
+}
+
+DescriptionInfoset::~DescriptionInfoset() {}
+
+::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
+ return getOptionalValue("desc:identifier/@value");
+}
+
+OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is() ? getNodeValue(n) : OUString();
+}
+
+void DescriptionInfoset::checkDenylist() const
+{
+ if (!m_element.is())
+ return;
+
+ std::optional< OUString > id(getIdentifier());
+ if (!id)
+ return; // nothing to check
+ OUString currentversion(getVersion());
+ if (currentversion.getLength() == 0)
+ return; // nothing to check
+
+ css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", css::uno::Any(OUString("/org.openoffice.Office.ExtensionDependencies/Extensions"))}
+ }));
+ css::uno::Reference< css::container::XNameAccess > denylist(
+ (css::configuration::theDefaultProvider::get(m_context)
+ ->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess", args)),
+ css::uno::UNO_QUERY_THROW);
+
+ // check first if a denylist entry is available
+ if (!(denylist.is() && denylist->hasByName(*id))) return;
+
+ css::uno::Reference< css::beans::XPropertySet > extProps(
+ denylist->getByName(*id), css::uno::UNO_QUERY_THROW);
+
+ css::uno::Any anyValue = extProps->getPropertyValue("Versions");
+
+ css::uno::Sequence< OUString > blversions;
+ anyValue >>= blversions;
+
+ // check if the current version requires further dependency checks from the denylist
+ if (!checkDenylistVersion(currentversion, blversions)) return;
+
+ anyValue = extProps->getPropertyValue("Dependencies");
+ OUString udeps;
+ anyValue >>= udeps;
+
+ if (udeps.getLength() == 0)
+ return; // nothing todo
+
+ OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
+
+ css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
+ m_context->getServiceManager()->createInstanceWithContext("com.sun.star.xml.dom.DocumentBuilder", m_context),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
+
+ css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
+ css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
+ css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
+ css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
+ sal_Int32 nLen = xDeps->getLength();
+
+ // get the parent xml document of current description info for the import
+ css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
+
+ // get dependency node of current description info to merge the new dependencies from the denylist
+ css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
+ m_xpath->selectSingleNode(m_element, "desc:dependencies"));
+
+ // if no dependency node exists, create a new one in the current description info
+ if (!xCurrentDeps.is()) {
+ css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
+ xCurrentDescInfo->createElementNS(
+ "http://openoffice.org/extensions/description/2006",
+ "dependencies"), css::uno::UNO_QUERY_THROW);
+ m_element->appendChild(xNewDepNode);
+ xCurrentDeps = m_xpath->selectSingleNode(m_element, "desc:dependencies");
+ }
+
+ for (sal_Int32 i=0; i<nLen; i++) {
+ css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
+ css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
+ if (xDep.is()) {
+ // found valid denylist dependency, import the node first and append it to the existing dependency node
+ css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
+ xCurrentDeps->appendChild(importedNode);
+ }
+ }
+}
+
+bool DescriptionInfoset::checkDenylistVersion(
+ std::u16string_view currentversion,
+ css::uno::Sequence< OUString > const & versions)
+{
+ sal_Int32 nLen = versions.getLength();
+ for (sal_Int32 i=0; i<nLen; i++) {
+ if (currentversion == versions[i])
+ return true;
+ }
+
+ return false;
+}
+
+OUString DescriptionInfoset::getVersion() const
+{
+ return getNodeValueFromExpression( "desc:version/@value" );
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
+{
+ //When there is no description.xml then we assume that we support all platforms
+ if (! m_element.is())
+ {
+ return { OUString("all") };
+ }
+
+ //Check if the <platform> element was provided. If not the default is "all" platforms
+ css::uno::Reference< css::xml::dom::XNode > nodePlatform(
+ m_xpath->selectSingleNode(m_element, "desc:platform"));
+ if (!nodePlatform.is())
+ {
+ return { OUString("all") };
+ }
+
+ //There is a platform element.
+ const OUString value = getNodeValueFromExpression("desc:platform/@value");
+ //parse the string, it can contained multiple strings separated by commas
+ std::vector< OUString> vec;
+ sal_Int32 nIndex = 0;
+ do
+ {
+ const OUString aToken( o3tl::trim(o3tl::getToken(value, 0, ',', nIndex )) );
+ if (!aToken.isEmpty())
+ vec.push_back(aToken);
+
+ }
+ while (nIndex >= 0);
+
+ return comphelper::containerToSequence(vec);
+}
+
+css::uno::Reference< css::xml::dom::XNodeList >
+DescriptionInfoset::getDependencies() const {
+ if (m_element.is()) {
+ try {
+ // check the extension denylist first and expand the dependencies if applicable
+ checkDenylist();
+
+ return m_xpath->selectNodeList(m_element, "desc:dependencies/*");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return new EmptyNodeList;
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateInformationUrls() const {
+ return getUrls("desc:update-information/desc:src/@xlink:href");
+}
+
+css::uno::Sequence< OUString >
+DescriptionInfoset::getUpdateDownloadUrls() const
+{
+ return getUrls("desc:update-download/desc:src/@xlink:href");
+}
+
+OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
+{
+ css::uno::Sequence< OUString > aStrList = getUrls( "desc:icon/desc:default/@xlink:href" );
+ css::uno::Sequence< OUString > aStrListHC = getUrls( "desc:icon/desc:high-contrast/@xlink:href" );
+
+ if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
+ return aStrListHC[0];
+
+ if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
+ return aStrList[0];
+
+ return OUString();
+}
+
+::std::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
+ const
+{
+ bool bParentExists = false;
+ const OUString sURL (getLocalizedHREFAttrFromChild("/desc:description/desc:update-website", &bParentExists ));
+
+ if (!sURL.isEmpty())
+ return ::std::optional< OUString >(sURL);
+ else
+ return bParentExists ? ::std::optional< OUString >(OUString()) :
+ ::std::optional< OUString >();
+}
+
+::std::optional< OUString > DescriptionInfoset::getOptionalValue(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return n.is()
+ ? ::std::optional< OUString >(getNodeValue(n))
+ : ::std::optional< OUString >();
+}
+
+css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
+ OUString const & expression) const
+{
+ css::uno::Reference< css::xml::dom::XNodeList > ns;
+ if (m_element.is()) {
+ try {
+ ns = m_xpath->selectNodeList(m_element, expression);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
+ auto urlsRange = asNonConstRange(urls);
+ for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
+ urlsRange[i] = getNodeValue(ns->item(i));
+ }
+ return urls;
+}
+
+std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:publisher");
+
+ OUString sPublisherName;
+ OUString sURL;
+ if (node.is())
+ {
+ css::uno::Reference< css::xml::dom::XNode > xPathName;
+ try {
+ xPathName = m_xpath->selectSingleNode(node, "text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xPathName.is());
+ if (xPathName.is())
+ sPublisherName = xPathName->getNodeValue();
+
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, "@xlink:href");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ return std::make_pair(sPublisherName, sURL);
+}
+
+OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:release-notes", nullptr);
+}
+
+OUString DescriptionInfoset::getLocalizedDisplayName() const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild("desc:display-name");
+ if (node.is())
+ {
+ css::uno::Reference< css::xml::dom::XNode > xtext;
+ try {
+ xtext = m_xpath->selectSingleNode(node, "text()");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (xtext.is())
+ return xtext->getNodeValue();
+ }
+ return OUString();
+}
+
+OUString DescriptionInfoset::getLocalizedLicenseURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:registration/desc:simple-license", nullptr);
+
+}
+
+::std::optional<SimpleLicenseAttributes>
+DescriptionInfoset::getSimpleLicenseAttributes() const
+{
+ //Check if the node exist
+ css::uno::Reference< css::xml::dom::XNode > n;
+ if (m_element.is()) {
+ try {
+ n = m_xpath->selectSingleNode(m_element, "/desc:description/desc:registration/desc:simple-license/@accept-by");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (n.is())
+ {
+ SimpleLicenseAttributes attributes;
+ attributes.acceptBy =
+ getNodeValueFromExpression("/desc:description/desc:registration/desc:simple-license/@accept-by");
+
+ ::std::optional< OUString > suppressOnUpdate = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-on-update");
+ if (suppressOnUpdate)
+ attributes.suppressOnUpdate = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressOnUpdate), u"true");
+ else
+ attributes.suppressOnUpdate = false;
+
+ ::std::optional< OUString > suppressIfRequired = getOptionalValue("/desc:description/desc:registration/desc:simple-license/@suppress-if-required");
+ if (suppressIfRequired)
+ attributes.suppressIfRequired = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressIfRequired), u"true");
+ else
+ attributes.suppressIfRequired = false;
+
+ return ::std::optional<SimpleLicenseAttributes>(attributes);
+ }
+ }
+ return ::std::optional<SimpleLicenseAttributes>();
+}
+
+OUString DescriptionInfoset::getLocalizedDescriptionURL() const
+{
+ return getLocalizedHREFAttrFromChild("/desc:description/desc:extension-description", nullptr);
+}
+
+css::uno::Reference< css::xml::dom::XNode >
+DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
+{
+ if ( ! m_element.is() || sParent.isEmpty())
+ return css::uno::Reference< css::xml::dom::XNode > ();
+
+ css::uno::Reference< css::xml::dom::XNode > xParent;
+ try {
+ xParent = m_xpath->selectSingleNode(m_element, sParent);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+ if (xParent.is())
+ {
+ nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
+
+ //office: en-DE, en, en-DE-altmark
+ if (! nodeMatch.is())
+ {
+ // Already tried full tag, continue with first fallback.
+ const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
+ for (auto const& fallback : aFallbacks)
+ {
+ nodeMatch = matchLanguageTag(xParent, fallback);
+ if (nodeMatch.is())
+ break;
+ }
+ if (! nodeMatch.is())
+ nodeMatch = getChildWithDefaultLocale(xParent);
+ }
+ }
+
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::matchLanguageTag(
+ css::uno::Reference< css::xml::dom::XNode > const & xParent, std::u16string_view rTag) const
+{
+ OSL_ASSERT(xParent.is());
+ css::uno::Reference<css::xml::dom::XNode> nodeMatch;
+
+ //first try exact match for lang
+ const OUString exp1(OUString::Concat("*[@lang=\"") + rTag + "\"]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+
+ //try to match in strings that also have a country and/or variant, for
+ //example en matches in en-US-montana, en-US, en-montana
+ if (!nodeMatch.is())
+ {
+ const OUString exp2(
+ OUString::Concat("*[starts-with(@lang,\"") + rTag + "-\")]");
+ try {
+ nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ return nodeMatch;
+}
+
+css::uno::Reference<css::xml::dom::XNode>
+DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
+ const & xParent) const
+{
+ OSL_ASSERT(xParent.is());
+ if ( xParent->getNodeName() == "simple-license" )
+ {
+ css::uno::Reference<css::xml::dom::XNode> nodeDefault;
+ try {
+ nodeDefault = m_xpath->selectSingleNode(xParent, "@default-license-id");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ if (nodeDefault.is())
+ {
+ //The old way
+ const OUString exp1("desc:license-text[@license-id = \""
+ + nodeDefault->getNodeValue()
+ + "\"]");
+ try {
+ return m_xpath->selectSingleNode(xParent, exp1);
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ }
+ }
+
+ try {
+ return m_xpath->selectSingleNode(xParent, "*[1]");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ return nullptr;
+ }
+}
+
+OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
+ OUString const & sXPathParent, bool * out_bParentExists)
+ const
+{
+ css::uno::Reference< css::xml::dom::XNode > node =
+ getLocalizedChild(sXPathParent);
+
+ OUString sURL;
+ if (node.is())
+ {
+ if (out_bParentExists)
+ *out_bParentExists = true;
+ css::uno::Reference< css::xml::dom::XNode > xURL;
+ try {
+ xURL = m_xpath->selectSingleNode(node, "@xlink:href");
+ } catch (const css::xml::xpath::XPathException &) {
+ // ignore
+ }
+ OSL_ASSERT(xURL.is());
+ if (xURL.is())
+ sURL = xURL->getNodeValue();
+ }
+ else
+ {
+ if (out_bParentExists)
+ *out_bParentExists = false;
+ }
+ return sURL;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_identifier.cxx b/desktop/source/deployment/misc/dp_identifier.cxx
new file mode 100644
index 000000000..8669710c7
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_identifier.cxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <optional>
+#include <com/sun/star/beans/Optional.hpp>
+#include <com/sun/star/deployment/XPackage.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ustring.hxx>
+
+#include <dp_identifier.hxx>
+
+namespace dp_misc {
+
+OUString generateIdentifier(
+ ::std::optional< OUString > const & optional,
+ std::u16string_view fileName)
+{
+ return optional ? *optional : generateLegacyIdentifier(fileName);
+}
+
+OUString getIdentifier(
+ css::uno::Reference< css::deployment::XPackage > const & package)
+{
+ OSL_ASSERT(package.is());
+ css::beans::Optional< OUString > id(package->getIdentifier());
+ return id.IsPresent
+ ? id.Value : generateLegacyIdentifier(package->getName());
+}
+
+OUString generateLegacyIdentifier(std::u16string_view fileName) {
+ return OUString::Concat("org.openoffice.legacy.") + fileName;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_interact.cxx b/desktop/source/deployment/misc/dp_interact.cxx
new file mode 100644
index 000000000..ae928a28e
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_interact.cxx
@@ -0,0 +1,137 @@
+/* -*- 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_interact.h>
+
+#include <comphelper/interaction.hxx>
+
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <osl/diagnose.h>
+
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc {
+namespace {
+
+
+class InteractionContinuationImpl : public ::cppu::OWeakObject,
+ public task::XInteractionContinuation
+{
+ const Type m_type;
+ bool * m_pselect;
+
+public:
+ InteractionContinuationImpl( Type const & type, bool * pselect )
+ : m_type( type ),
+ m_pselect( pselect )
+ { OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(m_type) ); }
+
+ // XInterface
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ virtual Any SAL_CALL queryInterface( Type const & type ) override;
+
+ // XInteractionContinuation
+ virtual void SAL_CALL select() override;
+};
+
+// XInterface
+
+void InteractionContinuationImpl::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+
+void InteractionContinuationImpl::release() noexcept
+{
+ OWeakObject::release();
+}
+
+
+Any InteractionContinuationImpl::queryInterface( Type const & type )
+{
+ if (type.isAssignableFrom( m_type )) {
+ Reference<task::XInteractionContinuation> xThis(this);
+ return Any( &xThis, type );
+ }
+ else
+ return OWeakObject::queryInterface(type);
+}
+
+// XInteractionContinuation
+
+void InteractionContinuationImpl::select()
+{
+ *m_pselect = true;
+}
+
+} // anon namespace
+
+
+bool interactContinuation( Any const & request,
+ Type const & continuation,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool * pcont, bool * pabort )
+{
+ OSL_ASSERT(
+ cppu::UnoType<task::XInteractionContinuation>::get().isAssignableFrom(
+ continuation ) );
+ if (!xCmdEnv)
+ return false;
+
+ Reference<task::XInteractionHandler> xInteractionHandler(
+ xCmdEnv->getInteractionHandler() );
+ if (!xInteractionHandler)
+ return false;
+
+ bool cont = false;
+ bool abort = false;
+ std::vector< Reference<task::XInteractionContinuation> > conts {
+ new InteractionContinuationImpl(continuation, &cont ),
+ new InteractionContinuationImpl( cppu::UnoType<task::XInteractionAbort>::get(), &abort ) };
+ xInteractionHandler->handle(
+ new ::comphelper::OInteractionRequest( request, std::move(conts) ) );
+ if (cont || abort) {
+ if (pcont != nullptr)
+ *pcont = cont;
+ if (pabort != nullptr)
+ *pabort = abort;
+ return true;
+ }
+ return false;
+}
+
+// XAbortChannel
+
+void AbortChannel::sendAbort()
+{
+ m_aborted = true;
+ if (m_xNext.is())
+ m_xNext->sendAbort();
+}
+
+} // dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_misc.cxx b/desktop/source/deployment/misc/dp_misc.cxx
new file mode 100644
index 000000000..f4437711d
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_misc.cxx
@@ -0,0 +1,561 @@
+/* -*- 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_folders.h>
+#include <config_features.h>
+#include <chrono>
+
+#include <dp_misc.h>
+#include <dp_interact.h>
+#include <dp_shared.hxx>
+#include <rtl/uri.hxx>
+#include <rtl/digest.h>
+#include <rtl/random.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/bootstrap.hxx>
+#include <osl/file.hxx>
+#include <osl/pipe.hxx>
+#include <osl/security.hxx>
+#include <osl/thread.hxx>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/bridge/BridgeFactory.hpp>
+#include <com/sun/star/bridge/UnoUrlResolver.hpp>
+#include <com/sun/star/bridge/XUnoUrlResolver.hpp>
+#include <com/sun/star/deployment/ExtensionManager.hpp>
+#include <com/sun/star/lang/DisposedException.hpp>
+#include <com/sun/star/task/OfficeRestartManager.hpp>
+#include <memory>
+#include <string_view>
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <salhelper/linkhelper.hxx>
+
+#ifdef _WIN32
+#include <prewin.h>
+#include <postwin.h>
+#endif
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc {
+namespace {
+
+struct UnoRc : public rtl::StaticWithInit<
+ std::shared_ptr<rtl::Bootstrap>, UnoRc> {
+ std::shared_ptr<rtl::Bootstrap> operator () () {
+ OUString unorc( "$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("louno") );
+ ::rtl::Bootstrap::expandMacros( unorc );
+ auto ret = std::make_shared<::rtl::Bootstrap>( unorc );
+ OSL_ASSERT( ret->getHandle() != nullptr );
+ return ret;
+ }
+};
+
+OUString generateOfficePipeId()
+{
+ OUString userPath;
+ ::utl::Bootstrap::PathStatus aLocateResult =
+ ::utl::Bootstrap::locateUserInstallation( userPath );
+ if (aLocateResult != ::utl::Bootstrap::PATH_EXISTS &&
+ aLocateResult != ::utl::Bootstrap::PATH_VALID)
+ {
+ throw Exception("Extension Manager: Could not obtain path for UserInstallation.", nullptr);
+ }
+
+ rtlDigest digest = rtl_digest_create( rtl_Digest_AlgorithmMD5 );
+ if (!digest) {
+ throw RuntimeException("cannot get digest rtl_Digest_AlgorithmMD5!", nullptr );
+ }
+
+ sal_uInt8 const * data =
+ reinterpret_cast<sal_uInt8 const *>(userPath.getStr());
+ std::size_t size = userPath.getLength() * sizeof (sal_Unicode);
+ sal_uInt32 md5_key_len = rtl_digest_queryLength( digest );
+ std::unique_ptr<sal_uInt8[]> md5_buf( new sal_uInt8 [ md5_key_len ] );
+
+ rtl_digest_init( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_update( digest, data, static_cast<sal_uInt32>(size) );
+ rtl_digest_get( digest, md5_buf.get(), md5_key_len );
+ rtl_digest_destroy( digest );
+
+ // create hex-value string from the MD5 value to keep
+ // the string size minimal
+ OUStringBuffer buf;
+ buf.append( "SingleOfficeIPC_" );
+ for ( sal_uInt32 i = 0; i < md5_key_len; ++i ) {
+ buf.append( static_cast<sal_Int32>(md5_buf[ i ]), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+bool existsOfficePipe()
+{
+ static OUString OfficePipeId = generateOfficePipeId();
+
+ OUString const & pipeId = OfficePipeId;
+ if (pipeId.isEmpty())
+ return false;
+ ::osl::Security sec;
+ ::osl::Pipe pipe( pipeId, osl_Pipe_OPEN, sec );
+ return pipe.is();
+}
+
+//get modification time
+bool getModifyTimeTargetFile(const OUString &rFileURL, TimeValue &rTime)
+{
+ salhelper::LinkResolver aResolver(osl_FileStatus_Mask_ModifyTime);
+
+ if (aResolver.fetchFileStatus(rFileURL) != osl::FileBase::E_None)
+ return false;
+
+ rTime = aResolver.m_aStatus.getModifyTime();
+
+ return true;
+}
+
+//Returns true if the Folder was more recently modified then
+//the lastsynchronized file. That is the repository needs to
+//be synchronized.
+bool compareExtensionFolderWithLastSynchronizedFile(
+ OUString const & folderURL, OUString const & fileURL)
+{
+ bool bNeedsSync = false;
+ ::osl::DirectoryItem itemExtFolder;
+ ::osl::File::RC err1 =
+ ::osl::DirectoryItem::get(folderURL, itemExtFolder);
+ //If it does not exist, then there is nothing to be done
+ if (err1 == ::osl::File::E_NOENT)
+ {
+ return false;
+ }
+ else if (err1 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access extension folder");
+ return true; //sync just in case
+ }
+
+ //If last synchronized does not exist, then OOo is started for the first time
+ ::osl::DirectoryItem itemFile;
+ ::osl::File::RC err2 = ::osl::DirectoryItem::get(fileURL, itemFile);
+ if (err2 == ::osl::File::E_NOENT)
+ {
+ return true;
+
+ }
+ else if (err2 != ::osl::File::E_None)
+ {
+ OSL_FAIL("Cannot access file lastsynchronized");
+ return true; //sync just in case
+ }
+
+ //compare the modification time of the extension folder and the last
+ //modified file
+ TimeValue timeFolder;
+ if (getModifyTimeTargetFile(folderURL, timeFolder))
+ {
+ TimeValue timeFile;
+ if (getModifyTimeTargetFile(fileURL, timeFile))
+ {
+ if (timeFile.Seconds < timeFolder.Seconds)
+ bNeedsSync = true;
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ bNeedsSync = true;
+ }
+
+ return bNeedsSync;
+}
+
+bool needToSyncRepository(std::u16string_view name)
+{
+ OUString folder;
+ OUString file;
+ if ( name == u"bundled" )
+ {
+ folder = "$BUNDLED_EXTENSIONS";
+ file = "$BUNDLED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else if ( name == u"shared" )
+ {
+ folder = "$UNO_SHARED_PACKAGES_CACHE/uno_packages";
+ file = "$SHARED_EXTENSIONS_USER/lastsynchronized";
+ }
+ else
+ {
+ OSL_ASSERT(false);
+ return true;
+ }
+ ::rtl::Bootstrap::expandMacros(folder);
+ ::rtl::Bootstrap::expandMacros(file);
+ return compareExtensionFolderWithLastSynchronizedFile(
+ folder, file);
+}
+
+
+} // anon namespace
+
+
+namespace {
+OUString encodeForRcFile( OUString const & str )
+{
+ // escape $\{} (=> rtl bootstrap files)
+ OUStringBuffer buf(64);
+ sal_Int32 pos = 0;
+ const sal_Int32 len = str.getLength();
+ for ( ; pos < len; ++pos ) {
+ sal_Unicode c = str[ pos ];
+ switch (c) {
+ case '$':
+ case '\\':
+ case '{':
+ case '}':
+ buf.append( '\\' );
+ break;
+ }
+ buf.append( c );
+ }
+ return buf.makeStringAndClear();
+}
+}
+
+
+OUString makeURL( OUString const & baseURL, OUString const & relPath_ )
+{
+ OUStringBuffer buf(128);
+ if (baseURL.getLength() > 1 && baseURL[ baseURL.getLength() - 1 ] == '/')
+ buf.append( baseURL.subView(0, baseURL.getLength() - 1) );
+ else
+ buf.append( baseURL );
+ OUString relPath(relPath_);
+ if( relPath.startsWith("/") )
+ relPath = relPath.copy( 1 );
+ if (!relPath.isEmpty())
+ {
+ buf.append( '/' );
+ if (baseURL.match( "vnd.sun.star.expand:" )) {
+ // encode for macro expansion: relPath is supposed to have no
+ // macros, so encode $, {} \ (bootstrap mimic)
+ relPath = encodeForRcFile(relPath);
+
+ // encode once more for vnd.sun.star.expand schema:
+ // vnd.sun.star.expand:$UNO_...
+ // will expand to file-url
+ relPath = ::rtl::Uri::encode( relPath, rtl_UriCharClassUric,
+ rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8 );
+ }
+ buf.append( relPath );
+ }
+ return buf.makeStringAndClear();
+}
+
+OUString makeURLAppendSysPathSegment( OUString const & baseURL, OUString const & segment )
+{
+ OSL_ASSERT(segment.indexOf(u'/') == -1);
+
+ ::rtl::Uri::encode(
+ segment, rtl_UriCharClassPchar, rtl_UriEncodeIgnoreEscapes,
+ RTL_TEXTENCODING_UTF8);
+ return makeURL(baseURL, segment);
+}
+
+
+OUString expandUnoRcTerm( OUString const & term_ )
+{
+ OUString term(term_);
+ UnoRc::get()->expandMacrosFrom( term );
+ return term;
+}
+
+OUString makeRcTerm( OUString const & url )
+{
+ OSL_ASSERT( url.match( "vnd.sun.star.expand:" ));
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcterm( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcterm = ::rtl::Uri::decode(
+ rcterm, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ return rcterm;
+ }
+ else
+ return url;
+}
+
+
+OUString expandUnoRcUrl( OUString const & url )
+{
+ if (url.match( "vnd.sun.star.expand:" )) {
+ // cut protocol:
+ OUString rcurl( url.copy( sizeof ("vnd.sun.star.expand:") - 1 ) );
+ // decode uric class chars:
+ rcurl = ::rtl::Uri::decode(
+ rcurl, rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8 );
+ // expand macro string:
+ UnoRc::get()->expandMacrosFrom( rcurl );
+ return rcurl;
+ }
+ else {
+ return url;
+ }
+}
+
+
+bool office_is_running()
+{
+ //We need to check if we run within the office process. Then we must not use the pipe, because
+ //this could cause a deadlock. This is actually a workaround for i82778
+ OUString sFile;
+ oslProcessError err = osl_getExecutableFile(& sFile.pData);
+ bool ret = false;
+ if (osl_Process_E_None == err)
+ {
+ sFile = sFile.copy(sFile.lastIndexOf('/') + 1);
+ if (
+#if defined _WIN32
+ //osl_getExecutableFile should deliver "soffice.bin" on windows
+ //even if swriter.exe, scalc.exe etc. was started. This is a bug
+ //in osl_getExecutableFile
+ sFile == "soffice.bin" || sFile == "soffice.exe" || sFile == "soffice.com"
+ || sFile == "soffice" || sFile == "swriter.exe" || sFile == "swriter"
+ || sFile == "scalc.exe" || sFile == "scalc" || sFile == "simpress.exe"
+ || sFile == "simpress" || sFile == "sdraw.exe" || sFile == "sdraw"
+ || sFile == "sbase.exe" || sFile == "sbase"
+#elif defined MACOSX
+ sFile == "soffice"
+#elif defined UNIX
+ sFile == "soffice.bin"
+#else
+#error "Unsupported platform"
+#endif
+
+ )
+ ret = true;
+ else
+ ret = existsOfficePipe();
+ }
+ else
+ {
+ OSL_FAIL("NOT osl_Process_E_None ");
+ //if osl_getExecutable file then we take the risk of creating a pipe
+ ret = existsOfficePipe();
+ }
+ return ret;
+}
+
+
+oslProcess raiseProcess(
+ OUString const & appURL, Sequence<OUString> const & args )
+{
+ ::osl::Security sec;
+ oslProcess hProcess = nullptr;
+ oslProcessError rc = osl_executeProcess(
+ appURL.pData,
+ reinterpret_cast<rtl_uString **>(
+ const_cast<OUString *>(args.getConstArray()) ),
+ args.getLength(),
+ osl_Process_DETACHED,
+ sec.getHandle(),
+ nullptr, // => current working dir
+ nullptr, 0, // => no env vars
+ &hProcess );
+
+ switch (rc) {
+ case osl_Process_E_None:
+ break;
+ case osl_Process_E_NotFound:
+ throw RuntimeException( "image not found!", nullptr );
+ case osl_Process_E_TimedOut:
+ throw RuntimeException( "timeout occurred!", nullptr );
+ case osl_Process_E_NoPermission:
+ throw RuntimeException( "permission denied!", nullptr );
+ case osl_Process_E_Unknown:
+ throw RuntimeException( "unknown error!", nullptr );
+ case osl_Process_E_InvalidError:
+ default:
+ throw RuntimeException( "unmapped error!", nullptr );
+ }
+
+ return hProcess;
+}
+
+
+OUString generateRandomPipeId()
+{
+ // compute some good pipe id:
+ static rtlRandomPool s_hPool = rtl_random_createPool();
+ if (s_hPool == nullptr)
+ throw RuntimeException( "cannot create random pool!?", nullptr );
+ sal_uInt8 bytes[ 32 ];
+ if (rtl_random_getBytes(
+ s_hPool, bytes, SAL_N_ELEMENTS(bytes) ) != rtl_Random_E_None) {
+ throw RuntimeException( "random pool error!?", nullptr );
+ }
+ OUStringBuffer buf;
+ for (unsigned char byte : bytes) {
+ buf.append( static_cast<sal_Int32>(byte), 0x10 );
+ }
+ return buf.makeStringAndClear();
+}
+
+
+Reference<XInterface> resolveUnoURL(
+ OUString const & connectString,
+ Reference<XComponentContext> const & xLocalContext,
+ AbortChannel const * abortChannel )
+{
+ Reference<bridge::XUnoUrlResolver> xUnoUrlResolver(
+ bridge::UnoUrlResolver::create( xLocalContext ) );
+
+ for (int i = 0; i <= 40; ++i) // 20 seconds
+ {
+ if (abortChannel != nullptr && abortChannel->isAborted()) {
+ throw ucb::CommandAbortedException( "abort!" );
+ }
+ try {
+ return xUnoUrlResolver->resolve( connectString );
+ }
+ catch (const connection::NoConnectException &) {
+ if (i < 40)
+ {
+ ::osl::Thread::wait( std::chrono::milliseconds(500) );
+ }
+ else throw;
+ }
+ }
+ return nullptr; // warning C4715
+}
+
+static void writeConsoleWithStream(std::u16string_view sText, FILE * stream)
+{
+ OString s = OUStringToOString(sText, osl_getThreadTextEncoding());
+ fprintf(stream, "%s", s.getStr());
+ fflush(stream);
+}
+
+void writeConsole(std::u16string_view sText)
+{
+ writeConsoleWithStream(sText, stdout);
+}
+
+void writeConsoleError(std::u16string_view sText)
+{
+ writeConsoleWithStream(sText, stderr);
+}
+
+OUString readConsole()
+{
+ char buf[1024];
+ memset(buf, 0, 1024);
+ // read one char less so that the last char in buf is always zero
+ if (fgets(buf, 1024, stdin) != nullptr)
+ {
+ OUString value = OStringToOUString(std::string_view(buf), osl_getThreadTextEncoding());
+ return value.trim();
+ }
+ throw css::uno::RuntimeException("reading from stdin failed");
+}
+
+void TRACE(OUString const & sText)
+{
+ SAL_INFO("desktop.deployment", sText);
+}
+
+void syncRepositories(
+ bool force, Reference<ucb::XCommandEnvironment> const & xCmdEnv)
+{
+ OUString sDisable;
+ ::rtl::Bootstrap::get( "DISABLE_EXTENSION_SYNCHRONIZATION", sDisable, OUString() );
+ if (!sDisable.isEmpty())
+ return;
+
+ Reference<deployment::XExtensionManager> xExtensionManager;
+ //synchronize shared before bundled otherwise there are
+ //more revoke and registration calls.
+ bool bModified = false;
+ if (force || needToSyncRepository(u"shared") || needToSyncRepository(u"bundled"))
+ {
+ xExtensionManager =
+ deployment::ExtensionManager::get(
+ comphelper::getProcessComponentContext());
+
+ if (xExtensionManager.is())
+ {
+ bModified = xExtensionManager->synchronize(
+ Reference<task::XAbortChannel>(), xCmdEnv);
+ }
+ }
+#if !HAVE_FEATURE_MACOSX_SANDBOX
+ if (bModified && !comphelper::LibreOfficeKit::isActive())
+ {
+ Reference<task::XRestartManager> restarter(task::OfficeRestartManager::get(comphelper::getProcessComponentContext()));
+ if (restarter.is())
+ {
+ restarter->requestRestart(xCmdEnv.is() ? xCmdEnv->getInteractionHandler() :
+ Reference<task::XInteractionHandler>());
+ }
+ }
+#endif
+}
+
+void disposeBridges(Reference<css::uno::XComponentContext> const & ctx)
+{
+ if (!ctx.is())
+ return;
+
+ Reference<css::bridge::XBridgeFactory2> bridgeFac( css::bridge::BridgeFactory::create(ctx) );
+
+ const Sequence< Reference<css::bridge::XBridge> >seqBridges = bridgeFac->getExistingBridges();
+ for (const Reference<css::bridge::XBridge>& bridge : seqBridges)
+ {
+ Reference<css::lang::XComponent> comp(bridge, UNO_QUERY);
+ if (comp.is())
+ {
+ try {
+ comp->dispose();
+ }
+ catch ( const css::lang::DisposedException& )
+ {
+ }
+ }
+ }
+}
+
+}
+
+OUString DpResId(TranslateId aId)
+{
+ static std::locale SINGLETON = Translate::Create("dkt");
+ return Translate::get(aId, SINGLETON);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_platform.cxx b/desktop/source/deployment/misc/dp_platform.cxx
new file mode 100644
index 000000000..879e617b7
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_platform.cxx
@@ -0,0 +1,207 @@
+/* -*- 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_platform.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/bootstrap.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+constexpr OUStringLiteral PLATFORM_ALL = u"all";
+
+
+namespace dp_misc
+{
+namespace
+{
+ struct StrOperatingSystem :
+ public rtl::StaticWithInit<OUString, StrOperatingSystem> {
+ OUString operator () () {
+ OUString os( "$_OS" );
+ ::rtl::Bootstrap::expandMacros( os );
+ return os;
+ }
+ };
+
+ struct StrCPU :
+ public rtl::StaticWithInit<OUString, StrCPU> {
+ OUString operator () () {
+ OUString arch( "$_ARCH" );
+ ::rtl::Bootstrap::expandMacros( arch );
+ return arch;
+ }
+ };
+
+
+ struct StrPlatform : public rtl::StaticWithInit<
+ OUString, StrPlatform> {
+ OUString operator () () {
+ return StrOperatingSystem::get() + "_" + StrCPU::get();
+ }
+ };
+
+ bool checkOSandCPU(std::u16string_view os, std::u16string_view cpu)
+ {
+ return (os == StrOperatingSystem::get())
+ && (cpu == StrCPU::get());
+ }
+
+ bool isPlatformSupported( std::u16string_view token )
+ {
+ bool ret = false;
+ if (token == PLATFORM_ALL)
+ ret = true;
+ else if (token == u"windows_x86")
+ ret = checkOSandCPU(u"Windows", u"x86");
+ else if (token == u"windows_x86_64")
+ ret = checkOSandCPU(u"Windows", u"X86_64");
+ else if (token == u"windows_aarch64")
+ ret = checkOSandCPU(u"Windows", u"AARCH64");
+ else if (token == u"solaris_sparc")
+ ret = checkOSandCPU(u"Solaris", u"SPARC");
+ else if (token == u"solaris_sparc64")
+ ret = checkOSandCPU(u"Solaris", u"SPARC64");
+ else if (token == u"solaris_x86")
+ ret = checkOSandCPU(u"Solaris", u"x86");
+ else if (token == u"aix_powerpc")
+ ret = checkOSandCPU(u"AIX", u"PowerPC");
+ else if (token == u"macosx_aarch64")
+ ret = checkOSandCPU(u"MacOSX", u"AARCH64");
+ else if (token == u"macosx_x86_64")
+ ret = checkOSandCPU(u"MacOSX", u"X86_64");
+ else if (token == u"linux_x86")
+ ret = checkOSandCPU(u"Linux", u"x86");
+ else if (token == u"linux_x86_64")
+ ret = checkOSandCPU(u"Linux", u"X86_64");
+ else if (token == u"linux_sparc")
+ ret = checkOSandCPU(u"Linux", u"SPARC");
+ else if (token == u"linux_sparc64")
+ ret = checkOSandCPU(u"Linux", u"SPARC64");
+ else if (token == u"linux_powerpc")
+ ret = checkOSandCPU(u"Linux", u"PowerPC");
+ else if (token == u"linux_powerpc64")
+ ret = checkOSandCPU(u"Linux", u"PowerPC_64");
+ else if (token == u"linux_powerpc64_le")
+ ret = checkOSandCPU(u"Linux", u"PowerPC_64_LE");
+ else if (token == u"linux_arm_eabi")
+ ret = checkOSandCPU(u"Linux", u"ARM_EABI");
+ else if (token == u"linux_arm_oabi")
+ ret = checkOSandCPU(u"Linux", u"ARM_OABI");
+ else if (token == u"linux_mips_el")
+ ret = checkOSandCPU(u"Linux", u"MIPS_EL");
+ else if (token == u"linux_mips64_el")
+ ret = checkOSandCPU(u"Linux", u"MIPS64_EL");
+ else if (token == u"linux_mips_eb")
+ ret = checkOSandCPU(u"Linux", u"MIPS_EB");
+ else if (token == u"linux_mips64_eb")
+ ret = checkOSandCPU(u"Linux", u"MIPS64_EB");
+ else if (token == u"linux_ia64")
+ ret = checkOSandCPU(u"Linux", u"IA64");
+ else if (token == u"linux_m68k")
+ ret = checkOSandCPU(u"Linux", u"M68K");
+ else if (token == u"linux_s390")
+ ret = checkOSandCPU(u"Linux", u"S390");
+ else if (token == u"linux_s390x")
+ ret = checkOSandCPU(u"Linux", u"S390x");
+ else if (token == u"linux_hppa")
+ ret = checkOSandCPU(u"Linux", u"HPPA");
+ else if (token == u"linux_alpha")
+ ret = checkOSandCPU(u"Linux", u"ALPHA");
+ else if (token == u"linux_aarch64")
+ ret = checkOSandCPU(u"Linux", u"AARCH64");
+ else if (token == u"freebsd_x86")
+ ret = checkOSandCPU(u"FreeBSD", u"x86");
+ else if (token == u"freebsd_x86_64")
+ ret = checkOSandCPU(u"FreeBSD", u"X86_64");
+ else if (token == u"freebsd_powerpc")
+ ret = checkOSandCPU(u"FreeBSD", u"PowerPC");
+ else if (token == u"freebsd_powerpc64")
+ ret = checkOSandCPU(u"FreeBSD", u"PowerPC64");
+ else if (token == u"kfreebsd_x86")
+ ret = checkOSandCPU(u"kFreeBSD", u"x86");
+ else if (token == u"kfreebsd_x86_64")
+ ret = checkOSandCPU(u"kFreeBSD", u"X86_64");
+ else if (token == u"netbsd_x86")
+ ret = checkOSandCPU(u"NetBSD", u"x86");
+ else if (token == u"netbsd_x86_64")
+ ret = checkOSandCPU(u"NetBSD", u"X86_64");
+ else if (token == u"openbsd_x86")
+ ret = checkOSandCPU(u"OpenBSD", u"x86");
+ else if (token == u"openbsd_x86_64")
+ ret = checkOSandCPU(u"OpenBSD", u"X86_64");
+ else if (token == u"dragonfly_x86")
+ ret = checkOSandCPU(u"DragonFly", u"x86");
+ else if (token == u"dragonfly_x86_64")
+ ret = checkOSandCPU(u"DragonFly", u"X86_64");
+ else
+ {
+ OSL_FAIL("Extension Manager: The extension supports an unknown platform. "
+ "Check the platform in the description.xml");
+ ret = false;
+ }
+ return ret;
+ }
+
+} // anon namespace
+
+
+OUString const & getPlatformString()
+{
+ return StrPlatform::get();
+}
+
+bool platform_fits( std::u16string_view platform_string )
+{
+ sal_Int32 index = 0;
+ for (;;)
+ {
+ const std::u16string_view token(
+ o3tl::trim(o3tl::getToken(platform_string, 0, ',', index )) );
+ // check if this platform:
+ if (o3tl::equalsIgnoreAsciiCase( token, StrPlatform::get() ) ||
+ (token.find( '_' ) == std::u16string_view::npos && /* check OS part only */
+ o3tl::equalsIgnoreAsciiCase( token, StrOperatingSystem::get() )))
+ {
+ return true;
+ }
+ if (index < 0)
+ break;
+ }
+ return false;
+}
+
+bool hasValidPlatform( css::uno::Sequence<OUString> const & platformStrings)
+{
+ bool ret = false;
+ for (const OUString& s : platformStrings)
+ {
+ if ( isPlatformSupported( s ) )
+ {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_resource.cxx b/desktop/source/deployment/misc/dp_resource.cxx
new file mode 100644
index 000000000..682c90e52
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_resource.cxx
@@ -0,0 +1,42 @@
+/* -*- 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_resource.h>
+#include <unotools/configmgr.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+namespace dp_misc
+{
+const LanguageTag& getOfficeLanguageTag()
+{
+ static const LanguageTag OFFICE_LANG = []() {
+ OUString slang(utl::ConfigManager::getUILocale());
+ //fallback, the locale is currently only set when the user starts the
+ //office for the first time.
+ if (slang.isEmpty())
+ slang = "en-US";
+ return LanguageTag(slang);
+ }();
+ return OFFICE_LANG;
+}
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_ucb.cxx b/desktop/source/deployment/misc/dp_ucb.cxx
new file mode 100644
index 000000000..28f4192ee
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_ucb.cxx
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <dp_misc.h>
+#include <dp_ucb.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <ucbhelper/content.hxx>
+#include <xmlscript/xml_helper.hxx>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <comphelper/processfactory.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace dp_misc
+{
+
+
+bool create_ucb_content(
+ ::ucbhelper::Content * ret_ucbContent, OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ try {
+ // Existence check...
+ // content ctor/isFolder() will throw exception in case the resource
+ // does not exist.
+
+ // dilemma: no chance to use the given handler here, because it would
+ // raise no such file dialogs, else no interaction for
+ // passwords, ...? xxx todo
+ ::ucbhelper::Content ucbContent(
+ url, Reference<XCommandEnvironment>(),
+ comphelper::getProcessComponentContext() );
+
+ ucbContent.isFolder();
+
+ if (ret_ucbContent != nullptr)
+ {
+ ucbContent.setCommandEnvironment( xCmdEnv );
+ *ret_ucbContent = ucbContent;
+ }
+ return true;
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ }
+ return false;
+}
+
+
+bool create_folder(
+ ::ucbhelper::Content * ret_ucb_content, OUString const & url_,
+ Reference<XCommandEnvironment> const & xCmdEnv, bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content(
+ &ucb_content, url_, xCmdEnv, false /* no throw */ ))
+ {
+ if (ucb_content.isFolder()) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+
+ OUString url( url_ );
+ // xxx todo: find parent
+ sal_Int32 slash = url.lastIndexOf( '/' );
+ if (slash < 0) {
+ // fallback:
+ url = expandUnoRcUrl( url );
+ slash = url.lastIndexOf( '/' );
+ }
+ if (slash < 0) {
+ // invalid: has to be at least "auth:/..."
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder (invalid path): '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+ }
+ ::ucbhelper::Content parentContent;
+ if (! create_folder(
+ &parentContent, url.copy( 0, slash ), xCmdEnv, throw_exc ))
+ return false;
+ const Any title( ::rtl::Uri::decode( url.copy( slash + 1 ),
+ rtl_UriDecodeWithCharset,
+ RTL_TEXTENCODING_UTF8 ) );
+ const Sequence<ContentInfo> infos(
+ parentContent.queryCreatableContentsInfo() );
+ for ( ContentInfo const & info : infos )
+ {
+ // look KIND_FOLDER:
+ if ((info.Attributes & ContentInfoAttribute::KIND_FOLDER) != 0)
+ {
+ // make sure the only required bootstrap property is "Title":
+ Sequence<beans::Property> const & rProps = info.Properties;
+ if ( rProps.getLength() != 1 || rProps[ 0 ].Name != "Title" )
+ continue;
+
+ try {
+ if (parentContent.insertNewContent(
+ info.Type,
+ StrTitle::getTitleSequence(),
+ Sequence<Any>( &title, 1 ),
+ ucb_content )) {
+ if (ret_ucb_content != nullptr)
+ *ret_ucb_content = ucb_content;
+ return true;
+ }
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const CommandFailedException &) {
+ // Interaction Handler already handled the error
+ // that has occurred...
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ }
+ if (throw_exc)
+ throw ContentCreationException(
+ "Cannot create folder: '" + url + "'",
+ Reference<XInterface>(), ContentCreationError_UNKNOWN );
+ return false;
+}
+
+
+bool erase_path( OUString const & url,
+ Reference<XCommandEnvironment> const & xCmdEnv,
+ bool throw_exc )
+{
+ ::ucbhelper::Content ucb_content;
+ if (create_ucb_content( &ucb_content, url, xCmdEnv, false /* no throw */ ))
+ {
+ try {
+ ucb_content.executeCommand(
+ "delete", Any( true /* delete physically */ ) );
+ }
+ catch (const RuntimeException &) {
+ throw;
+ }
+ catch (const Exception &) {
+ if (throw_exc)
+ throw;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+std::vector<sal_Int8> readFile( ::ucbhelper::Content & ucb_content )
+{
+ std::vector<sal_Int8> bytes;
+ Reference<io::XOutputStream> xStream(
+ ::xmlscript::createOutputStream( &bytes ) );
+ if (! ucb_content.openStream( xStream ))
+ throw RuntimeException(
+ "::ucbhelper::Content::openStream( XOutputStream ) failed!",
+ nullptr );
+ return bytes;
+}
+
+
+bool readLine( OUString * res, OUString const & startingWith,
+ ::ucbhelper::Content & ucb_content, rtl_TextEncoding textenc )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), textenc );
+ sal_Int32 pos = 0;
+ for (;;)
+ {
+ if (file.match( startingWith, pos ))
+ {
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+ pos += startingWith.getLength();
+ for (;;)
+ {
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( file.subView(start) );
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ {
+ // consume extra CR
+ buf.append( file.subView(start, pos - start - 1) );
+ ++pos;
+ }
+ else
+ buf.append( file.subView(start, pos - start) );
+ ++pos; // consume LF
+ // check next line:
+ if (pos < file.getLength() &&
+ (file[ pos ] == ' ' || file[ pos ] == '\t'))
+ {
+ buf.append( ' ' );
+ ++pos;
+ start = pos;
+ continue;
+ }
+ }
+ break;
+ }
+ *res = buf.makeStringAndClear();
+ return true;
+ }
+ // next line:
+ sal_Int32 next_lf = file.indexOf( LF, pos );
+ if (next_lf < 0) // EOF
+ break;
+ pos = next_lf + 1;
+ }
+ return false;
+}
+
+bool readProperties( std::vector< std::pair< OUString, OUString> > & out_result,
+ ::ucbhelper::Content & ucb_content )
+{
+ // read whole file:
+ std::vector<sal_Int8> bytes( readFile( ucb_content ) );
+ OUString file( reinterpret_cast<char const *>(bytes.data()),
+ bytes.size(), RTL_TEXTENCODING_UTF8);
+ sal_Int32 pos = 0;
+
+ for (;;)
+ {
+
+ OUStringBuffer buf;
+ sal_Int32 start = pos;
+
+ bool bEOF = false;
+ pos = file.indexOf( LF, pos );
+ if (pos < 0) { // EOF
+ buf.append( file.subView(start) );
+ bEOF = true;
+ }
+ else
+ {
+ if (pos > 0 && file[ pos - 1 ] == CR)
+ // consume extra CR
+ buf.append( file.subView(start, pos - start - 1) );
+ else
+ buf.append( file.subView(start, pos - start) );
+ pos++;
+ }
+ OUString aLine = buf.makeStringAndClear();
+
+ sal_Int32 posEqual = aLine.indexOf('=');
+ if (posEqual > 0 && (posEqual + 1) < aLine.getLength())
+ {
+ OUString name = aLine.copy(0, posEqual);
+ OUString value = aLine.copy(posEqual + 1);
+ out_result.emplace_back(name, value);
+ }
+
+ if (bEOF)
+ break;
+ }
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_update.cxx b/desktop/source/deployment/misc/dp_update.cxx
new file mode 100644
index 000000000..7116be42b
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_update.cxx
@@ -0,0 +1,408 @@
+/* -*- 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_folders.h>
+
+#include <dp_update.hxx>
+#include <dp_version.hxx>
+#include <dp_identifier.hxx>
+#include <dp_descriptioninfoset.hxx>
+
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <osl/diagnose.h>
+#include <rtl/bootstrap.hxx>
+#include <sal/log.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+
+
+namespace dp_misc {
+namespace {
+
+int determineHighestVersion(
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = 0;
+ OUString greatest = userVersion;
+ if (dp_misc::compareVersions(sharedVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 1;
+ greatest = sharedVersion;
+ }
+ if (dp_misc::compareVersions(bundledVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 2;
+ greatest = bundledVersion;
+ }
+ if (dp_misc::compareVersions(onlineVersion, greatest) == dp_misc::GREATER)
+ {
+ index = 3;
+ }
+ return index;
+}
+
+Sequence< Reference< xml::dom::XElement > >
+getUpdateInformation( Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ Sequence< OUString > const & urls,
+ OUString const & identifier,
+ uno::Any & out_error)
+{
+ try {
+ return updateInformation->getUpdateInformation(urls, identifier);
+ } catch (const uno::RuntimeException &) {
+ throw;
+ } catch (const ucb::CommandFailedException & e) {
+ out_error = e.Reason;
+ } catch (const ucb::CommandAbortedException &) {
+ } catch (const uno::Exception & e) {
+ out_error <<= e;
+ }
+ return
+ Sequence<Reference< xml::dom::XElement > >();
+}
+
+void getOwnUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map, std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors,
+ bool & out_allFound)
+{
+ bool bAllHaveOwnUpdateInformation = true;
+ for (auto & inout : inout_map)
+ {
+ OSL_ASSERT(inout.second.extension.is());
+ Sequence<OUString> urls(inout.second.extension->getUpdateInformationURLs());
+ if (urls.hasElements())
+ {
+ const OUString search_id = dp_misc::getIdentifier(inout.second.extension);
+ SAL_INFO( "extensions.update", "Searching update for " << search_id );
+ uno::Any anyError;
+ //It is unclear from the idl if there can be a null reference returned.
+ //However all valid information should be the same
+ const Sequence<Reference< xml::dom::XElement > >
+ infos(getUpdateInformation(updateInformation, urls, search_id, anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(inout.second.extension, anyError);
+
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ dp_misc::DescriptionInfoset infoset(
+ xContext,
+ Reference< xml::dom::XNode >(element, UNO_QUERY_THROW));
+ if (!infoset.hasDescription())
+ continue;
+ std::optional< OUString > result_id(infoset.getIdentifier());
+ if (!result_id)
+ continue;
+ SAL_INFO( "extensions.update", " found version "
+ << infoset.getVersion() << " for " << *result_id );
+ if (*result_id != search_id)
+ continue;
+ inout.second.version = infoset.getVersion();
+ inout.second.info.set(element, UNO_QUERY_THROW);
+ break;
+ }
+ }
+ else
+ {
+ bAllHaveOwnUpdateInformation = false;
+ }
+ }
+ out_allFound = bAllHaveOwnUpdateInformation;
+}
+
+void getDefaultUpdateInfos(
+ Reference<uno::XComponentContext> const & xContext,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ UpdateInfoMap& inout_map,
+ std::vector<std::pair<Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ const OUString sDefaultURL(dp_misc::getExtensionDefaultUpdateURL());
+ OSL_ASSERT(!sDefaultURL.isEmpty());
+
+ Any anyError;
+ const Sequence< Reference< xml::dom::XElement > >
+ infos(
+ getUpdateInformation(
+ updateInformation,
+ Sequence< OUString >(&sDefaultURL, 1), OUString(), anyError));
+ if (anyError.hasValue())
+ out_errors.emplace_back(Reference<deployment::XPackage>(), anyError);
+ for (const Reference< xml::dom::XElement >& element : infos)
+ {
+ Reference< xml::dom::XNode > node(element, UNO_QUERY_THROW);
+ dp_misc::DescriptionInfoset infoset(xContext, node);
+ std::optional< OUString > id(infoset.getIdentifier());
+ if (!id) {
+ continue;
+ }
+ UpdateInfoMap::iterator j = inout_map.find(*id);
+ if (j != inout_map.end())
+ {
+ //skip those extension which provide its own update urls
+ if (j->second.extension->getUpdateInformationURLs().getLength())
+ continue;
+ OUString v(infoset.getVersion());
+ //look for the highest version in the online repository
+ if (dp_misc::compareVersions(v, j->second.version) ==
+ dp_misc::GREATER)
+ {
+ j->second.version = v;
+ j->second.info = node;
+ }
+ }
+ }
+}
+
+bool containsBundledOnly(Sequence<Reference<deployment::XPackage> > const & sameIdExtensions)
+{
+ OSL_ASSERT(sameIdExtensions.getLength() == 3);
+ return !sameIdExtensions[0].is() && !sameIdExtensions[1].is() && sameIdExtensions[2].is();
+}
+
+/** Returns true if the list of extensions are bundled extensions and there are no
+ other extensions with the same identifier in the shared or user repository.
+ If extensionList is NULL, then it is checked if there are only bundled extensions.
+*/
+bool onlyBundledExtensions(
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ std::vector< Reference<deployment::XPackage > > const * extensionList)
+{
+ OSL_ASSERT(xExtMgr.is());
+ bool bOnlyBundled = true;
+ if (extensionList)
+ {
+ for (auto const& elem : *extensionList)
+ {
+ Sequence<Reference<deployment::XPackage> > seqExt = xExtMgr->getExtensionsWithSameIdentifier(
+ dp_misc::getIdentifier(elem), elem->getName(), Reference<ucb::XCommandEnvironment>());
+
+ bOnlyBundled = containsBundledOnly(seqExt);
+ if (!bOnlyBundled)
+ break;
+ }
+ }
+ else
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt =
+ xExtMgr->getAllExtensions(Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ for (int pos(0), nLen(seqAllExt.getLength()); bOnlyBundled && pos != nLen; ++pos)
+ {
+ bOnlyBundled = containsBundledOnly(seqAllExt[pos]);
+ }
+ }
+ return bOnlyBundled;
+}
+
+} // anon namespace
+
+
+OUString getExtensionDefaultUpdateURL()
+{
+ OUString sUrl(
+ "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version")
+ ":Version:ExtensionUpdateURL}");
+ ::rtl::Bootstrap::expandMacros(sUrl);
+ return sUrl;
+}
+
+/* returns the index of the greatest version, starting with 0
+
+ */
+UPDATE_SOURCE isUpdateUserExtension(
+ bool bReadOnlyShared,
+ OUString const & userVersion,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+ if (bReadOnlyShared)
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ else if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+
+ }
+ }
+ else
+ {
+ if (!userVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ userVersion, sharedVersion, bundledVersion, onlineVersion);
+ if (index == 1)
+ retVal = UPDATE_SOURCE_SHARED;
+ else if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ }
+
+ return retVal;
+}
+
+UPDATE_SOURCE isUpdateSharedExtension(
+ bool bReadOnlyShared,
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ if (bReadOnlyShared)
+ return UPDATE_SOURCE_NONE;
+ UPDATE_SOURCE retVal = UPDATE_SOURCE_NONE;
+
+ if (!sharedVersion.isEmpty())
+ {
+ int index = determineHighestVersion(
+ OUString(), sharedVersion, bundledVersion, onlineVersion);
+ if (index == 2)
+ retVal = UPDATE_SOURCE_BUNDLED;
+ else if (index == 3)
+ retVal = UPDATE_SOURCE_ONLINE;
+ }
+ return retVal;
+}
+
+Reference<deployment::XPackage>
+getExtensionWithHighestVersion(
+ Sequence<Reference<deployment::XPackage> > const & seqExt)
+{
+ if (!seqExt.hasElements())
+ return Reference<deployment::XPackage>();
+
+ Reference<deployment::XPackage> greatest;
+ sal_Int32 len = seqExt.getLength();
+
+ for (sal_Int32 i = 0; i < len; i++)
+ {
+ if (!greatest.is())
+ {
+ greatest = seqExt[i];
+ continue;
+ }
+ Reference<deployment::XPackage> const & current = seqExt[i];
+ //greatest has a value
+ if (! current.is())
+ continue;
+
+ if (dp_misc::compareVersions(current->getVersion(), greatest->getVersion()) == dp_misc::GREATER)
+ greatest = current;
+ }
+ return greatest;
+}
+
+UpdateInfo::UpdateInfo( Reference< deployment::XPackage> const & ext):
+extension(ext)
+{
+}
+
+
+UpdateInfoMap getOnlineUpdateInfos(
+ Reference<uno::XComponentContext> const &xContext,
+ Reference<deployment::XExtensionManager> const & xExtMgr,
+ Reference<deployment::XUpdateInformationProvider > const & updateInformation,
+ std::vector<Reference<deployment::XPackage > > const * extensionList,
+ std::vector<std::pair< Reference<deployment::XPackage>, uno::Any> > & out_errors)
+{
+ OSL_ASSERT(xExtMgr.is());
+ UpdateInfoMap infoMap;
+ if (!xExtMgr.is() || onlyBundledExtensions(xExtMgr, extensionList))
+ return infoMap;
+
+ if (!extensionList)
+ {
+ const uno::Sequence< uno::Sequence< Reference<deployment::XPackage > > > seqAllExt = xExtMgr->getAllExtensions(
+ Reference<task::XAbortChannel>(), Reference<ucb::XCommandEnvironment>());
+
+ //fill the UpdateInfoMap. key = extension identifier, value = UpdateInfo
+ for (int pos = seqAllExt.getLength(); pos --; )
+ {
+ uno::Sequence<Reference<deployment::XPackage> > const & seqExt = seqAllExt[pos];
+
+ Reference<deployment::XPackage> extension = getExtensionWithHighestVersion(seqExt);
+ OSL_ASSERT(extension.is());
+
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(extension), UpdateInfo(extension));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+ else
+ {
+ for (auto const& elem : *extensionList)
+ {
+ OSL_ASSERT(elem.is());
+ std::pair<UpdateInfoMap::iterator, bool> insertRet = infoMap.emplace(
+ dp_misc::getIdentifier(elem), UpdateInfo(elem));
+ OSL_ASSERT(insertRet.second);
+ }
+ }
+
+ //Now find the update information for the extensions which provide their own
+ //URLs to update information.
+ bool bAllInfosObtained = false;
+ getOwnUpdateInfos(xContext, updateInformation, infoMap, out_errors, bAllInfosObtained);
+
+ if (!bAllInfosObtained)
+ getDefaultUpdateInfos(xContext, updateInformation, infoMap, out_errors);
+ return infoMap;
+}
+OUString getHighestVersion(
+ OUString const & sharedVersion,
+ OUString const & bundledVersion,
+ OUString const & onlineVersion)
+{
+ int index = determineHighestVersion(OUString(), sharedVersion, bundledVersion, onlineVersion);
+ switch (index)
+ {
+ case 1: return sharedVersion;
+ case 2: return bundledVersion;
+ case 3: return onlineVersion;
+ default: OSL_ASSERT(false);
+ }
+
+ return OUString();
+}
+} //namespace dp_misc
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/dp_version.cxx b/desktop/source/deployment/misc/dp_version.cxx
new file mode 100644
index 000000000..703045dde
--- /dev/null
+++ b/desktop/source/deployment/misc/dp_version.cxx
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <sal/config.h>
+
+#include <rtl/ustring.hxx>
+
+#include <dp_version.hxx>
+
+namespace {
+
+OUString getElement(OUString const & version, ::sal_Int32 * index)
+{
+ while (*index < version.getLength() && version[*index] == '0') {
+ ++*index;
+ }
+ return version.getToken(0, '.', *index);
+}
+
+}
+
+namespace dp_misc {
+
+::dp_misc::Order compareVersions(
+ OUString const & version1, OUString const & version2)
+{
+ for (::sal_Int32 i1 = 0, i2 = 0; i1 >= 0 || i2 >= 0;) {
+ OUString e1(i1 >= 0 ? getElement(version1, &i1) : OUString());
+ OUString e2(i2 >= 0 ? getElement(version2, &i2) : OUString());
+ if (e1.getLength() < e2.getLength()) {
+ return ::dp_misc::LESS;
+ } else if (e1.getLength() > e2.getLength()) {
+ return ::dp_misc::GREATER;
+ } else if (e1 < e2) {
+ return ::dp_misc::LESS;
+ } else if (e1 > e2) {
+ return ::dp_misc::GREATER;
+ }
+ }
+ return ::dp_misc::EQUAL;
+}
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/desktop/source/deployment/misc/lockfile.cxx b/desktop/source/deployment/misc/lockfile.cxx
new file mode 100644
index 000000000..206da8286
--- /dev/null
+++ b/desktop/source/deployment/misc/lockfile.cxx
@@ -0,0 +1,211 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#else
+#if !defined WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#endif
+#include <comphelper/random.hxx>
+#include <sal/types.h>
+#include <osl/file.hxx>
+#include <osl/security.hxx>
+#include <unotools/bootstrap.hxx>
+#include <tools/config.hxx>
+
+#include <lockfile.hxx>
+
+using namespace ::osl;
+using namespace ::utl;
+
+
+static OString impl_getHostname()
+{
+ OString aHost;
+#ifdef _WIN32
+ /*
+ prevent windows from connecting to the net to get its own
+ hostname by using the netbios name
+ */
+ DWORD sz = MAX_COMPUTERNAME_LENGTH + 1;
+ TCHAR szHost[MAX_COMPUTERNAME_LENGTH + 1];
+ if (GetComputerNameA(szHost, &sz))
+ aHost = OString(szHost);
+ else
+ aHost = OString("UNKNOWN");
+#else
+ /* Don't do dns lookup on Linux either */
+ char pHostName[1024];
+
+ if ( gethostname( pHostName, sizeof( pHostName ) - 1 ) == 0 )
+ {
+ pHostName[sizeof( pHostName ) - 1] = '\0';
+ aHost = OString( pHostName );
+ }
+ else
+ aHost = OString("UNKNOWN");
+#endif
+
+ return aHost;
+}
+
+namespace desktop {
+
+ Lockfile::Lockfile( bool bIPCserver )
+ :m_bIPCserver(bIPCserver)
+ ,m_bRemove(false)
+ ,m_bIsLocked(false)
+ {
+ // build the file-url to use for the lock
+ OUString aUserPath;
+ utl::Bootstrap::locateUserInstallation( aUserPath );
+ m_aLockname = aUserPath + "/.lock";
+
+ // generate ID
+ const int nIdBytes = 16;
+ char tmpId[nIdBytes*2+1];
+ time_t t = time(nullptr);
+ for (int i = 0; i<nIdBytes; i++) {
+ int tmpByte = comphelper::rng::uniform_int_distribution(0, 0xFF);
+ sprintf( tmpId+i*2, "%02X", tmpByte );
+ }
+ tmpId[nIdBytes*2]=0x00;
+ m_aId = OUString::createFromAscii( tmpId );
+
+ // generate date string
+ char *tmpTime = ctime( &t );
+ if (tmpTime != nullptr) {
+ m_aDate = OUString::createFromAscii( tmpTime );
+ sal_Int32 i = m_aDate.indexOf('\n');
+ if (i > 0)
+ m_aDate = m_aDate.copy(0, i);
+ }
+
+
+ // try to create file
+ File aFile(m_aLockname);
+ if (aFile.open( osl_File_OpenFlag_Create ) == File::E_EXIST) {
+ m_bIsLocked = true;
+ } else {
+ // new lock created
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ }
+ }
+
+ bool Lockfile::check( fpExecWarning execWarning )
+ {
+
+ if (m_bIsLocked) {
+ // lock existed, ask user what to do
+ if (isStale() ||
+ (execWarning != nullptr && (*execWarning)( this ))) {
+ // remove file and create new
+ File::remove( m_aLockname );
+ File aFile(m_aLockname);
+ (void)aFile.open( osl_File_OpenFlag_Create );
+ aFile.close( );
+ syncToFile( );
+ m_bRemove = true;
+ return true;
+ } else {
+ //leave alone and return false
+ m_bRemove = false;
+ return false;
+ }
+ } else {
+ // lock was created by us
+ return true;
+ }
+ }
+
+ bool Lockfile::isStale() const
+ {
+ // this checks whether the lockfile was created on the same
+ // host by the same user. Should this be the case it is safe
+ // to assume that it is a stale lockfile which can be overwritten
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+ OString aIPCserver = aConfig.ReadKey( LOCKFILE_IPCKEY );
+ if (!aIPCserver.equalsIgnoreAsciiCase("true"))
+ return false;
+
+ OString aHost = aConfig.ReadKey( LOCKFILE_HOSTKEY );
+ OString aUser = aConfig.ReadKey( LOCKFILE_USERKEY );
+
+ // lockfile from same host?
+ OString myHost( impl_getHostname() );
+ if (aHost == myHost) {
+ // lockfile by same UID
+ OUString myUserName;
+ Security aSecurity;
+ aSecurity.getUserName( myUserName );
+ OString myUser(OUStringToOString(myUserName, RTL_TEXTENCODING_ASCII_US));
+ if (aUser == myUser)
+ return true;
+ }
+ return false;
+ }
+
+ void Lockfile::syncToFile() const
+ {
+ OUString aLockname = m_aLockname;
+ Config aConfig(aLockname);
+ aConfig.SetGroup(LOCKFILE_GROUP);
+
+ // get information
+ OString aHost( impl_getHostname() );
+ OUString aUserName;
+ Security aSecurity;
+ aSecurity.getUserName( aUserName );
+ OString aUser = OUStringToOString( aUserName, RTL_TEXTENCODING_ASCII_US );
+ OString aTime = OUStringToOString( m_aDate, RTL_TEXTENCODING_ASCII_US );
+ OString aStamp = OUStringToOString( m_aId, RTL_TEXTENCODING_ASCII_US );
+
+ // write information
+ aConfig.WriteKey( LOCKFILE_USERKEY, aUser );
+ aConfig.WriteKey( LOCKFILE_HOSTKEY, aHost );
+ aConfig.WriteKey( LOCKFILE_STAMPKEY, aStamp );
+ aConfig.WriteKey( LOCKFILE_TIMEKEY, aTime );
+ aConfig.WriteKey(
+ LOCKFILE_IPCKEY,
+ m_bIPCserver ? OString("true") : OString("false") );
+ aConfig.Flush( );
+ }
+
+ Lockfile::~Lockfile()
+ {
+ // unlock userdata by removing file
+ if ( m_bRemove )
+ File::remove( m_aLockname );
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */