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