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