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