diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /desktop/source/deployment/misc | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | desktop/source/deployment/misc/dp_dependencies.cxx | 196 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_descriptioninfoset.cxx | 809 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_identifier.cxx | 56 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_interact.cxx | 137 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_misc.cxx | 561 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_platform.cxx | 207 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_resource.cxx | 42 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_ucb.cxx | 304 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_update.cxx | 408 | ||||
-rw-r--r-- | desktop/source/deployment/misc/dp_version.cxx | 63 | ||||
-rw-r--r-- | desktop/source/deployment/misc/lockfile.cxx | 211 |
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: */ |