Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/desktop/source/deployment/misc/dp_descriptioninfoset.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include <string_view>
23
24
#include <dp_descriptioninfoset.hxx>
25
26
#include <dp_resource.h>
27
28
#include <comphelper/sequence.hxx>
29
#include <comphelper/processfactory.hxx>
30
#include <comphelper/propertysequence.hxx>
31
#include <optional>
32
#include <com/sun/star/configuration/theDefaultProvider.hpp>
33
#include <com/sun/star/container/XNameAccess.hpp>
34
#include <com/sun/star/deployment/DeploymentException.hpp>
35
#include <com/sun/star/beans/XPropertySet.hpp>
36
#include <com/sun/star/io/SequenceInputStream.hpp>
37
#include <com/sun/star/lang/XMultiComponentFactory.hpp>
38
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
39
#include <com/sun/star/task/XInteractionHandler.hpp>
40
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
41
#include <com/sun/star/uno/Reference.hxx>
42
#include <com/sun/star/uno/RuntimeException.hpp>
43
#include <com/sun/star/uno/Sequence.hxx>
44
#include <com/sun/star/uno/XInterface.hpp>
45
#include <com/sun/star/xml/dom/DOMException.hpp>
46
#include <com/sun/star/xml/dom/XNode.hpp>
47
#include <com/sun/star/xml/dom/XNodeList.hpp>
48
#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
49
#include <com/sun/star/xml/xpath/XPathAPI.hpp>
50
#include <com/sun/star/xml/xpath/XPathException.hpp>
51
#include <com/sun/star/ucb/InteractiveIOException.hpp>
52
#include <cppuhelper/implbase.hxx>
53
#include <cppuhelper/weak.hxx>
54
#include <cppuhelper/exc_hlp.hxx>
55
#include <rtl/ustring.hxx>
56
#include <sal/types.h>
57
#include <ucbhelper/content.hxx>
58
#include <o3tl/string_view.hxx>
59
60
namespace {
61
62
using css::uno::Reference;
63
64
class EmptyNodeList:
65
    public cppu::WeakImplHelper<css::xml::dom::XNodeList>
66
{
67
public:
68
    EmptyNodeList();
69
70
    EmptyNodeList(const EmptyNodeList&) = delete;
71
    const EmptyNodeList& operator=(const EmptyNodeList&) = delete;
72
73
    virtual ::sal_Int32 SAL_CALL getLength() override;
74
75
    virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
76
    item(::sal_Int32 index) override;
77
};
78
79
0
EmptyNodeList::EmptyNodeList() {}
80
81
0
::sal_Int32 EmptyNodeList::getLength() {
82
0
    return 0;
83
0
}
84
85
css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
86
0
{
87
0
    throw css::uno::RuntimeException(u"bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call"_ustr,
88
0
        static_cast< ::cppu::OWeakObject * >(this));
89
0
}
90
91
OUString getNodeValue(
92
    css::uno::Reference< css::xml::dom::XNode > const & node)
93
0
{
94
0
    OSL_ASSERT(node.is());
95
0
    try {
96
0
        return node->getNodeValue();
97
0
    } catch (const css::xml::dom::DOMException & e) {
98
0
        css::uno::Any anyEx = cppu::getCaughtException();
99
0
        throw css::lang::WrappedTargetRuntimeException(
100
0
            "com.sun.star.xml.dom.DOMException: " + e.Message,
101
0
            nullptr, anyEx );
102
0
    }
103
0
}
104
105
/**The class uses the UCB to access the description.xml file in an
106
   extension. The UCB must have been initialized already. It also
107
   requires that the extension has already be unzipped to a particular
108
   location.
109
 */
110
class ExtensionDescription
111
{
112
public:
113
    /**throws an exception if the description.xml is not
114
        available, cannot be read, does not contain the expected data,
115
        or any other error occurred. Therefore it should only be used with
116
        new extensions.
117
118
        Throws css::uno::RuntimeException,
119
        css::deployment::DeploymentException,
120
        dp_registry::backend::bundle::NoDescriptionException.
121
     */
122
    ExtensionDescription(
123
        const css::uno::Reference<css::uno::XComponentContext>& xContext,
124
        std::u16string_view installDir,
125
        const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
126
127
    const css::uno::Reference<css::xml::dom::XNode>& getRootElement() const
128
0
    {
129
0
        return m_xRoot;
130
0
    }
131
132
private:
133
    css::uno::Reference<css::xml::dom::XNode> m_xRoot;
134
};
135
136
class NoDescriptionException
137
{
138
};
139
140
class FileDoesNotExistFilter
141
    : public ::cppu::WeakImplHelper< css::ucb::XCommandEnvironment,
142
                                      css::task::XInteractionHandler >
143
144
{
145
    bool m_bExist;
146
    css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
147
148
public:
149
    explicit FileDoesNotExistFilter(
150
        const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
151
152
0
    bool exist() { return m_bExist;}
153
    // XCommandEnvironment
154
    virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
155
    getInteractionHandler() override;
156
    virtual css::uno::Reference<css::ucb::XProgressHandler >
157
    SAL_CALL getProgressHandler() override;
158
159
    // XInteractionHandler
160
    virtual void SAL_CALL handle(
161
        css::uno::Reference<css::task::XInteractionRequest > const & xRequest ) override;
162
};
163
164
ExtensionDescription::ExtensionDescription(
165
    const Reference<css::uno::XComponentContext>& xContext,
166
    std::u16string_view installDir,
167
    const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
168
0
{
169
0
    try {
170
        //may throw css::ucb::ContentCreationException
171
        //If there is no description.xml then ucb will start an interaction which
172
        //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
173
        //and filter the respective exception out.
174
0
        OUString sDescriptionUri(OUString::Concat(installDir) + "/description.xml");
175
0
        Reference<css::ucb::XCommandEnvironment> xFilter = new FileDoesNotExistFilter(xCmdEnv);
176
0
        ::ucbhelper::Content descContent(sDescriptionUri, xFilter, xContext);
177
178
        //throws a css::uno::Exception if the file is not available
179
0
        Reference<css::io::XInputStream> xIn;
180
0
        try
181
0
        {   //throws com.sun.star.ucb.InteractiveIOException
182
0
            xIn = descContent.openStream();
183
0
        }
184
0
        catch ( const css::uno::Exception& )
185
0
        {
186
0
            if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
187
0
                throw NoDescriptionException();
188
0
            throw;
189
0
        }
190
0
        if (!xIn.is())
191
0
        {
192
0
            throw css::uno::Exception(
193
0
                "Could not get XInputStream for description.xml of extension " +
194
0
                sDescriptionUri, nullptr);
195
0
        }
196
197
        //get root node of description.xml
198
0
        Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
199
0
            css::xml::dom::DocumentBuilder::create(xContext) );
200
201
0
        if (!xDocBuilder->isNamespaceAware())
202
0
        {
203
0
            throw css::uno::Exception(
204
0
                u"Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware."_ustr, nullptr);
205
0
        }
206
207
0
        Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
208
0
        if (!xDoc.is())
209
0
        {
210
0
            throw css::uno::Exception(sDescriptionUri + " contains data which cannot be parsed. ", nullptr);
211
0
        }
212
213
        //check for proper root element and namespace
214
0
        Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
215
0
        if (!xRoot.is())
216
0
        {
217
0
            throw css::uno::Exception(
218
0
                sDescriptionUri + " contains no root element.", nullptr);
219
0
        }
220
221
0
        if ( xRoot->getTagName() != "description")
222
0
        {
223
0
            throw css::uno::Exception(
224
0
                sDescriptionUri + " does not contain the root element <description>.", nullptr);
225
0
        }
226
227
0
        m_xRoot.set(xRoot, css::uno::UNO_QUERY_THROW);
228
0
        OUString nsDescription = xRoot->getNamespaceURI();
229
230
        //check if this namespace is supported
231
0
        if ( nsDescription != "http://openoffice.org/extensions/description/2006")
232
0
        {
233
0
            throw css::uno::Exception(sDescriptionUri + " contains a root element with an unsupported namespace. ", nullptr);
234
0
        }
235
0
    } catch (const css::uno::RuntimeException &) {
236
0
        throw;
237
0
    } catch (const css::deployment::DeploymentException &) {
238
0
        throw;
239
0
    } catch (const css::uno::Exception & e) {
240
0
        css::uno::Any a(cppu::getCaughtException());
241
0
        throw css::deployment::DeploymentException(
242
0
            e.Message, Reference< css::uno::XInterface >(), a);
243
0
    }
244
0
}
245
246
FileDoesNotExistFilter::FileDoesNotExistFilter(
247
    const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
248
0
    m_bExist(true), m_xCommandEnv(xCmdEnv)
249
0
{}
250
251
    // XCommandEnvironment
252
Reference<css::task::XInteractionHandler >
253
    FileDoesNotExistFilter::getInteractionHandler()
254
0
{
255
0
    return static_cast<css::task::XInteractionHandler*>(this);
256
0
}
257
258
Reference<css::ucb::XProgressHandler >
259
    FileDoesNotExistFilter::getProgressHandler()
260
0
{
261
0
    return m_xCommandEnv.is()
262
0
        ? m_xCommandEnv->getProgressHandler()
263
0
        : Reference<css::ucb::XProgressHandler>();
264
0
}
265
266
// XInteractionHandler
267
//If the interaction was caused by a non-existing file which is specified in the ctor
268
//of FileDoesNotExistFilter, then we do nothing
269
void  FileDoesNotExistFilter::handle(
270
        Reference<css::task::XInteractionRequest > const & xRequest )
271
0
{
272
0
    css::uno::Any request( xRequest->getRequest() );
273
274
0
    css::ucb::InteractiveIOException ioexc;
275
0
    if ((request>>= ioexc)
276
0
        && (ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING
277
0
            || ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING_PATH))
278
0
    {
279
0
        m_bExist = false;
280
0
        return;
281
0
    }
282
0
    Reference<css::task::XInteractionHandler> xInteraction;
283
0
    if (m_xCommandEnv.is()) {
284
0
        xInteraction = m_xCommandEnv->getInteractionHandler();
285
0
    }
286
0
    if (xInteraction.is()) {
287
0
        xInteraction->handle(xRequest);
288
0
    }
289
0
}
290
291
}
292
293
namespace dp_misc {
294
295
DescriptionInfoset getDescriptionInfoset(std::u16string_view sExtensionFolderURL)
296
0
{
297
0
    Reference< css::xml::dom::XNode > root;
298
0
    const Reference<css::uno::XComponentContext>& context(
299
0
        comphelper::getProcessComponentContext());
300
0
    try {
301
0
        root =
302
0
            ExtensionDescription(
303
0
                context, sExtensionFolderURL,
304
0
                Reference< css::ucb::XCommandEnvironment >()).
305
0
            getRootElement();
306
0
    } catch (const NoDescriptionException &) {
307
0
    } catch (const css::deployment::DeploymentException & e) {
308
0
        css::uno::Any anyEx = cppu::getCaughtException();
309
0
        throw css::lang::WrappedTargetRuntimeException(
310
0
            "com.sun.star.deployment.DeploymentException: " + e.Message,
311
0
            nullptr, anyEx );
312
0
    }
313
0
    return DescriptionInfoset(context, root);
314
0
}
315
316
DescriptionInfoset::DescriptionInfoset(
317
    css::uno::Reference< css::uno::XComponentContext > const & context,
318
    css::uno::Reference< css::xml::dom::XNode > const & element):
319
0
    m_context(context),
320
0
    m_element(element)
321
0
{
322
0
    if (m_element.is()) {
323
0
        m_xpath = css::xml::xpath::XPathAPI::create(context);
324
0
        m_xpath->registerNS(u"desc"_ustr, element->getNamespaceURI());
325
0
        m_xpath->registerNS(u"xlink"_ustr, u"http://www.w3.org/1999/xlink"_ustr);
326
0
    }
327
0
}
328
329
0
DescriptionInfoset::~DescriptionInfoset() {}
330
331
0
::std::optional< OUString > DescriptionInfoset::getIdentifier() const {
332
0
    return getOptionalValue(u"desc:identifier/@value"_ustr);
333
0
}
334
335
OUString DescriptionInfoset::getNodeValueFromExpression(OUString const & expression) const
336
0
{
337
0
    css::uno::Reference< css::xml::dom::XNode > n;
338
0
    if (m_element.is()) {
339
0
        try {
340
0
            n = m_xpath->selectSingleNode(m_element, expression);
341
0
        } catch (const css::xml::xpath::XPathException &) {
342
            // ignore
343
0
        }
344
0
    }
345
0
    return n.is() ? getNodeValue(n) : OUString();
346
0
}
347
348
void DescriptionInfoset::checkDenylist() const
349
0
{
350
0
    if (!m_element.is())
351
0
        return;
352
353
0
    std::optional< OUString > id(getIdentifier());
354
0
    if (!id)
355
0
        return; // nothing to check
356
0
    OUString currentversion(getVersion());
357
0
    if (currentversion.getLength() == 0)
358
0
        return;  // nothing to check
359
360
0
    css::uno::Sequence<css::uno::Any> args(comphelper::InitAnyPropertySequence(
361
0
    {
362
0
        {"nodepath", css::uno::Any(u"/org.openoffice.Office.ExtensionDependencies/Extensions"_ustr)}
363
0
    }));
364
0
    css::uno::Reference< css::container::XNameAccess > denylist(
365
0
        (css::configuration::theDefaultProvider::get(m_context)
366
0
         ->createInstanceWithArguments(
367
0
             u"com.sun.star.configuration.ConfigurationAccess"_ustr, args)),
368
0
        css::uno::UNO_QUERY_THROW);
369
370
    // check first if a denylist entry is available
371
0
    if (!(denylist.is() && denylist->hasByName(*id)))        return;
372
373
0
    css::uno::Reference< css::beans::XPropertySet > extProps(
374
0
        denylist->getByName(*id), css::uno::UNO_QUERY_THROW);
375
376
0
    css::uno::Any anyValue = extProps->getPropertyValue(u"Versions"_ustr);
377
378
0
    css::uno::Sequence< OUString > blversions;
379
0
    anyValue >>= blversions;
380
381
    // check if the current version requires further dependency checks from the denylist
382
0
    if (!checkDenylistVersion(currentversion, blversions))        return;
383
384
0
    anyValue = extProps->getPropertyValue(u"Dependencies"_ustr);
385
0
    OUString udeps;
386
0
    anyValue >>= udeps;
387
388
0
    if (udeps.getLength() == 0)
389
0
        return; // nothing todo
390
391
0
    OString xmlDependencies = OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
392
393
0
    css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
394
0
        m_context->getServiceManager()->createInstanceWithContext(u"com.sun.star.xml.dom.DocumentBuilder"_ustr, m_context),
395
0
        css::uno::UNO_QUERY_THROW);
396
397
0
    css::uno::Sequence< sal_Int8 > byteSeq(reinterpret_cast<const sal_Int8*>(xmlDependencies.getStr()), xmlDependencies.getLength());
398
399
0
    css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
400
0
                                                             css::uno::UNO_QUERY_THROW);
401
402
0
    css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
403
0
    css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
404
0
    css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
405
0
    sal_Int32 nLen = xDeps->getLength();
406
407
    // get the parent xml document  of current description info for the import
408
0
    css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
409
410
    // get dependency node of current description info to merge the new dependencies from the denylist
411
0
    css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
412
0
        m_xpath->selectSingleNode(m_element, u"desc:dependencies"_ustr));
413
414
    // if no dependency node exists, create a new one in the current description info
415
0
    if (!xCurrentDeps.is()) {
416
0
        css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
417
0
            xCurrentDescInfo->createElementNS(
418
0
                u"http://openoffice.org/extensions/description/2006"_ustr,
419
0
                u"dependencies"_ustr), css::uno::UNO_QUERY_THROW);
420
0
        m_element->appendChild(xNewDepNode);
421
0
        xCurrentDeps = m_xpath->selectSingleNode(m_element, u"desc:dependencies"_ustr);
422
0
    }
423
424
0
    for (sal_Int32 i=0; i<nLen; i++) {
425
0
        css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
426
0
        css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
427
0
        if (xDep.is()) {
428
            // found valid denylist dependency, import the node first and append it to the existing dependency node
429
0
            css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
430
0
            xCurrentDeps->appendChild(importedNode);
431
0
        }
432
0
    }
433
0
}
434
435
bool DescriptionInfoset::checkDenylistVersion(
436
    std::u16string_view currentversion,
437
    css::uno::Sequence< OUString > const & versions)
438
0
{
439
0
    sal_Int32 nLen = versions.getLength();
440
0
    for (sal_Int32 i=0; i<nLen; i++) {
441
0
        if (currentversion == versions[i])
442
0
            return true;
443
0
    }
444
445
0
    return false;
446
0
}
447
448
OUString DescriptionInfoset::getVersion() const
449
0
{
450
0
    return getNodeValueFromExpression( u"desc:version/@value"_ustr );
451
0
}
452
453
css::uno::Sequence< OUString > DescriptionInfoset::getSupportedPlatforms() const
454
0
{
455
    //When there is no description.xml then we assume that we support all platforms
456
0
    if (! m_element.is())
457
0
    {
458
0
        return { u"all"_ustr };
459
0
    }
460
461
    //Check if the <platform> element was provided. If not the default is "all" platforms
462
0
    css::uno::Reference< css::xml::dom::XNode > nodePlatform(
463
0
        m_xpath->selectSingleNode(m_element, u"desc:platform"_ustr));
464
0
    if (!nodePlatform.is())
465
0
    {
466
0
        return { u"all"_ustr };
467
0
    }
468
469
    //There is a platform element.
470
0
    const OUString value = getNodeValueFromExpression(u"desc:platform/@value"_ustr);
471
    //parse the string, it can contained multiple strings separated by commas
472
0
    std::vector< OUString> vec;
473
0
    sal_Int32 nIndex = 0;
474
0
    do
475
0
    {
476
0
        const OUString aToken( o3tl::trim(o3tl::getToken(value, 0, ',', nIndex )) );
477
0
        if (!aToken.isEmpty())
478
0
            vec.push_back(aToken);
479
480
0
    }
481
0
    while (nIndex >= 0);
482
483
0
    return comphelper::containerToSequence(vec);
484
0
}
485
486
css::uno::Reference< css::xml::dom::XNodeList >
487
0
DescriptionInfoset::getDependencies() const {
488
0
    if (m_element.is()) {
489
0
        try {
490
            // check the extension denylist first and expand the dependencies if applicable
491
0
            checkDenylist();
492
493
0
            return m_xpath->selectNodeList(m_element, u"desc:dependencies/*"_ustr);
494
0
        } catch (const css::xml::xpath::XPathException &) {
495
            // ignore
496
0
        }
497
0
    }
498
0
    return new EmptyNodeList;
499
0
}
500
501
css::uno::Sequence< OUString >
502
0
DescriptionInfoset::getUpdateInformationUrls() const {
503
0
    return getUrls(u"desc:update-information/desc:src/@xlink:href"_ustr);
504
0
}
505
506
css::uno::Sequence< OUString >
507
DescriptionInfoset::getUpdateDownloadUrls() const
508
0
{
509
0
    return getUrls(u"desc:update-download/desc:src/@xlink:href"_ustr);
510
0
}
511
512
OUString DescriptionInfoset::getIconURL( bool bHighContrast ) const
513
0
{
514
0
    css::uno::Sequence< OUString > aStrList = getUrls( u"desc:icon/desc:default/@xlink:href"_ustr );
515
0
    css::uno::Sequence< OUString > aStrListHC = getUrls( u"desc:icon/desc:high-contrast/@xlink:href"_ustr );
516
517
0
    if ( bHighContrast && aStrListHC.hasElements() && !aStrListHC[0].isEmpty() )
518
0
        return aStrListHC[0];
519
520
0
    if ( aStrList.hasElements() && !aStrList[0].isEmpty() )
521
0
        return aStrList[0];
522
523
0
    return OUString();
524
0
}
525
526
::std::optional< OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
527
    const
528
0
{
529
0
    bool bParentExists = false;
530
0
    const OUString sURL (getLocalizedHREFAttrFromChild(u"/desc:description/desc:update-website"_ustr, &bParentExists ));
531
532
0
    if (!sURL.isEmpty())
533
0
        return ::std::optional< OUString >(sURL);
534
0
    else
535
0
        return bParentExists ? ::std::optional< OUString >(OUString()) :
536
0
            ::std::optional< OUString >();
537
0
}
538
539
::std::optional< OUString > DescriptionInfoset::getOptionalValue(
540
    OUString const & expression) const
541
0
{
542
0
    css::uno::Reference< css::xml::dom::XNode > n;
543
0
    if (m_element.is()) {
544
0
        try {
545
0
            n = m_xpath->selectSingleNode(m_element, expression);
546
0
        } catch (const css::xml::xpath::XPathException &) {
547
            // ignore
548
0
        }
549
0
    }
550
0
    return n.is()
551
0
        ? ::std::optional< OUString >(getNodeValue(n))
552
0
        : ::std::optional< OUString >();
553
0
}
554
555
css::uno::Sequence< OUString > DescriptionInfoset::getUrls(
556
    OUString const & expression) const
557
0
{
558
0
    css::uno::Reference< css::xml::dom::XNodeList > ns;
559
0
    if (m_element.is()) {
560
0
        try {
561
0
            ns = m_xpath->selectNodeList(m_element, expression);
562
0
        } catch (const css::xml::xpath::XPathException &) {
563
            // ignore
564
0
        }
565
0
    }
566
0
    css::uno::Sequence< OUString > urls(ns.is() ? ns->getLength() : 0);
567
0
    auto urlsRange = asNonConstRange(urls);
568
0
    for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
569
0
        urlsRange[i] = getNodeValue(ns->item(i));
570
0
    }
571
0
    return urls;
572
0
}
573
574
std::pair< OUString, OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
575
0
{
576
0
    css::uno::Reference< css::xml::dom::XNode > node =
577
0
        getLocalizedChild(u"desc:publisher"_ustr);
578
579
0
    OUString sPublisherName;
580
0
    OUString sURL;
581
0
    if (node.is())
582
0
    {
583
0
        css::uno::Reference< css::xml::dom::XNode > xPathName;
584
0
        try {
585
0
            xPathName = m_xpath->selectSingleNode(node, u"text()"_ustr);
586
0
        } catch (const css::xml::xpath::XPathException &) {
587
            // ignore
588
0
        }
589
0
        OSL_ASSERT(xPathName.is());
590
0
        if (xPathName.is())
591
0
            sPublisherName = xPathName->getNodeValue();
592
593
0
        css::uno::Reference< css::xml::dom::XNode > xURL;
594
0
        try {
595
0
            xURL = m_xpath->selectSingleNode(node, u"@xlink:href"_ustr);
596
0
        } catch (const css::xml::xpath::XPathException &) {
597
            // ignore
598
0
        }
599
0
        OSL_ASSERT(xURL.is());
600
0
        if (xURL.is())
601
0
           sURL = xURL->getNodeValue();
602
0
    }
603
0
    return std::make_pair(sPublisherName, sURL);
604
0
}
605
606
OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
607
0
{
608
0
    return getLocalizedHREFAttrFromChild(u"/desc:description/desc:release-notes"_ustr, nullptr);
609
0
}
610
611
OUString DescriptionInfoset::getLocalizedDisplayName() const
612
0
{
613
0
    css::uno::Reference< css::xml::dom::XNode > node =
614
0
        getLocalizedChild(u"desc:display-name"_ustr);
615
0
    if (node.is())
616
0
    {
617
0
        css::uno::Reference< css::xml::dom::XNode > xtext;
618
0
        try {
619
0
            xtext = m_xpath->selectSingleNode(node, u"text()"_ustr);
620
0
        } catch (const css::xml::xpath::XPathException &) {
621
            // ignore
622
0
        }
623
0
        if (xtext.is())
624
0
            return xtext->getNodeValue();
625
0
    }
626
0
    return OUString();
627
0
}
628
629
OUString DescriptionInfoset::getLocalizedLicenseURL() const
630
0
{
631
0
    return getLocalizedHREFAttrFromChild(u"/desc:description/desc:registration/desc:simple-license"_ustr, nullptr);
632
633
0
}
634
635
::std::optional<SimpleLicenseAttributes>
636
DescriptionInfoset::getSimpleLicenseAttributes() const
637
0
{
638
    //Check if the node exist
639
0
    css::uno::Reference< css::xml::dom::XNode > n;
640
0
    if (m_element.is()) {
641
0
        try {
642
0
            n = m_xpath->selectSingleNode(m_element, u"/desc:description/desc:registration/desc:simple-license/@accept-by"_ustr);
643
0
        } catch (const css::xml::xpath::XPathException &) {
644
            // ignore
645
0
        }
646
0
        if (n.is())
647
0
        {
648
0
            SimpleLicenseAttributes attributes;
649
0
            attributes.acceptBy =
650
0
                getNodeValueFromExpression(u"/desc:description/desc:registration/desc:simple-license/@accept-by"_ustr);
651
652
0
            ::std::optional< OUString > suppressOnUpdate = getOptionalValue(u"/desc:description/desc:registration/desc:simple-license/@suppress-on-update"_ustr);
653
0
            if (suppressOnUpdate)
654
0
                attributes.suppressOnUpdate = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressOnUpdate), u"true");
655
0
            else
656
0
                attributes.suppressOnUpdate = false;
657
658
0
            ::std::optional< OUString > suppressIfRequired = getOptionalValue(u"/desc:description/desc:registration/desc:simple-license/@suppress-if-required"_ustr);
659
0
            if (suppressIfRequired)
660
0
                attributes.suppressIfRequired = o3tl::equalsIgnoreAsciiCase(o3tl::trim(*suppressIfRequired), u"true");
661
0
            else
662
0
                attributes.suppressIfRequired = false;
663
664
0
            return ::std::optional<SimpleLicenseAttributes>(attributes);
665
0
        }
666
0
    }
667
0
    return ::std::optional<SimpleLicenseAttributes>();
668
0
}
669
670
OUString DescriptionInfoset::getLocalizedDescriptionURL() const
671
0
{
672
0
    return getLocalizedHREFAttrFromChild(u"/desc:description/desc:extension-description"_ustr, nullptr);
673
0
}
674
675
css::uno::Reference< css::xml::dom::XNode >
676
DescriptionInfoset::getLocalizedChild( const OUString & sParent) const
677
0
{
678
0
    if ( ! m_element.is() || sParent.isEmpty())
679
0
        return css::uno::Reference< css::xml::dom::XNode > ();
680
681
0
    css::uno::Reference< css::xml::dom::XNode > xParent;
682
0
    try {
683
0
        xParent = m_xpath->selectSingleNode(m_element, sParent);
684
0
    } catch (const css::xml::xpath::XPathException &) {
685
        // ignore
686
0
    }
687
0
    css::uno::Reference<css::xml::dom::XNode> nodeMatch;
688
0
    if (xParent.is())
689
0
    {
690
0
        nodeMatch = matchLanguageTag(xParent, getOfficeLanguageTag().getBcp47());
691
692
        //office: en-DE, en, en-DE-altmark
693
0
        if (! nodeMatch.is())
694
0
        {
695
            // Already tried full tag, continue with first fallback.
696
0
            const std::vector< OUString > aFallbacks( getOfficeLanguageTag().getFallbackStrings( false));
697
0
            for (auto const& fallback : aFallbacks)
698
0
            {
699
0
                nodeMatch = matchLanguageTag(xParent, fallback);
700
0
                if (nodeMatch.is())
701
0
                    break;
702
0
            }
703
0
            if (! nodeMatch.is())
704
0
                nodeMatch = getChildWithDefaultLocale(xParent);
705
0
        }
706
0
    }
707
708
0
    return nodeMatch;
709
0
}
710
711
css::uno::Reference<css::xml::dom::XNode>
712
DescriptionInfoset::matchLanguageTag(
713
    css::uno::Reference< css::xml::dom::XNode > const & xParent, std::u16string_view rTag) const
714
0
{
715
0
    OSL_ASSERT(xParent.is());
716
0
    css::uno::Reference<css::xml::dom::XNode> nodeMatch;
717
718
    //first try exact match for lang
719
0
    const OUString exp1(OUString::Concat("*[@lang=\"") + rTag + "\"]");
720
0
    try {
721
0
        nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
722
0
    } catch (const css::xml::xpath::XPathException &) {
723
        // ignore
724
0
    }
725
726
    //try to match in strings that also have a country and/or variant, for
727
    //example en  matches in en-US-montana, en-US, en-montana
728
0
    if (!nodeMatch.is())
729
0
    {
730
0
        const OUString exp2(
731
0
            OUString::Concat("*[starts-with(@lang,\"") + rTag + "-\")]");
732
0
        try {
733
0
            nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
734
0
        } catch (const css::xml::xpath::XPathException &) {
735
            // ignore
736
0
        }
737
0
    }
738
0
    return nodeMatch;
739
0
}
740
741
css::uno::Reference<css::xml::dom::XNode>
742
DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
743
                                    const & xParent) const
744
0
{
745
0
    OSL_ASSERT(xParent.is());
746
0
    if ( xParent->getNodeName() == "simple-license" )
747
0
    {
748
0
        css::uno::Reference<css::xml::dom::XNode> nodeDefault;
749
0
        try {
750
0
            nodeDefault = m_xpath->selectSingleNode(xParent, u"@default-license-id"_ustr);
751
0
        } catch (const css::xml::xpath::XPathException &) {
752
            // ignore
753
0
        }
754
0
        if (nodeDefault.is())
755
0
        {
756
            //The old way
757
0
            const OUString exp1("desc:license-text[@license-id = \""
758
0
                + nodeDefault->getNodeValue()
759
0
                + "\"]");
760
0
            try {
761
0
                return m_xpath->selectSingleNode(xParent, exp1);
762
0
            } catch (const css::xml::xpath::XPathException &) {
763
                // ignore
764
0
            }
765
0
        }
766
0
    }
767
768
0
    try {
769
0
        return m_xpath->selectSingleNode(xParent, u"*[1]"_ustr);
770
0
    } catch (const css::xml::xpath::XPathException &) {
771
        // ignore
772
0
        return nullptr;
773
0
    }
774
0
}
775
776
OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
777
    OUString const & sXPathParent, bool * out_bParentExists)
778
    const
779
0
{
780
0
    css::uno::Reference< css::xml::dom::XNode > node =
781
0
        getLocalizedChild(sXPathParent);
782
783
0
    OUString sURL;
784
0
    if (node.is())
785
0
    {
786
0
        if (out_bParentExists)
787
0
            *out_bParentExists = true;
788
0
        css::uno::Reference< css::xml::dom::XNode > xURL;
789
0
        try {
790
0
            xURL = m_xpath->selectSingleNode(node, u"@xlink:href"_ustr);
791
0
        } catch (const css::xml::xpath::XPathException &) {
792
            // ignore
793
0
        }
794
0
        OSL_ASSERT(xURL.is());
795
0
        if (xURL.is())
796
0
            sURL = xURL->getNodeValue();
797
0
    }
798
0
    else
799
0
    {
800
0
        if (out_bParentExists)
801
0
            *out_bParentExists = false;
802
0
    }
803
0
    return sURL;
804
0
}
805
806
}
807
808
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */