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