809 lines
28 KiB
C++
809 lines
28 KiB
C++
/* -*- 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(u"bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call"_ustr,
|
|
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(
|
|
u"Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware."_ustr, 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;
|
|
const 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(u"desc"_ustr, element->getNamespaceURI());
|
|
m_xpath->registerNS(u"xlink"_ustr, u"http://www.w3.org/1999/xlink"_ustr);
|
|
}
|
|
}
|
|
|
|
DescriptionInfoset::~DescriptionInfoset() {}
|
|
|
|
::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
|
|
return getOptionalValue(u"desc:identifier/@value"_ustr);
|
|
}
|
|
|
|
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(u"/org.openoffice.Office.ExtensionDependencies/Extensions"_ustr)}
|
|
}));
|
|
css::uno::Reference< css::container::XNameAccess > denylist(
|
|
(css::configuration::theDefaultProvider::get(m_context)
|
|
->createInstanceWithArguments(
|
|
u"com.sun.star.configuration.ConfigurationAccess"_ustr, 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(u"Versions"_ustr);
|
|
|
|
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(u"Dependencies"_ustr);
|
|
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(u"com.sun.star.xml.dom.DocumentBuilder"_ustr, 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, u"desc:dependencies"_ustr));
|
|
|
|
// 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(
|
|
u"http://openoffice.org/extensions/description/2006"_ustr,
|
|
u"dependencies"_ustr), css::uno::UNO_QUERY_THROW);
|
|
m_element->appendChild(xNewDepNode);
|
|
xCurrentDeps = m_xpath->selectSingleNode(m_element, u"desc:dependencies"_ustr);
|
|
}
|
|
|
|
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( u"desc:version/@value"_ustr );
|
|
}
|
|
|
|
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 { u"all"_ustr };
|
|
}
|
|
|
|
//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, u"desc:platform"_ustr));
|
|
if (!nodePlatform.is())
|
|
{
|
|
return { u"all"_ustr };
|
|
}
|
|
|
|
//There is a platform element.
|
|
const OUString value = getNodeValueFromExpression(u"desc:platform/@value"_ustr);
|
|
//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, u"desc:dependencies/*"_ustr);
|
|
} catch (const css::xml::xpath::XPathException &) {
|
|
// ignore
|
|
}
|
|
}
|
|
return new EmptyNodeList;
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
DescriptionInfoset::getUpdateInformationUrls() const {
|
|
return getUrls(u"desc:update-information/desc:src/@xlink:href"_ustr);
|
|
}
|
|
|
|
css::uno::Sequence< OUString >
|
|
DescriptionInfoset::getUpdateDownloadUrls() const
|
|
{
|
|
return getUrls(u"desc:update-download/desc:src/@xlink:href"_ustr);
|
|
}
|
|
|
|
OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
|
|
{
|
|
css::uno::Sequence< OUString > aStrList = getUrls( u"desc:icon/desc:default/@xlink:href"_ustr );
|
|
css::uno::Sequence< OUString > aStrListHC = getUrls( u"desc:icon/desc:high-contrast/@xlink:href"_ustr );
|
|
|
|
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(u"/desc:description/desc:update-website"_ustr, &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(u"desc:publisher"_ustr);
|
|
|
|
OUString sPublisherName;
|
|
OUString sURL;
|
|
if (node.is())
|
|
{
|
|
css::uno::Reference< css::xml::dom::XNode > xPathName;
|
|
try {
|
|
xPathName = m_xpath->selectSingleNode(node, u"text()"_ustr);
|
|
} 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, u"@xlink:href"_ustr);
|
|
} 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(u"/desc:description/desc:release-notes"_ustr, nullptr);
|
|
}
|
|
|
|
OUString DescriptionInfoset::getLocalizedDisplayName() const
|
|
{
|
|
css::uno::Reference< css::xml::dom::XNode > node =
|
|
getLocalizedChild(u"desc:display-name"_ustr);
|
|
if (node.is())
|
|
{
|
|
css::uno::Reference< css::xml::dom::XNode > xtext;
|
|
try {
|
|
xtext = m_xpath->selectSingleNode(node, u"text()"_ustr);
|
|
} catch (const css::xml::xpath::XPathException &) {
|
|
// ignore
|
|
}
|
|
if (xtext.is())
|
|
return xtext->getNodeValue();
|
|
}
|
|
return OUString();
|
|
}
|
|
|
|
OUString DescriptionInfoset::getLocalizedLicenseURL() const
|
|
{
|
|
return getLocalizedHREFAttrFromChild(u"/desc:description/desc:registration/desc:simple-license"_ustr, 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, u"/desc:description/desc:registration/desc:simple-license/@accept-by"_ustr);
|
|
} catch (const css::xml::xpath::XPathException &) {
|
|
// ignore
|
|
}
|
|
if (n.is())
|
|
{
|
|
SimpleLicenseAttributes attributes;
|
|
attributes.acceptBy =
|
|
getNodeValueFromExpression(u"/desc:description/desc:registration/desc:simple-license/@accept-by"_ustr);
|
|
|
|
::std::optional< OUString > suppressOnUpdate = getOptionalValue(u"/desc:description/desc:registration/desc:simple-license/@suppress-on-update"_ustr);
|
|
if (suppressOnUpdate)
|
|
attributes.suppressOnUpdate = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressOnUpdate), u"true");
|
|
else
|
|
attributes.suppressOnUpdate = false;
|
|
|
|
::std::optional< OUString > suppressIfRequired = getOptionalValue(u"/desc:description/desc:registration/desc:simple-license/@suppress-if-required"_ustr);
|
|
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(u"/desc:description/desc:extension-description"_ustr, 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, u"@default-license-id"_ustr);
|
|
} 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, u"*[1]"_ustr);
|
|
} 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, u"@xlink:href"_ustr);
|
|
} 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: */
|