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