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 dependency node of current description info to merge the new dependencies from the blacklist
483                 css::uno::Reference< css::xml::dom::XNode > xCurrentDeps(
484                     m_xpath->selectSingleNode(m_element, ::rtl::OUString(
485                                                   RTL_CONSTASCII_USTRINGPARAM("desc:dependencies"))));
486 
487                 css::uno::Reference< css::xml::dom::XDocument > xCurrentDescInfo(xCurrentDeps->getOwnerDocument());
488 
489                 for (sal_Int32 i=0; i<nLen; i++) {
490                     css::uno::Reference< css::xml::dom::XNode > xNode(xDeps->item(i));
491                     css::uno::Reference< css::xml::dom::XElement > xDep(xNode, css::uno::UNO_QUERY);
492                     if (xDep.is()) {
493                         // found valid blacklist dependency, import the node first and append it to the existing dependency node
494                         css::uno::Reference< css::xml::dom::XNode > importedNode = xCurrentDescInfo->importNode(xNode, true);
495                         xCurrentDeps->appendChild(importedNode);
496                     }
497                 }
498             }
499         }
500     }
501 }
502 
503 bool DescriptionInfoset::checkBlacklistVersion(
504     ::rtl::OUString currentversion,
505     ::com::sun::star::uno::Sequence< ::rtl::OUString > const & versions) const
506 {
507     sal_Int32 nLen = versions.getLength();
508     for (sal_Int32 i=0; i<nLen; i++) {
509         if (currentversion.equals(versions[i]))
510             return true;
511     }
512 
513     return false;
514 }
515 
516 ::rtl::OUString DescriptionInfoset::getVersion() const
517 {
518     return getNodeValueFromExpression( ::rtl::OUString(
519             RTL_CONSTASCII_USTRINGPARAM("desc:version/@value")));
520 }
521 
522 css::uno::Sequence< ::rtl::OUString > DescriptionInfoset::getSupportedPlaforms() const
523 {
524     //When there is no description.xml then we assume that we support all platforms
525     if (! m_element.is())
526     {
527         return comphelper::makeSequence(
528             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("all")));
529     }
530 
531     //Check if the <platform> element was provided. If not the default is "all" platforms
532     css::uno::Reference< css::xml::dom::XNode > nodePlatform(
533         m_xpath->selectSingleNode(m_element, ::rtl::OUString(
534             RTL_CONSTASCII_USTRINGPARAM("desc:platform"))));
535     if (!nodePlatform.is())
536     {
537         return comphelper::makeSequence(
538             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("all")));
539     }
540 
541     //There is a platform element.
542     const ::rtl::OUString value = getNodeValueFromExpression(::rtl::OUString(
543             RTL_CONSTASCII_USTRINGPARAM("desc:platform/@value")));
544     //parse the string, it can contained multiple strings separated by commas
545     ::std::vector< ::rtl::OUString> vec;
546     sal_Int32 nIndex = 0;
547     do
548     {
549         ::rtl::OUString aToken = value.getToken( 0, ',', nIndex );
550         aToken = aToken.trim();
551         if (aToken.getLength())
552             vec.push_back(aToken);
553 
554     }
555     while (nIndex >= 0);
556 
557     return comphelper::containerToSequence(vec);
558 }
559 
560 css::uno::Reference< css::xml::dom::XNodeList >
561 DescriptionInfoset::getDependencies() const {
562     if (m_element.is()) {
563         try {
564             // check the extension blacklist first and expand the dependencies if applicable
565             checkBlacklist();
566 
567             return m_xpath->selectNodeList(m_element, ::rtl::OUString(
568                         RTL_CONSTASCII_USTRINGPARAM("desc:dependencies/*")));
569         } catch (css::xml::xpath::XPathException &) {
570             // ignore
571         }
572     }
573     return new EmptyNodeList;
574 }
575 
576 css::uno::Sequence< ::rtl::OUString >
577 DescriptionInfoset::getUpdateInformationUrls() const {
578     return getUrls(
579         ::rtl::OUString(
580             RTL_CONSTASCII_USTRINGPARAM(
581                 "desc:update-information/desc:src/@xlink:href")));
582 }
583 
584 css::uno::Sequence< ::rtl::OUString >
585 DescriptionInfoset::getUpdateDownloadUrls() const
586 {
587     return getUrls(
588         ::rtl::OUString(
589             RTL_CONSTASCII_USTRINGPARAM(
590                 "desc:update-download/desc:src/@xlink:href")));
591 }
592 
593 ::rtl::OUString DescriptionInfoset::getIconURL( sal_Bool bHighContrast ) const
594 {
595     css::uno::Sequence< ::rtl::OUString > aStrList = getUrls( ::rtl::OUString(
596             RTL_CONSTASCII_USTRINGPARAM( "desc:icon/desc:default/@xlink:href")));
597     css::uno::Sequence< ::rtl::OUString > aStrListHC = getUrls( ::rtl::OUString(
598             RTL_CONSTASCII_USTRINGPARAM( "desc:icon/desc:high-contrast/@xlink:href")));
599 
600     if ( bHighContrast && aStrListHC.hasElements() && aStrListHC[0].getLength() )
601         return aStrListHC[0];
602 
603     if ( aStrList.hasElements() && aStrList[0].getLength() )
604         return aStrList[0];
605 
606     return ::rtl::OUString();
607 }
608 
609 ::boost::optional< ::rtl::OUString > DescriptionInfoset::getLocalizedUpdateWebsiteURL()
610     const
611 {
612     bool bParentExists = false;
613     const ::rtl::OUString sURL (getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
614         "/desc:description/desc:update-website")), &bParentExists ));
615 
616     if (sURL.getLength() > 0)
617         return ::boost::optional< ::rtl::OUString >(sURL);
618     else
619         return bParentExists ? ::boost::optional< ::rtl::OUString >(::rtl::OUString()) :
620             ::boost::optional< ::rtl::OUString >();
621 }
622 
623 ::boost::optional< ::rtl::OUString > DescriptionInfoset::getOptionalValue(
624     ::rtl::OUString const & expression) const
625 {
626     css::uno::Reference< css::xml::dom::XNode > n;
627     if (m_element.is()) {
628         try {
629             n = m_xpath->selectSingleNode(m_element, expression);
630         } catch (css::xml::xpath::XPathException &) {
631             // ignore
632         }
633     }
634     return n.is()
635         ? ::boost::optional< ::rtl::OUString >(getNodeValue(n))
636         : ::boost::optional< ::rtl::OUString >();
637 }
638 
639 css::uno::Sequence< ::rtl::OUString > DescriptionInfoset::getUrls(
640     ::rtl::OUString const & expression) const
641 {
642     css::uno::Reference< css::xml::dom::XNodeList > ns;
643     if (m_element.is()) {
644         try {
645             ns = m_xpath->selectNodeList(m_element, expression);
646         } catch (css::xml::xpath::XPathException &) {
647             // ignore
648         }
649     }
650     css::uno::Sequence< ::rtl::OUString > urls(ns.is() ? ns->getLength() : 0);
651     for (::sal_Int32 i = 0; i < urls.getLength(); ++i) {
652         urls[i] = getNodeValue(ns->item(i));
653     }
654     return urls;
655 }
656 
657 ::std::pair< ::rtl::OUString, ::rtl::OUString > DescriptionInfoset::getLocalizedPublisherNameAndURL() const
658 {
659     css::uno::Reference< css::xml::dom::XNode > node =
660         getLocalizedChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:publisher")));
661 
662     ::rtl::OUString sPublisherName;
663     ::rtl::OUString sURL;
664     if (node.is())
665     {
666         const ::rtl::OUString exp1(RTL_CONSTASCII_USTRINGPARAM("text()"));
667         css::uno::Reference< css::xml::dom::XNode > xPathName;
668         try {
669             xPathName = m_xpath->selectSingleNode(node, exp1);
670         } catch (css::xml::xpath::XPathException &) {
671             // ignore
672         }
673         OSL_ASSERT(xPathName.is());
674         if (xPathName.is())
675             sPublisherName = xPathName->getNodeValue();
676 
677         const ::rtl::OUString exp2(RTL_CONSTASCII_USTRINGPARAM("@xlink:href"));
678         css::uno::Reference< css::xml::dom::XNode > xURL;
679         try {
680             xURL = m_xpath->selectSingleNode(node, exp2);
681         } catch (css::xml::xpath::XPathException &) {
682             // ignore
683         }
684         OSL_ASSERT(xURL.is());
685         if (xURL.is())
686            sURL = xURL->getNodeValue();
687     }
688     return ::std::make_pair(sPublisherName, sURL);
689 }
690 
691 ::rtl::OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
692 {
693     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
694         "/desc:description/desc:release-notes")), NULL);
695 }
696 
697 ::rtl::OUString DescriptionInfoset::getLocalizedDisplayName() const
698 {
699     css::uno::Reference< css::xml::dom::XNode > node =
700         getLocalizedChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:display-name")));
701     if (node.is())
702     {
703         const ::rtl::OUString exp(RTL_CONSTASCII_USTRINGPARAM("text()"));
704         css::uno::Reference< css::xml::dom::XNode > xtext;
705         try {
706             xtext = m_xpath->selectSingleNode(node, exp);
707         } catch (css::xml::xpath::XPathException &) {
708             // ignore
709         }
710         if (xtext.is())
711             return xtext->getNodeValue();
712     }
713     return ::rtl::OUString();
714 }
715 
716 ::rtl::OUString DescriptionInfoset::getLocalizedLicenseURL() const
717 {
718     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
719         "/desc:description/desc:registration/desc:simple-license")), NULL);
720 
721 }
722 
723 ::boost::optional<SimpleLicenseAttributes>
724 DescriptionInfoset::getSimpleLicenseAttributes() const
725 {
726     //Check if the node exist
727     css::uno::Reference< css::xml::dom::XNode > n;
728     if (m_element.is()) {
729         try {
730             n = m_xpath->selectSingleNode(m_element,
731                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
732                 "/desc:description/desc:registration/desc:simple-license/@accept-by")));
733         } catch (css::xml::xpath::XPathException &) {
734             // ignore
735         }
736         if (n.is())
737         {
738             SimpleLicenseAttributes attributes;
739             attributes.acceptBy =
740                 getNodeValueFromExpression(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
741                 "/desc:description/desc:registration/desc:simple-license/@accept-by")));
742 
743             ::boost::optional< ::rtl::OUString > suppressOnUpdate = getOptionalValue(
744                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
745                 "/desc:description/desc:registration/desc:simple-license/@suppress-on-update")));
746             if (suppressOnUpdate)
747                 attributes.suppressOnUpdate = (*suppressOnUpdate).trim().equalsIgnoreAsciiCase(
748                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("true")));
749             else
750                 attributes.suppressOnUpdate = false;
751 
752             ::boost::optional< ::rtl::OUString > suppressIfRequired = getOptionalValue(
753                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
754                 "/desc:description/desc:registration/desc:simple-license/@suppress-if-required")));
755             if (suppressIfRequired)
756                 attributes.suppressIfRequired = (*suppressIfRequired).trim().equalsIgnoreAsciiCase(
757                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("true")));
758             else
759                 attributes.suppressIfRequired = false;
760 
761             return ::boost::optional<SimpleLicenseAttributes>(attributes);
762         }
763     }
764     return ::boost::optional<SimpleLicenseAttributes>();
765 }
766 
767 ::rtl::OUString DescriptionInfoset::getLocalizedDescriptionURL() const
768 {
769     return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
770         "/desc:description/desc:extension-description")), NULL);
771 }
772 
773 css::uno::Reference< css::xml::dom::XNode >
774 DescriptionInfoset::getLocalizedChild( const ::rtl::OUString & sParent) const
775 {
776     if ( ! m_element.is() || !sParent.getLength())
777         return css::uno::Reference< css::xml::dom::XNode > ();
778 
779     css::uno::Reference< css::xml::dom::XNode > xParent;
780     try {
781         xParent = m_xpath->selectSingleNode(m_element, sParent);
782     } catch (css::xml::xpath::XPathException &) {
783         // ignore
784     }
785     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
786     if (xParent.is())
787     {
788         const ::rtl::OUString sLocale = getOfficeLocaleString();
789         nodeMatch = matchFullLocale(xParent, sLocale);
790 
791         //office: en-DE, en, en-DE-altmark
792         if (! nodeMatch.is())
793         {
794             const css::lang::Locale officeLocale = getOfficeLocale();
795             nodeMatch = matchCountryAndLanguage(xParent, officeLocale);
796             if ( ! nodeMatch.is())
797             {
798                 nodeMatch = matchLanguage(xParent, officeLocale);
799                 if (! nodeMatch.is())
800                     nodeMatch = getChildWithDefaultLocale(xParent);
801             }
802         }
803     }
804 
805     return nodeMatch;
806 }
807 
808 css::uno::Reference<css::xml::dom::XNode>
809 DescriptionInfoset::matchFullLocale(css::uno::Reference< css::xml::dom::XNode >
810                                     const & xParent, ::rtl::OUString const & sLocale) const
811 {
812     OSL_ASSERT(xParent.is());
813     const ::rtl::OUString exp1(
814         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
815         + sLocale +
816         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
817     try {
818         return m_xpath->selectSingleNode(xParent, exp1);
819     } catch (css::xml::xpath::XPathException &) {
820         // ignore
821         return 0;
822     }
823 }
824 
825 css::uno::Reference<css::xml::dom::XNode>
826 DescriptionInfoset::matchCountryAndLanguage(
827     css::uno::Reference< css::xml::dom::XNode > const & xParent, css::lang::Locale const & officeLocale) const
828 {
829     OSL_ASSERT(xParent.is());
830     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
831 
832     if (officeLocale.Country.getLength())
833     {
834         const ::rtl::OUString sLangCountry(officeLocale.Language +
835             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-")) +
836             officeLocale.Country);
837         //first try exact match for lang-country
838         const ::rtl::OUString exp1(
839             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
840             + sLangCountry +
841             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
842         try {
843             nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
844         } catch (css::xml::xpath::XPathException &) {
845             // ignore
846         }
847 
848         //try to match in strings that also have a variant, for example en-US matches in
849         //en-US-montana
850         if (!nodeMatch.is())
851         {
852             const ::rtl::OUString exp2(
853                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[starts-with(@lang,\""))
854                 + sLangCountry +
855                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-\")]")));
856             try {
857                 nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
858             } catch (css::xml::xpath::XPathException &) {
859                 // ignore
860             }
861         }
862     }
863 
864     return nodeMatch;
865 }
866 
867 
868 css::uno::Reference<css::xml::dom::XNode>
869 DescriptionInfoset::matchLanguage(
870     css::uno::Reference< css::xml::dom::XNode > const & xParent, css::lang::Locale const & officeLocale) const
871 {
872     OSL_ASSERT(xParent.is());
873     css::uno::Reference<css::xml::dom::XNode> nodeMatch;
874 
875     //first try exact match for lang
876     const ::rtl::OUString exp1(
877         ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[@lang=\""))
878         + officeLocale.Language
879         + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
880     try {
881         nodeMatch = m_xpath->selectSingleNode(xParent, exp1);
882     } catch (css::xml::xpath::XPathException &) {
883         // ignore
884     }
885 
886     //try to match in strings that also have a country and/orvariant, for example en  matches in
887     //en-US-montana, en-US, en-montana
888     if (!nodeMatch.is())
889     {
890         const ::rtl::OUString exp2(
891             ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("*[starts-with(@lang,\""))
892             + officeLocale.Language
893             + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("-\")]")));
894         try {
895             nodeMatch = m_xpath->selectSingleNode(xParent, exp2);
896         } catch (css::xml::xpath::XPathException &) {
897             // ignore
898         }
899     }
900     return nodeMatch;
901 }
902 
903 css::uno::Reference<css::xml::dom::XNode>
904 DescriptionInfoset::getChildWithDefaultLocale(css::uno::Reference< css::xml::dom::XNode >
905                                     const & xParent) const
906 {
907     OSL_ASSERT(xParent.is());
908     if (xParent->getNodeName().equals(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("simple-license"))))
909     {
910         css::uno::Reference<css::xml::dom::XNode> nodeDefault;
911         try {
912             nodeDefault = m_xpath->selectSingleNode(xParent, ::rtl::OUString(
913                 RTL_CONSTASCII_USTRINGPARAM("@default-license-id")));
914         } catch (css::xml::xpath::XPathException &) {
915             // ignore
916         }
917         if (nodeDefault.is())
918         {
919             //The old way
920             const ::rtl::OUString exp1(
921                 ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:license-text[@license-id = \""))
922                 + nodeDefault->getNodeValue()
923                 + ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("\"]")));
924             try {
925                 return m_xpath->selectSingleNode(xParent, exp1);
926             } catch (css::xml::xpath::XPathException &) {
927                 // ignore
928             }
929         }
930     }
931 
932     const ::rtl::OUString exp2(RTL_CONSTASCII_USTRINGPARAM("*[1]"));
933     try {
934         return m_xpath->selectSingleNode(xParent, exp2);
935     } catch (css::xml::xpath::XPathException &) {
936         // ignore
937         return 0;
938     }
939 }
940 
941 ::rtl::OUString DescriptionInfoset::getLocalizedHREFAttrFromChild(
942     ::rtl::OUString const & sXPathParent, bool * out_bParentExists)
943     const
944 {
945     css::uno::Reference< css::xml::dom::XNode > node =
946         getLocalizedChild(sXPathParent);
947 
948     ::rtl::OUString sURL;
949     if (node.is())
950     {
951         if (out_bParentExists)
952             *out_bParentExists = true;
953         const ::rtl::OUString exp(RTL_CONSTASCII_USTRINGPARAM("@xlink:href"));
954         css::uno::Reference< css::xml::dom::XNode > xURL;
955         try {
956             xURL = m_xpath->selectSingleNode(node, exp);
957         } catch (css::xml::xpath::XPathException &) {
958             // ignore
959         }
960         OSL_ASSERT(xURL.is());
961         if (xURL.is())
962             sURL = xURL->getNodeValue();
963     }
964     else
965     {
966         if (out_bParentExists)
967             *out_bParentExists = false;
968     }
969     return sURL;
970 }
971 
972 }
973