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/diagnose.h>
23 #include <osl/file.hxx>
24 #include <com/sun/star/deployment/DeploymentException.hpp>
25 #include <com/sun/star/uno/XComponentContext.hpp>
26 #include <com/sun/star/xml/dom/DocumentBuilder.hpp>
27 #include <com/sun/star/xml/xpath/XPathAPI.hpp>
28 #include <com/sun/star/io/XActiveDataSource.hpp>
29 #include <com/sun/star/io/XActiveDataControl.hpp>
31 #include <ucbhelper/content.hxx>
32 #include <xmlscript/xml_helper.hxx>
33 #include <dp_backenddb.hxx>
36 using namespace ::com::sun::star::uno
;
39 namespace dp_registry::backend
{
42 Reference
<css::uno::XComponentContext
> const & xContext
,
43 OUString
const & url
):
46 m_urlDb
= dp_misc::expandUnoRcUrl(url
);
49 void BackendDb::save()
51 const Reference
<css::io::XActiveDataSource
> xDataSource(m_doc
,css::uno::UNO_QUERY_THROW
);
52 std::vector
<sal_Int8
> bytes
;
53 xDataSource
->setOutputStream(::xmlscript::createOutputStream(&bytes
));
54 const Reference
<css::io::XActiveDataControl
> xDataControl(m_doc
,css::uno::UNO_QUERY_THROW
);
55 xDataControl
->start();
57 const Reference
<css::io::XInputStream
> xData(
58 ::xmlscript::createInputStream(std::move(bytes
)));
59 ::ucbhelper::Content
ucbDb(m_urlDb
, nullptr, m_xContext
);
60 ucbDb
.writeStream(xData
, true /*replace existing*/);
63 css::uno::Reference
<css::xml::dom::XDocument
> const & BackendDb::getDocument()
67 const Reference
<css::xml::dom::XDocumentBuilder
> xDocBuilder(
68 css::xml::dom::DocumentBuilder::create(m_xContext
) );
70 ::osl::DirectoryItem item
;
71 ::osl::File::RC err
= ::osl::DirectoryItem::get(m_urlDb
, item
);
72 if (err
== ::osl::File::E_None
)
74 ::ucbhelper::Content
descContent(
75 m_urlDb
, css::uno::Reference
<css::ucb::XCommandEnvironment
>(),
77 Reference
<css::io::XInputStream
> xIn
= descContent
.openStream();
78 m_doc
= xDocBuilder
->parse(xIn
);
80 else if (err
== ::osl::File::E_NOENT
)
82 //Create a new document and insert some basic stuff
83 m_doc
= xDocBuilder
->newDocument();
84 const Reference
<css::xml::dom::XElement
> rootNode
=
85 m_doc
->createElementNS(getDbNSName(), getNSPrefix() +
86 ":" + getRootElementName());
88 m_doc
->appendChild(Reference
<css::xml::dom::XNode
>(
89 rootNode
, UNO_QUERY_THROW
));
93 throw css::uno::RuntimeException(
94 "Extension manager could not access database file:"
98 throw css::uno::RuntimeException(
99 "Extension manager could not get root node of data base file: "
106 Reference
<css::xml::xpath::XXPathAPI
> const & BackendDb::getXPathAPI()
108 if (!m_xpathApi
.is())
110 m_xpathApi
= css::xml::xpath::XPathAPI::create( m_xContext
);
112 m_xpathApi
->registerNS( getNSPrefix(), getDbNSName() );
118 void BackendDb::removeElement(OUString
const & sXPathExpression
)
122 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
123 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
124 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
125 //find the extension element that is to be removed
126 const Reference
<css::xml::dom::XNode
> aNode
=
127 xpathApi
->selectSingleNode(root
, sXPathExpression
);
131 root
->removeChild(aNode
);
135 #if OSL_DEBUG_LEVEL > 0
136 //There must not be any other entry with the same url
137 const Reference
<css::xml::dom::XNode
> nextNode
=
138 xpathApi
->selectSingleNode(root
, sXPathExpression
);
139 OSL_ASSERT(! nextNode
.is());
142 catch(const css::uno::Exception
&)
144 Any
exc( ::cppu::getCaughtException() );
145 throw css::deployment::DeploymentException(
146 "Extension Manager: failed to write data entry in backend db: " +
147 m_urlDb
, nullptr, exc
);
151 void BackendDb::removeEntry(std::u16string_view url
)
153 const OUString sKeyElement
= getKeyElementName();
154 const OUString sPrefix
= getNSPrefix();
155 OUString sExpression
=
163 removeElement(sExpression
);
166 void BackendDb::revokeEntry(std::u16string_view url
)
170 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
173 entry
->setAttribute(u
"revoked"_ustr
, u
"true"_ustr
);
177 catch(const css::uno::Exception
&)
179 Any
exc( ::cppu::getCaughtException() );
180 throw css::deployment::DeploymentException(
181 "Extension Manager: failed to revoke data entry in backend db: " +
182 m_urlDb
, nullptr, exc
);
186 bool BackendDb::activateEntry(std::u16string_view url
)
191 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
194 //no attribute "active" means it is active, that is, registered.
195 entry
->removeAttribute(u
"revoked"_ustr
);
201 catch(const css::uno::Exception
&)
203 Any
exc( ::cppu::getCaughtException() );
204 throw css::deployment::DeploymentException(
205 "Extension Manager: failed to revoke data entry in backend db: " +
206 m_urlDb
, nullptr, exc
);
210 bool BackendDb::hasActiveEntry(std::u16string_view url
)
215 Reference
<css::xml::dom::XElement
> entry(getKeyElement(url
), UNO_QUERY
);
218 OUString sActive
= entry
->getAttribute(u
"revoked"_ustr
);
219 if (!(sActive
== "true"))
225 catch(const css::uno::Exception
&)
227 Any
exc( ::cppu::getCaughtException() );
228 throw css::deployment::DeploymentException(
229 "Extension Manager: failed to determine an active entry in backend db: " +
230 m_urlDb
, nullptr, exc
);
234 Reference
<css::xml::dom::XNode
> BackendDb::getKeyElement(
235 std::u16string_view url
)
239 const OUString sPrefix
= getNSPrefix();
240 const OUString sKeyElement
= getKeyElementName();
241 OUString sExpression
=
249 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
250 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
251 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
252 return xpathApi
->selectSingleNode(root
, sExpression
);
254 catch(const css::uno::Exception
&)
256 Any
exc( ::cppu::getCaughtException() );
257 throw css::deployment::DeploymentException(
258 "Extension Manager: failed to read key element in backend db: " +
259 m_urlDb
, nullptr, exc
);
263 //Only writes the data if there is at least one entry
264 void BackendDb::writeVectorOfPair(
265 std::vector
< std::pair
< OUString
, OUString
> > const & vecPairs
,
266 std::u16string_view sVectorTagName
,
267 std::u16string_view sPairTagName
,
268 std::u16string_view sFirstTagName
,
269 std::u16string_view sSecondTagName
,
270 css::uno::Reference
<css::xml::dom::XNode
> const & xParent
)
273 if (vecPairs
.empty())
275 const OUString sNameSpace
= getDbNSName();
276 OSL_ASSERT(!sNameSpace
.isEmpty());
277 const OUString
sPrefix(getNSPrefix() + ":");
278 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
280 const Reference
<css::xml::dom::XElement
> vectorNode(
281 doc
->createElementNS(sNameSpace
, sPrefix
+ sVectorTagName
));
283 xParent
->appendChild(
284 Reference
<css::xml::dom::XNode
>(
285 vectorNode
, css::uno::UNO_QUERY_THROW
));
286 for (auto const& vecPair
: vecPairs
)
288 const Reference
<css::xml::dom::XElement
> pairNode(
289 doc
->createElementNS(sNameSpace
, sPrefix
+ sPairTagName
));
291 vectorNode
->appendChild(
292 Reference
<css::xml::dom::XNode
>(
293 pairNode
, css::uno::UNO_QUERY_THROW
));
295 const Reference
<css::xml::dom::XElement
> firstNode(
296 doc
->createElementNS(sNameSpace
, sPrefix
+ sFirstTagName
));
298 pairNode
->appendChild(
299 Reference
<css::xml::dom::XNode
>(
300 firstNode
, css::uno::UNO_QUERY_THROW
));
302 const Reference
<css::xml::dom::XText
> firstTextNode(
303 doc
->createTextNode( vecPair
.first
));
305 firstNode
->appendChild(
306 Reference
<css::xml::dom::XNode
>(
307 firstTextNode
, css::uno::UNO_QUERY_THROW
));
309 const Reference
<css::xml::dom::XElement
> secondNode(
310 doc
->createElementNS(sNameSpace
, sPrefix
+ sSecondTagName
));
312 pairNode
->appendChild(
313 Reference
<css::xml::dom::XNode
>(
314 secondNode
, css::uno::UNO_QUERY_THROW
));
316 const Reference
<css::xml::dom::XText
> secondTextNode(
317 doc
->createTextNode( vecPair
.second
));
319 secondNode
->appendChild(
320 Reference
<css::xml::dom::XNode
>(
321 secondTextNode
, css::uno::UNO_QUERY_THROW
));
324 catch(const css::uno::Exception
&)
326 Any
exc( ::cppu::getCaughtException() );
327 throw css::deployment::DeploymentException(
328 "Extension Manager: failed to write data entry in backend db: " +
329 m_urlDb
, nullptr, exc
);
333 std::vector
< std::pair
< OUString
, OUString
> >
334 BackendDb::readVectorOfPair(
335 Reference
<css::xml::dom::XNode
> const & parent
,
336 std::u16string_view sListTagName
,
337 std::u16string_view sPairTagName
,
338 std::u16string_view sFirstTagName
,
339 std::u16string_view sSecondTagName
)
343 OSL_ASSERT(parent
.is());
344 const OUString
sPrefix(getNSPrefix() + ":");
345 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
346 const OUString
sExprPairs(
347 sPrefix
+ sListTagName
+ "/" + sPrefix
+ sPairTagName
);
348 const Reference
<css::xml::dom::XNodeList
> listPairs
=
349 xpathApi
->selectNodeList(parent
, sExprPairs
);
351 std::vector
< std::pair
< OUString
, OUString
> > retVector
;
352 sal_Int32 length
= listPairs
->getLength();
353 for (sal_Int32 i
= 0; i
< length
; i
++)
355 const Reference
<css::xml::dom::XNode
> aPair
= listPairs
->item(i
);
356 const OUString
sExprFirst(sPrefix
+ sFirstTagName
+ "/text()");
357 const Reference
<css::xml::dom::XNode
> first
=
358 xpathApi
->selectSingleNode(aPair
, sExprFirst
);
360 const OUString
sExprSecond(sPrefix
+ sSecondTagName
+ "/text()");
361 const Reference
<css::xml::dom::XNode
> second
=
362 xpathApi
->selectSingleNode(aPair
, sExprSecond
);
363 OSL_ASSERT(first
.is() && second
.is());
365 retVector
.emplace_back(
366 first
->getNodeValue(), second
->getNodeValue());
370 catch(const css::uno::Exception
&)
372 Any
exc( ::cppu::getCaughtException() );
373 throw css::deployment::DeploymentException(
374 "Extension Manager: failed to read data entry in backend db: " +
375 m_urlDb
, nullptr, exc
);
379 //Only writes the data if there is at least one entry
380 void BackendDb::writeSimpleList(
381 std::deque
< OUString
> const & list
,
382 std::u16string_view sListTagName
,
383 std::u16string_view sMemberTagName
,
384 Reference
<css::xml::dom::XNode
> const & xParent
)
390 const OUString sNameSpace
= getDbNSName();
391 const OUString
sPrefix(getNSPrefix() + ":");
392 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
394 const Reference
<css::xml::dom::XElement
> listNode(
395 doc
->createElementNS(sNameSpace
, sPrefix
+ sListTagName
));
397 xParent
->appendChild(
398 Reference
<css::xml::dom::XNode
>(
399 listNode
, css::uno::UNO_QUERY_THROW
));
401 for (auto const& elem
: list
)
403 const Reference
<css::xml::dom::XNode
> memberNode(
404 doc
->createElementNS(sNameSpace
, sPrefix
+ sMemberTagName
), css::uno::UNO_QUERY_THROW
);
406 listNode
->appendChild(memberNode
);
408 const Reference
<css::xml::dom::XNode
> textNode(
409 doc
->createTextNode(elem
), css::uno::UNO_QUERY_THROW
);
411 memberNode
->appendChild(textNode
);
414 catch(const css::uno::Exception
&)
416 Any
exc( ::cppu::getCaughtException() );
417 throw css::deployment::DeploymentException(
418 "Extension Manager: failed to write data entry in backend db: " +
419 m_urlDb
, nullptr, exc
);
423 //Writes only the element if is has a value.
424 //The prefix is automatically added to the element name
425 void BackendDb::writeSimpleElement(
426 std::u16string_view sElementName
, OUString
const & value
,
427 Reference
<css::xml::dom::XNode
> const & xParent
)
433 const OUString sPrefix
= getNSPrefix();
434 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
435 const OUString sNameSpace
= getDbNSName();
436 const Reference
<css::xml::dom::XNode
> dataNode(
437 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sElementName
),
439 xParent
->appendChild(dataNode
);
441 const Reference
<css::xml::dom::XNode
> dataValue(
442 doc
->createTextNode(value
), UNO_QUERY_THROW
);
443 dataNode
->appendChild(dataValue
);
445 catch(const css::uno::Exception
&)
447 Any
exc( ::cppu::getCaughtException() );
448 throw css::deployment::DeploymentException(
449 "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " +
450 m_urlDb
, nullptr, exc
);
455 /// The key elements have a url attribute and are always children of the root element.
456 Reference
<css::xml::dom::XNode
> BackendDb::writeKeyElement(
457 OUString
const & url
)
461 const OUString sNameSpace
= getDbNSName();
462 const OUString sPrefix
= getNSPrefix();
463 const OUString sElementName
= getKeyElementName();
464 const Reference
<css::xml::dom::XDocument
> doc
= getDocument();
465 const Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
467 //Check if there are an entry with the same url. This can be the case if the
468 //status of an XPackage is ambiguous. In this case a call to activateExtension
469 //(dp_extensionmanager.cxx), will register the package again. See also
470 //Package::processPackage_impl in dp_backend.cxx.
471 //A package can become
472 //invalid after its successful registration, for example if a second extension with
473 //the same service is installed.
474 const OUString
sExpression(
475 sPrefix
+ ":" + sElementName
+ "[@url = \"" + url
+ "\"]");
476 const Reference
<css::xml::dom::XNode
> existingNode
=
477 getXPathAPI()->selectSingleNode(root
, sExpression
);
478 if (existingNode
.is())
481 //replace the existing entry.
485 const Reference
<css::xml::dom::XElement
> keyElement(
486 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sElementName
));
488 keyElement
->setAttribute(u
"url"_ustr
, url
);
490 const Reference
<css::xml::dom::XNode
> keyNode(
491 keyElement
, UNO_QUERY_THROW
);
492 root
->appendChild(keyNode
);
495 catch(const css::uno::Exception
&)
497 Any
exc( ::cppu::getCaughtException() );
498 throw css::deployment::DeploymentException(
499 "Extension Manager: failed to write key element in backend db: " +
500 m_urlDb
, nullptr, exc
);
504 OUString
BackendDb::readSimpleElement(
505 std::u16string_view sElementName
, Reference
<css::xml::dom::XNode
> const & xParent
)
509 const OUString sPrefix
= getNSPrefix();
510 const OUString
sExpr(sPrefix
+ ":" + sElementName
+ "/text()");
511 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
512 const Reference
<css::xml::dom::XNode
> val
=
513 xpathApi
->selectSingleNode(xParent
, sExpr
);
515 return val
->getNodeValue();
518 catch(const css::uno::Exception
&)
520 Any
exc( ::cppu::getCaughtException() );
521 throw css::deployment::DeploymentException(
522 "Extension Manager: failed to read data (readSimpleElement) in backend db: " +
523 m_urlDb
, nullptr, exc
);
528 std::deque
< OUString
> BackendDb::readList(
529 Reference
<css::xml::dom::XNode
> const & parent
,
530 std::u16string_view sListTagName
,
531 std::u16string_view sMemberTagName
)
535 OSL_ASSERT(parent
.is());
536 const OUString
sPrefix(getNSPrefix() + ":");
537 const Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
538 const OUString
sExprList(
539 sPrefix
+ sListTagName
+ "/" + sPrefix
+ sMemberTagName
+ "/text()");
540 const Reference
<css::xml::dom::XNodeList
> list
=
541 xpathApi
->selectNodeList(parent
, sExprList
);
543 std::deque
<OUString
> retList
;
544 sal_Int32 length
= list
->getLength();
545 for (sal_Int32 i
= 0; i
< length
; i
++)
547 const Reference
<css::xml::dom::XNode
> member
= list
->item(i
);
548 retList
.push_back(member
->getNodeValue());
552 catch(const css::uno::Exception
&)
554 Any
exc( ::cppu::getCaughtException() );
555 throw css::deployment::DeploymentException(
556 "Extension Manager: failed to read data entry in backend db: " +
557 m_urlDb
, nullptr, exc
);
561 std::vector
<OUString
> BackendDb::getOneChildFromAllEntries(
562 std::u16string_view name
)
566 std::vector
<OUString
> listRet
;
567 Reference
<css::xml::dom::XDocument
> doc
= getDocument();
568 Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
570 Reference
<css::xml::xpath::XXPathAPI
> xpathApi
= getXPathAPI();
571 const OUString sPrefix
= getNSPrefix();
572 const OUString sKeyElement
= getKeyElementName();
573 OUString sNodeSelectExpr
=
583 Reference
<css::xml::dom::XNodeList
> nodes
=
584 xpathApi
->selectNodeList(root
, sNodeSelectExpr
);
587 sal_Int32 length
= nodes
->getLength();
588 for (sal_Int32 i
= 0; i
< length
; i
++)
589 listRet
.push_back(nodes
->item(i
)->getNodeValue());
593 catch ( const css::deployment::DeploymentException
& )
597 catch(const css::uno::Exception
&)
599 Any
exc( ::cppu::getCaughtException() );
600 throw css::deployment::DeploymentException(
601 "Extension Manager: failed to read data entry in backend db: " +
602 m_urlDb
, nullptr, exc
);
607 RegisteredDb::RegisteredDb(
608 Reference
<XComponentContext
> const & xContext
,
609 OUString
const & url
):BackendDb(xContext
, url
)
613 void RegisteredDb::addEntry(OUString
const & url
)
616 if (!activateEntry(url
))
618 const OUString sNameSpace
= getDbNSName();
619 const OUString sPrefix
= getNSPrefix();
620 const OUString sEntry
= getKeyElementName();
622 Reference
<css::xml::dom::XDocument
> doc
= getDocument();
623 Reference
<css::xml::dom::XNode
> root
= doc
->getFirstChild();
625 #if OSL_DEBUG_LEVEL > 0
626 //There must not be yet an entry with the same url
627 OUString
sExpression(
628 sPrefix
+ ":" + sEntry
+ "[@url = \"" + url
+ "\"]");
629 Reference
<css::xml::dom::XNode
> _extensionNode
=
630 getXPathAPI()->selectSingleNode(root
, sExpression
);
631 OSL_ASSERT(! _extensionNode
.is());
633 Reference
<css::xml::dom::XElement
> helpElement(
634 doc
->createElementNS(sNameSpace
, sPrefix
+ ":" + sEntry
));
636 helpElement
->setAttribute(u
"url"_ustr
, url
);
638 Reference
<css::xml::dom::XNode
> helpNode(
639 helpElement
, UNO_QUERY_THROW
);
640 root
->appendChild(helpNode
);
645 catch(const css::uno::Exception
&)
647 Any
exc( ::cppu::getCaughtException() );
648 throw css::deployment::DeploymentException(
649 "Extension Manager: failed to write data entry in backend db: " +
650 m_urlDb
, nullptr, exc
);
654 } // namespace dp_registry
656 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */