1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_desktop.hxx"
30 
31 #include "dp_descriptioninfoset.hxx"
32 
33 #include "dp_resource.h"
34 #include "sal/config.h"
35 
36 #include "comphelper/sequence.hxx"
37 #include "comphelper/seqstream.hxx"
38 #include "comphelper/makesequence.hxx"
39 #include "comphelper/processfactory.hxx"
40 #include "boost/optional.hpp"
41 #include "com/sun/star/container/XNameAccess.hpp"
42 #include "com/sun/star/beans/Optional.hpp"
43 #include "com/sun/star/beans/PropertyValue.hpp"
44 #include "com/sun/star/beans/XPropertySet.hpp"
45 #include "com/sun/star/io/SequenceInputStream.hpp"
46 #include "com/sun/star/lang/XMultiComponentFactory.hpp"
47 #include "com/sun/star/lang/Locale.hpp"
48 #include "com/sun/star/uno/Reference.hxx"
49 #include "com/sun/star/uno/RuntimeException.hpp"
50 #include "com/sun/star/uno/Sequence.hxx"
51 #include "com/sun/star/uno/XComponentContext.hpp"
52 #include "com/sun/star/uno/XInterface.hpp"
53 #include "com/sun/star/xml/dom/DOMException.hpp"
54 #include "com/sun/star/xml/dom/XNode.hpp"
55 #include "com/sun/star/xml/dom/XNodeList.hpp"
56 #include "com/sun/star/xml/dom/XDocumentBuilder.hpp"
57 #include "com/sun/star/xml/xpath/XXPathAPI.hpp"
58 #include "com/sun/star/ucb/InteractiveAugmentedIOException.hpp"
59 #include "cppuhelper/implbase1.hxx"
60 #include "cppuhelper/implbase2.hxx"
61 #include "cppuhelper/weak.hxx"
62 #include "cppuhelper/exc_hlp.hxx"
63 #include "rtl/ustring.h"
64 #include "rtl/ustring.hxx"
65 #include "sal/types.h"
66 #include "ucbhelper/content.hxx"
67 
68 namespace {
69 
70 namespace css = ::com::sun::star;
71 using css::uno::Reference;
72 using ::rtl::OUString;
73 
74 class EmptyNodeList: public ::cppu::WeakImplHelper1< css::xml::dom::XNodeList >
75 {
76 public:
77     EmptyNodeList();
78 
79     virtual ~EmptyNodeList();
80 
81     virtual ::sal_Int32 SAL_CALL getLength() throw (css::uno::RuntimeException);
82 
83     virtual css::uno::Reference< css::xml::dom::XNode > SAL_CALL
84     item(::sal_Int32 index) throw (css::uno::RuntimeException);
85 
86 private:
87     EmptyNodeList(EmptyNodeList &); // not defined
88     void operator =(EmptyNodeList &); // not defined
89 };
90 
91 EmptyNodeList::EmptyNodeList() {}
92 
93 EmptyNodeList::~EmptyNodeList() {}
94 
95 ::sal_Int32 EmptyNodeList::getLength() throw (css::uno::RuntimeException) {
96     return 0;
97 }
98 
99 css::uno::Reference< css::xml::dom::XNode > EmptyNodeList::item(::sal_Int32)
100     throw (css::uno::RuntimeException)
101 {
102     throw css::uno::RuntimeException(
103         ::rtl::OUString(
104             RTL_CONSTASCII_USTRINGPARAM(
105                 "bad EmptyNodeList com.sun.star.xml.dom.XNodeList.item call")),
106         static_cast< ::cppu::OWeakObject * >(this));
107 }
108 
109 ::rtl::OUString getNodeValue(
110     css::uno::Reference< css::xml::dom::XNode > const & node)
111 {
112     OSL_ASSERT(node.is());
113     try {
114         return node->getNodeValue();
115     } catch (css::xml::dom::DOMException & e) {
116         throw css::uno::RuntimeException(
117             (::rtl::OUString(
118                 RTL_CONSTASCII_USTRINGPARAM(
119                     "com.sun.star.xml.dom.DOMException: ")) +
120              e.Message),
121             css::uno::Reference< css::uno::XInterface >());
122     }
123 }
124 
125 /**The class uses the UCB to access the description.xml file in an
126    extension. The UCB must have been initialized already. It also
127    requires that the extension has already be unzipped to a particular
128    location.
129  */
130 class ExtensionDescription
131 {
132 public:
133     /**throws an exception if the description.xml is not
134 		available, cannot be read, does not contain the expected data,
135 		or any other error occured. Therefore it shoult only be used with
136 		new extensions.
137 
138         Throws com::sun::star::uno::RuntimeException,
139         com::sun::star::deployment::DeploymentException,
140         dp_registry::backend::bundle::NoDescriptionException.
141      */
142     ExtensionDescription(
143         const css::uno::Reference<css::uno::XComponentContext>& xContext,
144         const ::rtl::OUString& installDir,
145         const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
146 
147 	~ExtensionDescription();
148 
149 	css::uno::Reference<css::xml::dom::XNode> getRootElement() const
150 	{
151 		return m_xRoot;
152 	}
153 
154 	::rtl::OUString getExtensionRootUrl() const
155 	{
156 		return m_sExtensionRootUrl;
157 	}
158 
159 
160 private:
161 	css::uno::Reference<css::xml::dom::XNode> m_xRoot;
162 	::rtl::OUString m_sExtensionRootUrl;
163 };
164 
165 class NoDescriptionException
166 {
167 };
168 
169 class FileDoesNotExistFilter
170     : public ::cppu::WeakImplHelper2< css::ucb::XCommandEnvironment,
171                                       css::task::XInteractionHandler >
172 
173 {
174     //css::uno::Reference<css::task::XInteractionHandler> m_xHandler;
175 	bool m_bExist;
176 	css::uno::Reference< css::ucb::XCommandEnvironment > m_xCommandEnv;
177 
178 public:
179     virtual ~FileDoesNotExistFilter();
180 	FileDoesNotExistFilter(
181 		const css::uno::Reference< css::ucb::XCommandEnvironment >& xCmdEnv);
182 
183 	bool exist();
184     // XCommandEnvironment
185     virtual css::uno::Reference<css::task::XInteractionHandler > SAL_CALL
186     getInteractionHandler() throw (css::uno::RuntimeException);
187     virtual css::uno::Reference<css::ucb::XProgressHandler >
188     SAL_CALL getProgressHandler() throw (css::uno::RuntimeException);
189 
190     // XInteractionHandler
191     virtual void SAL_CALL handle(
192         css::uno::Reference<css::task::XInteractionRequest > const & xRequest )
193         throw (css::uno::RuntimeException);
194 };
195 
196 ExtensionDescription::ExtensionDescription(
197     const Reference<css::uno::XComponentContext>& xContext,
198     const OUString& installDir,
199     const Reference< css::ucb::XCommandEnvironment >& xCmdEnv)
200 {
201     try {
202         m_sExtensionRootUrl = installDir;
203         //may throw ::com::sun::star::ucb::ContentCreationException
204         //If there is no description.xml then ucb will start an interaction which
205         //brings up a dialog.We want to prevent this. Therefore we wrap the xCmdEnv
206         //and filter the respective exception out.
207         OUString sDescriptionUri(installDir + OUSTR("/description.xml"));
208         Reference<css::ucb::XCommandEnvironment> xFilter =
209             static_cast<css::ucb::XCommandEnvironment*>(
210                 new FileDoesNotExistFilter(xCmdEnv));
211         ::ucbhelper::Content descContent(sDescriptionUri, xFilter);
212 
213         //throws an com::sun::star::uno::Exception if the file is not available
214         Reference<css::io::XInputStream> xIn;
215         try
216         {	//throws com.sun.star.ucb.InteractiveAugmentedIOException
217             xIn = descContent.openStream();
218         }
219         catch (css::uno::Exception& )
220         {
221             if ( ! static_cast<FileDoesNotExistFilter*>(xFilter.get())->exist())
222                 throw NoDescriptionException();
223             throw;
224         }
225         if (!xIn.is())
226         {
227             throw css::uno::Exception(
228                 OUSTR("Could not get XInputStream for description.xml of extension ") +
229                 sDescriptionUri, 0);
230         }
231 
232         //get root node of description.xml
233         Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
234             xContext->getServiceManager()->createInstanceWithContext(
235                 OUSTR("com.sun.star.xml.dom.DocumentBuilder"),
236                 xContext ), css::uno::UNO_QUERY);
237         if (!xDocBuilder.is())
238             throw css::uno::Exception(OUSTR(" Could not create service com.sun.star.xml.dom.DocumentBuilder"), 0);
239 
240         if (xDocBuilder->isNamespaceAware() == sal_False)
241         {
242             throw css::uno::Exception(
243                 OUSTR("Service com.sun.star.xml.dom.DocumentBuilder is not namespace aware."), 0);
244         }
245 
246         Reference<css::xml::dom::XDocument> xDoc = xDocBuilder->parse(xIn);
247         if (!xDoc.is())
248         {
249             throw css::uno::Exception(sDescriptionUri + OUSTR(" contains data which cannot be parsed. "), 0);
250         }
251 
252         //check for proper root element and namespace
253         Reference<css::xml::dom::XElement> xRoot = xDoc->getDocumentElement();
254         if (!xRoot.is())
255         {
256             throw css::uno::Exception(
257                 sDescriptionUri + OUSTR(" contains no root element."), 0);
258         }
259 
260         if ( ! xRoot->getTagName().equals(OUSTR("description")))
261         {
262             throw css::uno::Exception(
263                 sDescriptionUri + OUSTR(" does not contain the root element <description>."), 0);
264         }
265 
266         m_xRoot = Reference<css::xml::dom::XNode>(
267             xRoot, css::uno::UNO_QUERY_THROW);
268         OUString nsDescription = xRoot->getNamespaceURI();
269 
270         //check if this namespace is supported
271         if ( ! nsDescription.equals(OUSTR("http://openoffice.org/extensions/description/2006")))
272         {
273             throw css::uno::Exception(sDescriptionUri + OUSTR(" contains a root element with an unsupported namespace. "), 0);
274         }
275     } catch (css::uno::RuntimeException &) {
276         throw;
277     } catch (css::deployment::DeploymentException &) {
278         throw;
279     } catch (css::uno::Exception & e) {
280         css::uno::Any a(cppu::getCaughtException());
281         throw css::deployment::DeploymentException(
282 			e.Message, Reference< css::uno::XInterface >(), a);
283     }
284 }
285 
286 ExtensionDescription::~ExtensionDescription()
287 {
288 }
289 
290 //======================================================================
291 FileDoesNotExistFilter::FileDoesNotExistFilter(
292 	const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
293 	m_bExist(true), m_xCommandEnv(xCmdEnv)
294 {}
295 
296 FileDoesNotExistFilter::~FileDoesNotExistFilter()
297 {
298 };
299 
300 bool FileDoesNotExistFilter::exist()
301 {
302 	return m_bExist;
303 }
304     // XCommandEnvironment
305 Reference<css::task::XInteractionHandler >
306     FileDoesNotExistFilter::getInteractionHandler() throw (css::uno::RuntimeException)
307 {
308 	return static_cast<css::task::XInteractionHandler*>(this);
309 }
310 
311 Reference<css::ucb::XProgressHandler >
312     FileDoesNotExistFilter::getProgressHandler() throw (css::uno::RuntimeException)
313 {
314 	return m_xCommandEnv.is()
315         ? m_xCommandEnv->getProgressHandler()
316         : Reference<css::ucb::XProgressHandler>();
317 }
318 
319 // XInteractionHandler
320 //If the interaction was caused by a non-existing file which is specified in the ctor
321 //of FileDoesNotExistFilter, then we do nothing
322 void  FileDoesNotExistFilter::handle(
323         Reference<css::task::XInteractionRequest > const & xRequest )
324         throw (css::uno::RuntimeException)
325 {
326 	css::uno::Any request( xRequest->getRequest() );
327 
328 	css::ucb::InteractiveAugmentedIOException ioexc;
329 	if ((request>>= ioexc) && ioexc.Code == css::ucb::IOErrorCode_NOT_EXISTING )
330 	{
331 		m_bExist = false;
332 		return;
333 	}
334 	Reference<css::task::XInteractionHandler> xInteraction;
335     if (m_xCommandEnv.is()) {
336         xInteraction = m_xCommandEnv->getInteractionHandler();
337     }
338     if (xInteraction.is()) {
339         xInteraction->handle(xRequest);
340     }
341 }
342 
343 }
344 
345 namespace dp_misc {
346 
347 DescriptionInfoset getDescriptionInfoset(OUString const & sExtensionFolderURL)
348 {
349     Reference< css::xml::dom::XNode > root;
350     Reference<css::uno::XComponentContext> context =
351         comphelper_getProcessComponentContext();
352     OSL_ASSERT(context.is());
353     try {
354         root =
355             ExtensionDescription(
356                 context, sExtensionFolderURL,
357                 Reference< css::ucb::XCommandEnvironment >()).
358             getRootElement();
359     } catch (NoDescriptionException &) {
360     } catch (css::deployment::DeploymentException & e) {
361         throw css::uno::RuntimeException(
362             (OUString(
363                 RTL_CONSTASCII_USTRINGPARAM(
364                     "com.sun.star.deployment.DeploymentException: ")) +
365              e.Message), 0);
366     }
367     return DescriptionInfoset(context, root);
368 }
369 
370 DescriptionInfoset::DescriptionInfoset(
371     css::uno::Reference< css::uno::XComponentContext > const & context,
372     css::uno::Reference< css::xml::dom::XNode > const & element):
373     m_context(context),
374     m_element(element)
375 {
376     css::uno::Reference< css::lang::XMultiComponentFactory > manager(
377         context->getServiceManager(), css::uno::UNO_QUERY_THROW);
378     if (m_element.is()) {
379         m_xpath = css::uno::Reference< css::xml::xpath::XXPathAPI >(
380             manager->createInstanceWithContext(
381                 ::rtl::OUString(
382                     RTL_CONSTASCII_USTRINGPARAM(
383                         "com.sun.star.xml.xpath.XPathAPI")),
384                 context),
385             css::uno::UNO_QUERY_THROW);
386         m_xpath->registerNS(
387             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc")),
388             element->getNamespaceURI());
389         m_xpath->registerNS(
390             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("xlink")),
391             ::rtl::OUString(
392                 RTL_CONSTASCII_USTRINGPARAM("http://www.w3.org/1999/xlink")));
393     }
394 }
395 
396 DescriptionInfoset::~DescriptionInfoset() {}
397 
398 ::boost::optional< ::rtl::OUString > DescriptionInfoset::getIdentifier() const {
399     return getOptionalValue(
400         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:identifier/@value")));
401 }
402 
403 ::rtl::OUString DescriptionInfoset::getNodeValueFromExpression(::rtl::OUString const & expression) const
404 {
405     css::uno::Reference< css::xml::dom::XNode > n;
406     if (m_element.is()) {
407         try {
408             n = m_xpath->selectSingleNode(m_element, expression);
409         } catch (css::xml::xpath::XPathException &) {
410             // ignore
411         }
412     }
413     return n.is() ? getNodeValue(n) : ::rtl::OUString();
414 }
415 
416 void DescriptionInfoset::checkBlacklist() const
417 {
418     if (m_element.is()) {
419         boost::optional< OUString > id(getIdentifier());
420         if (!id)
421             return; // nothing to check
422         OUString currentversion(getVersion());
423         if (currentversion.getLength() == 0)
424             return;  // nothing to check
425 
426         css::uno::Reference< css::lang::XMultiComponentFactory > manager(
427             m_context->getServiceManager(), css::uno::UNO_QUERY_THROW);
428         css::uno::Reference< css::lang::XMultiServiceFactory> provider(
429             manager->createInstanceWithContext(
430                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.configuration.ConfigurationProvider")), m_context),
431                 css::uno::UNO_QUERY_THROW);
432 
433         css::uno::Sequence< css::uno::Any > args = css::uno::Sequence< css::uno::Any >(1);
434         css::beans::PropertyValue prop;
435         prop.Name = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("nodepath"));
436         prop.Value <<= ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("/org.openoffice.Office.ExtensionDependencies/Extensions"));
437         args[0] <<= prop;
438 
439         css::uno::Reference< css::container::XNameAccess > blacklist(
440             provider->createInstanceWithArguments(
441                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.configuration.ConfigurationAccess")), args),
442                 css::uno::UNO_QUERY_THROW);
443 
444         // check first if a blacklist entry is available
445         if (blacklist.is() && blacklist->hasByName(*id)) {
446             css::uno::Reference< css::beans::XPropertySet > extProps(
447                 blacklist->getByName(*id), css::uno::UNO_QUERY_THROW);
448 
449             css::uno::Any anyValue = extProps->getPropertyValue(
450                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Versions")));
451 
452             css::uno::Sequence< ::rtl::OUString > blversions;
453             anyValue >>= blversions;
454 
455             // check if the current version requires further dependency checks from the blacklist
456             if (checkBlacklistVersion(currentversion, blversions)) {
457                 anyValue = extProps->getPropertyValue(
458                     ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("Dependencies")));
459                 ::rtl::OUString udeps;
460                 anyValue >>= udeps;
461 
462                 if (udeps.getLength() == 0)
463                     return; // nothing todo
464 
465                 ::rtl::OString xmlDependencies = ::rtl::OUStringToOString(udeps, RTL_TEXTENCODING_UNICODE);
466 
467                 css::uno::Reference< css::xml::dom::XDocumentBuilder> docbuilder(
468                     manager->createInstanceWithContext(
469                         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.xml.dom.DocumentBuilder")), m_context),
470                     css::uno::UNO_QUERY_THROW);
471 
472                 css::uno::Sequence< sal_Int8 > byteSeq((const sal_Int8*)xmlDependencies.getStr(), xmlDependencies.getLength());
473 
474                 css::uno::Reference< css::io::XInputStream> inputstream( css::io::SequenceInputStream::createStreamFromSequence(m_context, byteSeq),
475                                                                          css::uno::UNO_QUERY_THROW);
476 
477                 css::uno::Reference< css::xml::dom::XDocument > xDocument(docbuilder->parse(inputstream));
478                 css::uno::Reference< css::xml::dom::XElement > xElement(xDocument->getDocumentElement());
479                 css::uno::Reference< css::xml::dom::XNodeList > xDeps(xElement->getChildNodes());
480                 sal_Int32 nLen = xDeps->getLength();
481 
482                 // get the parent xml document  of current description info for the import
483                 css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(m_element->getOwnerDocument());
484 
485                 // get dependency node of current description info to merge the new dependencies from the blacklist
486                 css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
487                     m_xpath->selectSingleNode(m_element, ::rtl::OUString(
488                                                   RTL_CONSTASCII_USTRINGPARAM("desc:dependencies"))));
489 
490                 // if no dependency node exists, create a new one in the current description info
491                 if (!xCurrentDeps.is()) {
492                     css::uno::Reference< css::xml::dom::XNode > xNewDepNode(
493                         xCurrentDescInfo->createElementNS(
494                             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("http://openoffice.org/extensions/description/2006")),
495                             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("dependencies"))), css::uno::UNO_QUERY_THROW);
496                     m_element->appendChild(xNewDepNode);
497                     xCurrentDeps = m_xpath->selectSingleNode(m_element, ::rtl::OUString(
498                                                   RTL_CONSTASCII_USTRINGPARAM("desc:dependencies")));
499                 }
500 
501                 for (sal_Int32 i=0; i<nLen; i++) {
502                     css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
503                     css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
504                     if (xDep.is()) {
505                         // found valid blacklist dependency, import the node first and append it to the existing dependency node
506                         css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
507                         xCurrentDeps->appendChild(importedNode);
508                     }
509                 }
510             }
511         }
512     }
513 }
514 
515 bool DescriptionInfoset::checkBlacklistVersion(
516     ::rtl::OUString currentversion,
517     ::com::sun::star::uno::Sequence< ::rtl::OUString > const & versions) const
518 {
519     sal_Int32 nLen = versions.getLength();
520     for (sal_Int32 i=0; i<nLen; i++) {
521         if (currentversion.equals(versions[i]))
522             return true;
523     }
524 
525     return false;
526 }
527 
528 ::rtl::OUString DescriptionInfoset::getVersion() const
529 {
530     return getNodeValueFromExpression( ::rtl::OUString(
531             RTL_CONSTASCII_USTRINGPARAM("desc:version/@value")));
532 }
533 
534 css::uno::Sequence< ::rtl::OUString > DescriptionInfoset::getSupportedPlaforms() const
535 {
536     //When there is no description.xml then we assume that we support all platforms
537     if (! m_element.is())
538     {
539         return comphelper::makeSequence(
540             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("all")));
541     }
542 
543     //Check if the <platform> element was provided. If not the default is "all" platforms
544     css::uno::Reference< css::xml::dom::XNode > nodePlatform(
545         m_xpath->selectSingleNode(m_element, ::rtl::OUString(
546             RTL_CONSTASCII_USTRINGPARAM("desc:platform"))));
547     if (!nodePlatform.is())
548     {
549         return comphelper::makeSequence(
550             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("all")));
551     }
552 
553     //There is a platform element.
554     const ::rtl::OUString value = getNodeValueFromExpression(::rtl::OUString(
555             RTL_CONSTASCII_USTRINGPARAM("desc:platform/@value")));
556     //parse the string, it can contained multiple strings separated by commas
557     ::std::vector< ::rtl::OUString> vec;
558     sal_Int32 nIndex = 0;
559     do
560     {
561         ::rtl::OUString aToken = value.getToken( 0, ',', nIndex );
562         aToken = aToken.trim();
563         if (aToken.getLength())
564             vec.push_back(aToken);
565 
566     }
567     while (nIndex >= 0);
568 
569     return comphelper::containerToSequence(vec);
570 }
571 
572 css::uno::Reference< css::xml::dom::XNodeList >
573 DescriptionInfoset::getDependencies() const {
574     if (m_element.is()) {
575         try {
576             // check the extension blacklist first and expand the dependencies if applicable
577             checkBlacklist();
578 
579             return m_xpath->selectNodeList(m_element, ::rtl::OUString(
580                         RTL_CONSTASCII_USTRINGPARAM("desc:dependencies/*")));
581         } catch (css::xml::xpath::XPathException &) {
582             // ignore
583         }
584     }
585     return new EmptyNodeList;
586 }
587 
588 css::uno::Sequence< ::rtl::OUString >
589 DescriptionInfoset::getUpdateInformationUrls() const {
590     return getUrls(
591         ::rtl::OUString(
592             RTL_CONSTASCII_USTRINGPARAM(
593                 "desc:update-information/desc:src/@xlink:href")));
594 }
595 
596 css::uno::Sequence< ::rtl::OUString >
597 DescriptionInfoset::getUpdateDownloadUrls() const
598 {
599     return getUrls(
600         ::rtl::OUString(
601             RTL_CONSTASCII_USTRINGPARAM(
602                 "desc:update-download/desc:src/@xlink:href")));
603 }
604 
605 ::rtl::OUString DescriptionInfoset::getIconURL( sal_Bool bHighContrast ) const
606 {
607     css::uno::Sequence< ::rtl::OUString > aStrList = getUrls( ::rtl::OUString(
608             RTL_CONSTASCII_USTRINGPARAM( "desc:icon/desc:default/@xlink:href")));
609     css::uno::Sequence< ::rtl::OUString > aStrListHC = getUrls( ::rtl::OUString(
610             RTL_CONSTASCII_USTRINGPARAM( "desc:icon/desc:high-contrast/@xlink:href")));
611 
612     if ( bHighContrast && aStrListHC.hasElements() && aStrListHC[0].getLength() )
613         return aStrListHC[0];
614 
615     if ( aStrList.hasElements() && aStrList[0].getLength() )
616         return aStrList[0];
617 
618     return ::rtl::OUString();
619 }
620 
621 ::boost::optional< ::rtl::OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
622     const
623 {
624     bool bParentExists = false;
625     const ::rtl::OUString sURL (getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
626         "/desc:description/desc:update-website")), &bParentExists ));
627 
628     if (sURL.getLength() > 0)
629         return ::boost::optional< ::rtl::OUString >(sURL);
630     else
631         return bParentExists ? ::boost::optional< ::rtl::OUString >(::rtl::OUString()) :
632             ::boost::optional< ::rtl::OUString >();
633 }
634 
635 ::boost::optional< ::rtl::OUString > DescriptionInfoset::getOptionalValue(
636     ::rtl::OUString const & expression) const
637 {
638     css::uno::Reference< css::xml::dom::XNode > n;
639     if (m_element.is()) {
640         try {
641             n = m_xpath->selectSingleNode(m_element, expression);
642         } catch (css::xml::xpath::XPathException &) {
643             // ignore
644         }
645     }
646     return n.is()
647         ? ::boost::optional< ::rtl::OUString >(getNodeValue(n))
648         : ::boost::optional< ::rtl::OUString >();
649 }
650 
651 css::uno::Sequence< ::rtl::OUString > DescriptionInfoset::getUrls(
652     ::rtl::OUString const & expression) const
653 {
654     css::uno::Reference< css::xml::dom::XNodeList > ns;
655     if (m_element.is()) {
656         try {
657             ns = m_xpath->selectNodeList(m_element, expression);
658         } catch (css::xml::xpath::XPathException &) {
659             // ignore
660         }
661     }
662     css::uno::Sequence< ::rtl::OUString > urls(ns.is() ? ns->getLength() : 0);
663     for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
664         urls[i] = getNodeValue(ns->item(i));
665     }
666     return urls;
667 }
668 
669 ::std::pair< ::rtl::OUString, ::rtl::OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
670 {
671     css::uno::Reference< css::xml::dom::XNode > node =
672         getLocalizedChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:publisher")));
673 
674     ::rtl::OUString sPublisherName;
675     ::rtl::OUString sURL;
676     if (node.is())
677     {
678         const ::rtl::OUString exp1(RTL_CONSTASCII_USTRINGPARAM("text()"));
679         css::uno::Reference< css::xml::dom::XNode > xPathName;
680         try {
681             xPathName = m_xpath->selectSingleNode(node, exp1);
682         } catch (css::xml::xpath::XPathException &) {
683             // ignore
684         }
685         OSL_ASSERT(xPathName.is());
686         if (xPathName.is())
687             sPublisherName = xPathName->getNodeValue();
688 
689         const ::rtl::OUString exp2(RTL_CONSTASCII_USTRINGPARAM("@xlink:href"));
690         css::uno::Reference< css::xml::dom::XNode > xURL;
691         try {
692             xURL = m_xpath->selectSingleNode(node, exp2);
693         } catch (css::xml::xpath::XPathException &) {
694             // ignore
695         }
696         OSL_ASSERT(xURL.is());
697         if (xURL.is())
698            sURL = xURL->getNodeValue();
699     }
700     return ::std::make_pair(sPublisherName, sURL);
701 }
702 
703 ::rtl::OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
704 {
705     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
706         "/desc:description/desc:release-notes")), NULL);
707 }
708 
709 ::rtl::OUString DescriptionInfoset::getLocalizedDisplayName() const
710 {
711     css::uno::Reference< css::xml::dom::XNode > node =
712         getLocalizedChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:display-name")));
713     if (node.is())
714     {
715         const ::rtl::OUString exp(RTL_CONSTASCII_USTRINGPARAM("text()"));
716         css::uno::Reference< css::xml::dom::XNode > xtext;
717         try {
718             xtext = m_xpath->selectSingleNode(node, exp);
719         } catch (css::xml::xpath::XPathException &) {
720             // ignore
721         }
722         if (xtext.is())
723             return xtext->getNodeValue();
724     }
725     return ::rtl::OUString();
726 }
727 
728 ::rtl::OUString DescriptionInfoset::getLocalizedLicenseURL() const
729 {
730     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
731         "/desc:description/desc:registration/desc:simple-license")), NULL);
732 
733 }
734 
735 ::boost::optional<SimpleLicenseAttributes>
736 DescriptionInfoset::getSimpleLicenseAttributes() const
737 {
738     //Check if the node exist
739     css::uno::Reference< css::xml::dom::XNode > n;
740     if (m_element.is()) {
741         try {
742             n = m_xpath->selectSingleNode(m_element,
743                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
744                 "/desc:description/desc:registration/desc:simple-license/@accept-by")));
745         } catch (css::xml::xpath::XPathException &) {
746             // ignore
747         }
748         if (n.is())
749         {
750             SimpleLicenseAttributes attributes;
751             attributes.acceptBy =
752                 getNodeValueFromExpression(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
753                 "/desc:description/desc:registration/desc:simple-license/@accept-by")));
754 
755             ::boost::optional< ::rtl::OUString > suppressOnUpdate = getOptionalValue(
756                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
757                 "/desc:description/desc:registration/desc:simple-license/@suppress-on-update")));
758             if (suppressOnUpdate)
759                 attributes.suppressOnUpdate = (*suppressOnUpdate).trim().equalsIgnoreAsciiCase(
760                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("true")));
761             else
762                 attributes.suppressOnUpdate = false;
763 
764             ::boost::optional< ::rtl::OUString > suppressIfRequired = getOptionalValue(
765                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
766                 "/desc:description/desc:registration/desc:simple-license/@suppress-if-required")));
767             if (suppressIfRequired)
768                 attributes.suppressIfRequired = (*suppressIfRequired).trim().equalsIgnoreAsciiCase(
769                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("true")));
770             else
771                 attributes.suppressIfRequired = false;
772 
773             return ::boost::optional<SimpleLicenseAttributes>(attributes);
774         }
775     }
776     return ::boost::optional<SimpleLicenseAttributes>();
777 }
778 
779 ::rtl::OUString DescriptionInfoset::getLocalizedDescriptionURL() const
780 {
781     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
782         "/desc:description/desc:extension-description")), NULL);
783 }
784 
785 css::uno::Reference< css::xml::dom::XNode >
786 DescriptionInfoset::getLocalizedChild( const ::rtl::OUString & sParent) const
787 {
788     if ( ! m_element.is() || !sParent.getLength())
789         return css::uno::Reference< css::xml::dom::XNode > ();
790 
791     css::uno::Reference< css::xml::dom::XNode > xParent;
792     try {
793         xParent = m_xpath->selectSingleNode(m_element, sParent);
794     } catch (css::xml::xpath::XPathException &) {
795         // ignore
796     }
797     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
798     if (xParent.is())
799     {
800         const ::rtl::OUString sLocale = getOfficeLocaleString();
801         nodeMatch = matchFullLocale(xParent, sLocale);
802 
803         //office: en-DE, en, en-DE-altmark
804         if (! nodeMatch.is())
805         {
806             const css::lang::Locale officeLocale = getOfficeLocale();
807             nodeMatch = matchCountryAndLanguage(xParent, officeLocale);
808             if ( ! nodeMatch.is())
809             {
810                 nodeMatch = matchLanguage(xParent, officeLocale);
811                 if (! nodeMatch.is())
812                     nodeMatch = getChildWithDefaultLocale(xParent);
813             }
814         }
815     }
816 
817     return nodeMatch;
818 }
819 
820 css::uno::Reference<css::xml::dom::XNode>
821 DescriptionInfoset::matchFullLocale(css::uno::Reference< css::xml::dom::XNode >
822                                     const & xParent, ::rtl::OUString const & sLocale) const
823 {
824     OSL_ASSERT(xParent.is());
825     const ::rtl::OUString exp1(
826         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
827         + sLocale +
828         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
829     try {
830         return m_xpath->selectSingleNode(xParent, exp1);
831     } catch (css::xml::xpath::XPathException &) {
832         // ignore
833         return 0;
834     }
835 }
836 
837 css::uno::Reference<css::xml::dom::XNode>
838 DescriptionInfoset::matchCountryAndLanguage(
839     css::uno::Reference< css::xml::dom::XNode > const & xParent, css::lang::Locale const & officeLocale) const
840 {
841     OSL_ASSERT(xParent.is());
842     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
843 
844     if (officeLocale.Country.getLength())
845     {
846         const ::rtl::OUString sLangCountry(officeLocale.Language +
847             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-")) +
848             officeLocale.Country);
849         //first try exact match for lang-country
850         const ::rtl::OUString exp1(
851             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
852             + sLangCountry +
853             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
854         try {
855             nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
856         } catch (css::xml::xpath::XPathException &) {
857             // ignore
858         }
859 
860         //try to match in strings that also have a variant, for example en-US matches in
861         //en-US-montana
862         if (!nodeMatch.is())
863         {
864             const ::rtl::OUString exp2(
865                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[starts-with(@lang,\""))
866                 + sLangCountry +
867                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-\")]")));
868             try {
869                 nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
870             } catch (css::xml::xpath::XPathException &) {
871                 // ignore
872             }
873         }
874     }
875 
876     return nodeMatch;
877 }
878 
879 
880 css::uno::Reference<css::xml::dom::XNode>
881 DescriptionInfoset::matchLanguage(
882     css::uno::Reference< css::xml::dom::XNode > const & xParent, css::lang::Locale const & officeLocale) const
883 {
884     OSL_ASSERT(xParent.is());
885     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
886 
887     //first try exact match for lang
888     const ::rtl::OUString exp1(
889         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
890         + officeLocale.Language
891         + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
892     try {
893         nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
894     } catch (css::xml::xpath::XPathException &) {
895         // ignore
896     }
897 
898     //try to match in strings that also have a country and/orvariant, for example en  matches in
899     //en-US-montana, en-US, en-montana
900     if (!nodeMatch.is())
901     {
902         const ::rtl::OUString exp2(
903             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[starts-with(@lang,\""))
904             + officeLocale.Language
905             + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-\")]")));
906         try {
907             nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
908         } catch (css::xml::xpath::XPathException &) {
909             // ignore
910         }
911     }
912     return nodeMatch;
913 }
914 
915 css::uno::Reference<css::xml::dom::XNode>
916 DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
917                                     const & xParent) const
918 {
919     OSL_ASSERT(xParent.is());
920     if (xParent->getNodeName().equals(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("simple-license"))))
921     {
922         css::uno::Reference<css::xml::dom::XNode> nodeDefault;
923         try {
924             nodeDefault = m_xpath->selectSingleNode(xParent, ::rtl::OUString(
925                 RTL_CONSTASCII_USTRINGPARAM("@default-license-id")));
926         } catch (css::xml::xpath::XPathException &) {
927             // ignore
928         }
929         if (nodeDefault.is())
930         {
931             //The old way
932             const ::rtl::OUString exp1(
933                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:license-text[@license-id = \""))
934                 + nodeDefault->getNodeValue()
935                 + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
936             try {
937                 return m_xpath->selectSingleNode(xParent, exp1);
938             } catch (css::xml::xpath::XPathException &) {
939                 // ignore
940             }
941         }
942     }
943 
944     const ::rtl::OUString exp2(RTL_CONSTASCII_USTRINGPARAM("*[1]"));
945     try {
946         return m_xpath->selectSingleNode(xParent, exp2);
947     } catch (css::xml::xpath::XPathException &) {
948         // ignore
949         return 0;
950     }
951 }
952 
953 ::rtl::OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
954     ::rtl::OUString const & sXPathParent, bool * out_bParentExists)
955     const
956 {
957     css::uno::Reference< css::xml::dom::XNode > node =
958         getLocalizedChild(sXPathParent);
959 
960     ::rtl::OUString sURL;
961     if (node.is())
962     {
963         if (out_bParentExists)
964             *out_bParentExists = true;
965         const ::rtl::OUString exp(RTL_CONSTASCII_USTRINGPARAM("@xlink:href"));
966         css::uno::Reference< css::xml::dom::XNode > xURL;
967         try {
968             xURL = m_xpath->selectSingleNode(node, exp);
969         } catch (css::xml::xpath::XPathException &) {
970             // ignore
971         }
972         OSL_ASSERT(xURL.is());
973         if (xURL.is())
974             sURL = xURL->getNodeValue();
975     }
976     else
977     {
978         if (out_bParentExists)
979             *out_bParentExists = false;
980     }
981     return sURL;
982 }
983 
984 }
985