1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <cppuhelper/exc_hlp.hxx>
22 #include <osl/file.hxx>
23 #include <com/sun/star/deployment/DeploymentException.hpp>
24 #include <com/sun/star/uno/XComponentContext.hpp>
25 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
26 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
27 #include <com/sun/star/io/XActiveDataSource.hpp>
28 #include <com/sun/star/io/XActiveDataControl.hpp>
30 #include <ucbhelper/content.hxx>
31 #include <xmlscript/xml_helper.hxx>
32 #include <dp_backenddb.hxx>
35 using namespace ::com::sun::star::uno
;
38 namespace dp_registry::backend
{
41 Reference
<css::uno::XComponentContext
> const & xContext
,
42 OUString
const & url
):
45 m_urlDb
= dp_misc::expandUnoRcUrl(url
);
48 void BackendDb::save()
50 const Reference
<css::io::XActiveDataSource
> xDataSource(m_doc
,css::uno::UNO_QUERY_THROW
);
51 std::vector
<sal_Int8
> bytes
;
52 xDataSource
->setOutputStream(::xmlscript::createOutputStream(&bytes
));
53 const Reference
<css::io::XActiveDataControl
> xDataControl(m_doc
,css::uno::UNO_QUERY_THROW
);
54 xDataControl
->start();
56 const Reference
<css::io::XInputStream
> xData(
57 ::xmlscript::createInputStream(std::move(bytes
)));
58 ::ucbhelper::Content
ucbDb(m_urlDb
, nullptr, m_xContext
);
59 ucbDb
.writeStream(xData
, true /*replace existing*/);
62 css::uno::Reference
<css::xml::dom::XDocument
> const & BackendDb::getDocument()
66 const Reference
<css::xml::dom::XDocumentBuilder
> xDocBuilder(
67 css::xml::dom::DocumentBuilder::create(m_xContext
) );
69 ::osl::DirectoryItem item
;
70 ::osl::File::RC err
= ::osl::DirectoryItem::get(m_urlDb
, item
);
71 if (err
== ::osl::File::E_None
)
73 ::ucbhelper::Content
descContent(
74 m_urlDb
, css::uno::Reference
<css::ucb::XCommandEnvironment
>(),
76 Reference
<css::io::XInputStream
> xIn
= descContent
.openStream();
77 m_doc
= xDocBuilder
->parse(xIn
);
79 else if (err
== ::osl::File::E_NOENT
)
81 //Create a new document and insert some basic stuff
82 m_doc
= xDocBuilder
->newDocument();
83 const Reference
<css::xml::dom::XElement
> rootNode
=
84 m_doc
->createElementNS(getDbNSName(), getNSPrefix() +
85 ":" + getRootElementName());
87 m_doc
->appendChild(Reference
<css::xml::dom::XNode
>(
88 rootNode
, UNO_QUERY_THROW
));
92 throw css::uno::RuntimeException(
93 "Extension manager could not access database file:"
97 throw css::uno::RuntimeException(
98 "Extension manager could not get root node of data base file: "
105 Reference
<css::xml::xpath::XXPathAPI
> const & BackendDb::getXPathAPI()
107 if (!m_xpathApi
.is())
109 m_xpathApi
= css::xml::xpath::XPathAPI::create( m_xContext
);
111 m_xpathApi
->registerNS( getNSPrefix(), getDbNSName() );
117 void BackendDb::removeElement(OUString
const & sXPathExpression
)
121 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
122 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
123 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
124 //find the extension element that is to be removed
125 const Reference
<css::xml::dom::XNode
> aNode
=
126 xpathApi
->selectSingleNode(root
, sXPathExpression
);
130 root
->removeChild(aNode
);
134 #if OSL_DEBUG_LEVEL > 0
135 //There must not be any other entry with the same url
136 const Reference
<css::xml::dom::XNode
> nextNode
=
137 xpathApi
->selectSingleNode(root
, sXPathExpression
);
138 OSL_ASSERT(! nextNode
.is());
141 catch(const css::uno::Exception
&)
143 Any
exc( ::cppu::getCaughtException() );
144 throw css::deployment::DeploymentException(
145 "Extension Manager: failed to write data entry in backend db: " +
146 m_urlDb
, nullptr, exc
);
150 void BackendDb::removeEntry(std::u16string_view url
)
152 const OUString sKeyElement
= getKeyElementName();
153 const OUString sPrefix
= getNSPrefix();
154 OUString sExpression
=
162 removeElement(sExpression
);
165 void BackendDb::revokeEntry(std::u16string_view url
)
169 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
172 entry
->setAttribute("revoked", "true");
176 catch(const css::uno::Exception
&)
178 Any
exc( ::cppu::getCaughtException() );
179 throw css::deployment::DeploymentException(
180 "Extension Manager: failed to revoke data entry in backend db: " +
181 m_urlDb
, nullptr, exc
);
185 bool BackendDb::activateEntry(std::u16string_view url
)
190 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
193 //no attribute "active" means it is active, that is, registered.
194 entry
->removeAttribute("revoked");
200 catch(const css::uno::Exception
&)
202 Any
exc( ::cppu::getCaughtException() );
203 throw css::deployment::DeploymentException(
204 "Extension Manager: failed to revoke data entry in backend db: " +
205 m_urlDb
, nullptr, exc
);
209 bool BackendDb::hasActiveEntry(std::u16string_view url
)
214 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
217 OUString sActive
= entry
->getAttribute("revoked");
218 if (!(sActive
== "true"))
224 catch(const css::uno::Exception
&)
226 Any
exc( ::cppu::getCaughtException() );
227 throw css::deployment::DeploymentException(
228 "Extension Manager: failed to determine an active entry in backend db: " +
229 m_urlDb
, nullptr, exc
);
233 Reference
<css::xml::dom::XNode
> BackendDb::getKeyElement(
234 std::u16string_view url
)
238 const OUString sPrefix
= getNSPrefix();
239 const OUString sKeyElement
= getKeyElementName();
240 OUString sExpression
=
248 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
249 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
250 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
251 return xpathApi
->selectSingleNode(root
, sExpression
);
253 catch(const css::uno::Exception
&)
255 Any
exc( ::cppu::getCaughtException() );
256 throw css::deployment::DeploymentException(
257 "Extension Manager: failed to read key element in backend db: " +
258 m_urlDb
, nullptr, exc
);
262 //Only writes the data if there is at least one entry
263 void BackendDb::writeVectorOfPair(
264 std::vector
< std::pair
< OUString
, OUString
> > const & vecPairs
,
265 std::u16string_view sVectorTagName
,
266 std::u16string_view sPairTagName
,
267 std::u16string_view sFirstTagName
,
268 std::u16string_view sSecondTagName
,
269 css::uno::Reference
<css::xml::dom::XNode
> const & xParent
)
272 if (vecPairs
.empty())
274 const OUString sNameSpace
= getDbNSName();
275 OSL_ASSERT(!sNameSpace
.isEmpty());
276 const OUString
sPrefix(getNSPrefix() + ":");
277 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
279 const Reference
<css::xml::dom::XElement
> vectorNode(
280 doc
->createElementNS(sNameSpace
, sPrefix
+ sVectorTagName
));
282 xParent
->appendChild(
283 Reference
<css::xml::dom::XNode
>(
284 vectorNode
, css::uno::UNO_QUERY_THROW
));
285 for (auto const& vecPair
: vecPairs
)
287 const Reference
<css::xml::dom::XElement
> pairNode(
288 doc
->createElementNS(sNameSpace
, sPrefix
+ sPairTagName
));
290 vectorNode
->appendChild(
291 Reference
<css::xml::dom::XNode
>(
292 pairNode
, css::uno::UNO_QUERY_THROW
));
294 const Reference
<css::xml::dom::XElement
> firstNode(
295 doc
->createElementNS(sNameSpace
, sPrefix
+ sFirstTagName
));
297 pairNode
->appendChild(
298 Reference
<css::xml::dom::XNode
>(
299 firstNode
, css::uno::UNO_QUERY_THROW
));
301 const Reference
<css::xml::dom::XText
> firstTextNode(
302 doc
->createTextNode( vecPair
.first
));
304 firstNode
->appendChild(
305 Reference
<css::xml::dom::XNode
>(
306 firstTextNode
, css::uno::UNO_QUERY_THROW
));
308 const Reference
<css::xml::dom::XElement
> secondNode(
309 doc
->createElementNS(sNameSpace
, sPrefix
+ sSecondTagName
));
311 pairNode
->appendChild(
312 Reference
<css::xml::dom::XNode
>(
313 secondNode
, css::uno::UNO_QUERY_THROW
));
315 const Reference
<css::xml::dom::XText
> secondTextNode(
316 doc
->createTextNode( vecPair
.second
));
318 secondNode
->appendChild(
319 Reference
<css::xml::dom::XNode
>(
320 secondTextNode
, css::uno::UNO_QUERY_THROW
));
323 catch(const css::uno::Exception
&)
325 Any
exc( ::cppu::getCaughtException() );
326 throw css::deployment::DeploymentException(
327 "Extension Manager: failed to write data entry in backend db: " +
328 m_urlDb
, nullptr, exc
);
332 std::vector
< std::pair
< OUString
, OUString
> >
333 BackendDb::readVectorOfPair(
334 Reference
<css::xml::dom::XNode
> const & parent
,
335 std::u16string_view sListTagName
,
336 std::u16string_view sPairTagName
,
337 std::u16string_view sFirstTagName
,
338 std::u16string_view sSecondTagName
)
342 OSL_ASSERT(parent
.is());
343 const OUString
sPrefix(getNSPrefix() + ":");
344 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
345 const OUString
sExprPairs(
346 sPrefix
+ sListTagName
+ "/" + sPrefix
+ sPairTagName
);
347 const Reference
<css::xml::dom::XNodeList
> listPairs
=
348 xpathApi
->selectNodeList(parent
, sExprPairs
);
350 std::vector
< std::pair
< OUString
, OUString
> > retVector
;
351 sal_Int32 length
= listPairs
->getLength();
352 for (sal_Int32 i
= 0; i
< length
; i
++)
354 const Reference
<css::xml::dom::XNode
> aPair
= listPairs
->item(i
);
355 const OUString
sExprFirst(sPrefix
+ sFirstTagName
+ "/text()");
356 const Reference
<css::xml::dom::XNode
> first
=
357 xpathApi
->selectSingleNode(aPair
, sExprFirst
);
359 const OUString
sExprSecond(sPrefix
+ sSecondTagName
+ "/text()");
360 const Reference
<css::xml::dom::XNode
> second
=
361 xpathApi
->selectSingleNode(aPair
, sExprSecond
);
362 OSL_ASSERT(first
.is() && second
.is());
364 retVector
.emplace_back(
365 first
->getNodeValue(), second
->getNodeValue());
369 catch(const css::uno::Exception
&)
371 Any
exc( ::cppu::getCaughtException() );
372 throw css::deployment::DeploymentException(
373 "Extension Manager: failed to read data entry in backend db: " +
374 m_urlDb
, nullptr, exc
);
378 //Only writes the data if there is at least one entry
379 void BackendDb::writeSimpleList(
380 std::deque
< OUString
> const & list
,
381 std::u16string_view sListTagName
,
382 std::u16string_view sMemberTagName
,
383 Reference
<css::xml::dom::XNode
> const & xParent
)
389 const OUString sNameSpace
= getDbNSName();
390 const OUString
sPrefix(getNSPrefix() + ":");
391 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
393 const Reference
<css::xml::dom::XElement
> listNode(
394 doc
->createElementNS(sNameSpace
, sPrefix
+ sListTagName
));
396 xParent
->appendChild(
397 Reference
<css::xml::dom::XNode
>(
398 listNode
, css::uno::UNO_QUERY_THROW
));
400 for (auto const& elem
: list
)
402 const Reference
<css::xml::dom::XNode
> memberNode(
403 doc
->createElementNS(sNameSpace
, sPrefix
+ sMemberTagName
), css::uno::UNO_QUERY_THROW
);
405 listNode
->appendChild(memberNode
);
407 const Reference
<css::xml::dom::XNode
> textNode(
408 doc
->createTextNode(elem
), css::uno::UNO_QUERY_THROW
);
410 memberNode
->appendChild(textNode
);
413 catch(const css::uno::Exception
&)
415 Any
exc( ::cppu::getCaughtException() );
416 throw css::deployment::DeploymentException(
417 "Extension Manager: failed to write data entry in backend db: " +
418 m_urlDb
, nullptr, exc
);
422 //Writes only the element if is has a value.
423 //The prefix is automatically added to the element name
424 void BackendDb::writeSimpleElement(
425 std::u16string_view sElementName
, OUString
const & value
,
426 Reference
<css::xml::dom::XNode
> const & xParent
)
432 const OUString sPrefix
= getNSPrefix();
433 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
434 const OUString sNameSpace
= getDbNSName();
435 const Reference
<css::xml::dom::XNode
> dataNode(
436 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sElementName
),
438 xParent
->appendChild(dataNode
);
440 const Reference
<css::xml::dom::XNode
> dataValue(
441 doc
->createTextNode(value
), UNO_QUERY_THROW
);
442 dataNode
->appendChild(dataValue
);
444 catch(const css::uno::Exception
&)
446 Any
exc( ::cppu::getCaughtException() );
447 throw css::deployment::DeploymentException(
448 "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " +
449 m_urlDb
, nullptr, exc
);
454 /// The key elements have a url attribute and are always children of the root element.
455 Reference
<css::xml::dom::XNode
> BackendDb::writeKeyElement(
456 OUString
const & url
)
460 const OUString sNameSpace
= getDbNSName();
461 const OUString sPrefix
= getNSPrefix();
462 const OUString sElementName
= getKeyElementName();
463 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
464 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
466 //Check if there are an entry with the same url. This can be the case if the
467 //status of an XPackage is ambiguous. In this case a call to activateExtension
468 //(dp_extensionmanager.cxx), will register the package again. See also
469 //Package::processPackage_impl in dp_backend.cxx.
470 //A package can become
471 //invalid after its successful registration, for example if a second extension with
472 //the same service is installed.
473 const OUString
sExpression(
474 sPrefix
+ ":" + sElementName
+ "[@url = \"" + url
+ "\"]");
475 const Reference
<css::xml::dom::XNode
> existingNode
=
476 getXPathAPI()->selectSingleNode(root
, sExpression
);
477 if (existingNode
.is())
480 //replace the existing entry.
484 const Reference
<css::xml::dom::XElement
> keyElement(
485 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sElementName
));
487 keyElement
->setAttribute("url", url
);
489 const Reference
<css::xml::dom::XNode
> keyNode(
490 keyElement
, UNO_QUERY_THROW
);
491 root
->appendChild(keyNode
);
494 catch(const css::uno::Exception
&)
496 Any
exc( ::cppu::getCaughtException() );
497 throw css::deployment::DeploymentException(
498 "Extension Manager: failed to write key element in backend db: " +
499 m_urlDb
, nullptr, exc
);
503 OUString
BackendDb::readSimpleElement(
504 std::u16string_view sElementName
, Reference
<css::xml::dom::XNode
> const & xParent
)
508 const OUString sPrefix
= getNSPrefix();
509 const OUString
sExpr(sPrefix
+ ":" + sElementName
+ "/text()");
510 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
511 const Reference
<css::xml::dom::XNode
> val
=
512 xpathApi
->selectSingleNode(xParent
, sExpr
);
514 return val
->getNodeValue();
517 catch(const css::uno::Exception
&)
519 Any
exc( ::cppu::getCaughtException() );
520 throw css::deployment::DeploymentException(
521 "Extension Manager: failed to read data (readSimpleElement) in backend db: " +
522 m_urlDb
, nullptr, exc
);
527 std::deque
< OUString
> BackendDb::readList(
528 Reference
<css::xml::dom::XNode
> const & parent
,
529 std::u16string_view sListTagName
,
530 std::u16string_view sMemberTagName
)
534 OSL_ASSERT(parent
.is());
535 const OUString
sPrefix(getNSPrefix() + ":");
536 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
537 const OUString
sExprList(
538 sPrefix
+ sListTagName
+ "/" + sPrefix
+ sMemberTagName
+ "/text()");
539 const Reference
<css::xml::dom::XNodeList
> list
=
540 xpathApi
->selectNodeList(parent
, sExprList
);
542 std::deque
<OUString
> retList
;
543 sal_Int32 length
= list
->getLength();
544 for (sal_Int32 i
= 0; i
< length
; i
++)
546 const Reference
<css::xml::dom::XNode
> member
= list
->item(i
);
547 retList
.push_back(member
->getNodeValue());
551 catch(const css::uno::Exception
&)
553 Any
exc( ::cppu::getCaughtException() );
554 throw css::deployment::DeploymentException(
555 "Extension Manager: failed to read data entry in backend db: " +
556 m_urlDb
, nullptr, exc
);
560 std::vector
<OUString
> BackendDb::getOneChildFromAllEntries(
561 std::u16string_view name
)
565 std::vector
<OUString
> listRet
;
566 Reference
<css::xml::dom::XDocument
> doc
= getDocument();
567 Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
569 Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
570 const OUString sPrefix
= getNSPrefix();
571 const OUString sKeyElement
= getKeyElementName();
572 OUString sNodeSelectExpr
=
582 Reference
<css::xml::dom::XNodeList
> nodes
=
583 xpathApi
->selectNodeList(root
, sNodeSelectExpr
);
586 sal_Int32 length
= nodes
->getLength();
587 for (sal_Int32 i
= 0; i
< length
; i
++)
588 listRet
.push_back(nodes
->item(i
)->getNodeValue());
592 catch ( const css::deployment::DeploymentException
& )
596 catch(const css::uno::Exception
&)
598 Any
exc( ::cppu::getCaughtException() );
599 throw css::deployment::DeploymentException(
600 "Extension Manager: failed to read data entry in backend db: " +
601 m_urlDb
, nullptr, exc
);
606 RegisteredDb::RegisteredDb(
607 Reference
<XComponentContext
> const & xContext
,
608 OUString
const & url
):BackendDb(xContext
, url
)
612 void RegisteredDb::addEntry(OUString
const & url
)
615 if (!activateEntry(url
))
617 const OUString sNameSpace
= getDbNSName();
618 const OUString sPrefix
= getNSPrefix();
619 const OUString sEntry
= getKeyElementName();
621 Reference
<css::xml::dom::XDocument
> doc
= getDocument();
622 Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
624 #if OSL_DEBUG_LEVEL > 0
625 //There must not be yet an entry with the same url
626 OUString
sExpression(
627 sPrefix
+ ":" + sEntry
+ "[@url = \"" + url
+ "\"]");
628 Reference
<css::xml::dom::XNode
> _extensionNode
=
629 getXPathAPI()->selectSingleNode(root
, sExpression
);
630 OSL_ASSERT(! _extensionNode
.is());
632 Reference
<css::xml::dom::XElement
> helpElement(
633 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sEntry
));
635 helpElement
->setAttribute("url", url
);
637 Reference
<css::xml::dom::XNode
> helpNode(
638 helpElement
, UNO_QUERY_THROW
);
639 root
->appendChild(helpNode
);
644 catch(const css::uno::Exception
&)
646 Any
exc( ::cppu::getCaughtException() );
647 throw css::deployment::DeploymentException(
648 "Extension Manager: failed to write data entry in backend db: " +
649 m_urlDb
, nullptr, exc
);
653 } // namespace dp_registry
655 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */