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  
87  EmptyNodeList::EmptyNodeList() {}
88  
89  EmptyNodeList::~EmptyNodeList() {}
90  
91  ::sal_Int32 EmptyNodeList::getLength() throw (css::uno::RuntimeException) {
92      return 0;
93  }
94  
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  
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 occured. Therefore it shoult 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  
145  	css::uno::Reference<css::xml::dom::XNode> getRootElement() const
146  	{
147  		return m_xRoot;
148  	}
149  
150  	::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  
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  
282  ExtensionDescription::~ExtensionDescription()
283  {
284  }
285  
286  //======================================================================
287  FileDoesNotExistFilter::FileDoesNotExistFilter(
288  	const Reference< css::ucb::XCommandEnvironment >& xCmdEnv):
289  	m_bExist(true), m_xCommandEnv(xCmdEnv)
290  {}
291  
292  FileDoesNotExistFilter::~FileDoesNotExistFilter()
293  {
294  };
295  
296  bool FileDoesNotExistFilter::exist()
297  {
298  	return m_bExist;
299  }
300      // XCommandEnvironment
301  Reference<css::task::XInteractionHandler >
302      FileDoesNotExistFilter::getInteractionHandler() throw (css::uno::RuntimeException)
303  {
304  	return static_cast<css::task::XInteractionHandler*>(this);
305  }
306  
307  Reference<css::ucb::XProgressHandler >
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
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  
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  
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  
392  DescriptionInfoset::~DescriptionInfoset() {}
393  
394  ::boost::optional< ::rtl::OUString > DescriptionInfoset::getIdentifier() const {
395      return getOptionalValue(
396          ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("desc:identifier/@value")));
397  }
398  
399  ::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  
412  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 todo
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  
511  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  
524  ::rtl::OUString DescriptionInfoset::getVersion() const
525  {
526      return getNodeValueFromExpression( ::rtl::OUString(
527              RTL_CONSTASCII_USTRINGPARAM("desc:version/@value")));
528  }
529  
530  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 contained 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 >
569  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 >
585  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 >
593  DescriptionInfoset::getUpdateDownloadUrls() const
594  {
595      return getUrls(
596          ::rtl::OUString(
597              RTL_CONSTASCII_USTRINGPARAM(
598                  "desc:update-download/desc:src/@xlink:href")));
599  }
600  
601  ::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  
617  ::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  
631  ::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  
647  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  
665  ::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  
699  ::rtl::OUString DescriptionInfoset::getLocalizedReleaseNotesURL() const
700  {
701      return getLocalizedHREFAttrFromChild(::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
702          "/desc:description/desc:release-notes")), NULL);
703  }
704  
705  ::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  
724  ::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>
732  DescriptionInfoset::getSimpleLicenseAttributes() const
733  {
734      //Check if the node exist
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  
775  ::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 >
782  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>
817  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>
834  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>
877  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/orvariant, 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>
912  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  
949  ::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