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