LibreOffice Module desktop (master) 1
dp_backenddb.cxx
Go to the documentation of this file.
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
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>
29#include <dp_misc.h>
30#include <ucbhelper/content.hxx>
32#include <dp_backenddb.hxx>
33
34
35using namespace ::com::sun::star::uno;
36
37
38namespace dp_registry::backend {
39
41 Reference<css::uno::XComponentContext> const & xContext,
42 OUString const & url):
44{
45 m_urlDb = dp_misc::expandUnoRcUrl(url);
46}
47
48void BackendDb::save()
49{
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();
55
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*/);
60}
61
62css::uno::Reference<css::xml::dom::XDocument> const & BackendDb::getDocument()
63{
64 if (!m_doc.is())
65 {
66 const Reference<css::xml::dom::XDocumentBuilder> xDocBuilder(
68
69 ::osl::DirectoryItem item;
70 ::osl::File::RC err = ::osl::DirectoryItem::get(m_urlDb, item);
71 if (err == ::osl::File::E_None)
72 {
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);
78 }
79 else if (err == ::osl::File::E_NOENT)
80 {
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());
86
87 m_doc->appendChild(Reference<css::xml::dom::XNode>(
88 rootNode, UNO_QUERY_THROW));
89 save();
90 }
91 else
92 throw css::uno::RuntimeException(
93 "Extension manager could not access database file:"
94 + m_urlDb, nullptr);
95
96 if (!m_doc.is())
97 throw css::uno::RuntimeException(
98 "Extension manager could not get root node of data base file: "
99 + m_urlDb, nullptr);
100 }
101
102 return m_doc;
103}
104
105Reference<css::xml::xpath::XXPathAPI> const & BackendDb::getXPathAPI()
106{
107 if (!m_xpathApi.is())
108 {
110
111 m_xpathApi->registerNS( getNSPrefix(), getDbNSName() );
112 }
113
114 return m_xpathApi;
115}
116
117void BackendDb::removeElement(OUString const & sXPathExpression)
118{
119 try
120 {
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);
127
128 if (aNode.is())
129 {
130 root->removeChild(aNode);
131 save();
132 }
133
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());
139#endif
140 }
141 catch(const css::uno::Exception &)
142 {
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);
147 }
148}
149
150void BackendDb::removeEntry(std::u16string_view url)
151{
152 const OUString sKeyElement = getKeyElementName();
153 const OUString sPrefix = getNSPrefix();
154 OUString sExpression =
155 sPrefix +
156 ":" +
157 sKeyElement +
158 "[@url = \"" +
159 url +
160 "\"]";
161
162 removeElement(sExpression);
163}
164
165void BackendDb::revokeEntry(std::u16string_view url)
166{
167 try
168 {
169 Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
170 if (entry.is())
171 {
172 entry->setAttribute("revoked", "true");
173 save();
174 }
175 }
176 catch(const css::uno::Exception &)
177 {
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);
182 }
183}
184
185bool BackendDb::activateEntry(std::u16string_view url)
186{
187 try
188 {
189 bool ret = false;
190 Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
191 if (entry.is())
192 {
193 //no attribute "active" means it is active, that is, registered.
194 entry->removeAttribute("revoked");
195 save();
196 ret = true;
197 }
198 return ret;
199 }
200 catch(const css::uno::Exception &)
201 {
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);
206 }
207}
208
209bool BackendDb::hasActiveEntry(std::u16string_view url)
210{
211 try
212 {
213 bool ret = false;
214 Reference<css::xml::dom::XElement> entry(getKeyElement(url), UNO_QUERY);
215 if (entry.is())
216 {
217 OUString sActive = entry->getAttribute("revoked");
218 if (!(sActive == "true"))
219 ret = true;
220 }
221 return ret;
222
223 }
224 catch(const css::uno::Exception &)
225 {
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);
230 }
231}
232
233Reference<css::xml::dom::XNode> BackendDb::getKeyElement(
234 std::u16string_view url)
235{
236 try
237 {
238 const OUString sPrefix = getNSPrefix();
239 const OUString sKeyElement = getKeyElementName();
240 OUString sExpression =
241 sPrefix +
242 ":" +
243 sKeyElement +
244 "[@url = \"" +
245 url +
246 "\"]";
247
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);
252 }
253 catch(const css::uno::Exception &)
254 {
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);
259 }
260}
261
262//Only writes the data if there is at least one entry
263void 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)
270{
271 try{
272 if (vecPairs.empty())
273 return;
274 const OUString sNameSpace = getDbNSName();
275 OSL_ASSERT(!sNameSpace.isEmpty());
276 const OUString sPrefix(getNSPrefix() + ":");
277 const Reference<css::xml::dom::XDocument> doc = getDocument();
278
279 const Reference<css::xml::dom::XElement> vectorNode(
280 doc->createElementNS(sNameSpace, sPrefix + sVectorTagName));
281
282 xParent->appendChild(
283 Reference<css::xml::dom::XNode>(
284 vectorNode, css::uno::UNO_QUERY_THROW));
285 for (auto const& vecPair : vecPairs)
286 {
287 const Reference<css::xml::dom::XElement> pairNode(
288 doc->createElementNS(sNameSpace, sPrefix + sPairTagName));
289
290 vectorNode->appendChild(
291 Reference<css::xml::dom::XNode>(
292 pairNode, css::uno::UNO_QUERY_THROW));
293
294 const Reference<css::xml::dom::XElement> firstNode(
295 doc->createElementNS(sNameSpace, sPrefix + sFirstTagName));
296
297 pairNode->appendChild(
298 Reference<css::xml::dom::XNode>(
299 firstNode, css::uno::UNO_QUERY_THROW));
300
301 const Reference<css::xml::dom::XText> firstTextNode(
302 doc->createTextNode( vecPair.first));
303
304 firstNode->appendChild(
305 Reference<css::xml::dom::XNode>(
306 firstTextNode, css::uno::UNO_QUERY_THROW));
307
308 const Reference<css::xml::dom::XElement> secondNode(
309 doc->createElementNS(sNameSpace, sPrefix + sSecondTagName));
310
311 pairNode->appendChild(
312 Reference<css::xml::dom::XNode>(
313 secondNode, css::uno::UNO_QUERY_THROW));
314
315 const Reference<css::xml::dom::XText> secondTextNode(
316 doc->createTextNode( vecPair.second));
317
318 secondNode->appendChild(
319 Reference<css::xml::dom::XNode>(
320 secondTextNode, css::uno::UNO_QUERY_THROW));
321 }
322 }
323 catch(const css::uno::Exception &)
324 {
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);
329 }
330}
331
332std::vector< std::pair< OUString, OUString > >
333BackendDb::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)
339{
340 try
341 {
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);
349
350 std::vector< std::pair< OUString, OUString > > retVector;
351 sal_Int32 length = listPairs->getLength();
352 for (sal_Int32 i = 0; i < length; i++)
353 {
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);
358
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());
363
364 retVector.emplace_back(
365 first->getNodeValue(), second->getNodeValue());
366 }
367 return retVector;
368 }
369 catch(const css::uno::Exception &)
370 {
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);
375 }
376}
377
378//Only writes the data if there is at least one entry
379void 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)
384{
385 try
386 {
387 if (list.empty())
388 return;
389 const OUString sNameSpace = getDbNSName();
390 const OUString sPrefix(getNSPrefix() + ":");
391 const Reference<css::xml::dom::XDocument> doc = getDocument();
392
393 const Reference<css::xml::dom::XElement> listNode(
394 doc->createElementNS(sNameSpace, sPrefix + sListTagName));
395
396 xParent->appendChild(
397 Reference<css::xml::dom::XNode>(
398 listNode, css::uno::UNO_QUERY_THROW));
399
400 for (auto const& elem : list)
401 {
402 const Reference<css::xml::dom::XNode> memberNode(
403 doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW);
404
405 listNode->appendChild(memberNode);
406
407 const Reference<css::xml::dom::XNode> textNode(
408 doc->createTextNode(elem), css::uno::UNO_QUERY_THROW);
409
410 memberNode->appendChild(textNode);
411 }
412 }
413 catch(const css::uno::Exception &)
414 {
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);
419 }
420}
421
422//Writes only the element if is has a value.
423//The prefix is automatically added to the element name
424void BackendDb::writeSimpleElement(
425 std::u16string_view sElementName, OUString const & value,
426 Reference<css::xml::dom::XNode> const & xParent)
427{
428 try
429 {
430 if (value.isEmpty())
431 return;
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),
437 UNO_QUERY_THROW);
438 xParent->appendChild(dataNode);
439
440 const Reference<css::xml::dom::XNode> dataValue(
441 doc->createTextNode(value), UNO_QUERY_THROW);
442 dataNode->appendChild(dataValue);
443 }
444 catch(const css::uno::Exception &)
445 {
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);
450 }
451
452}
453
455Reference<css::xml::dom::XNode> BackendDb::writeKeyElement(
456 OUString const & url)
457{
458 try
459 {
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();
465
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())
478 {
479 OSL_ASSERT(false);
480 //replace the existing entry.
481 removeEntry(url);
482 }
483
484 const Reference<css::xml::dom::XElement> keyElement(
485 doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName));
486
487 keyElement->setAttribute("url", url);
488
489 const Reference<css::xml::dom::XNode> keyNode(
490 keyElement, UNO_QUERY_THROW);
491 root->appendChild(keyNode);
492 return keyNode;
493 }
494 catch(const css::uno::Exception &)
495 {
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);
500 }
501}
502
503OUString BackendDb::readSimpleElement(
504 std::u16string_view sElementName, Reference<css::xml::dom::XNode> const & xParent)
505{
506 try
507 {
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);
513 if (val.is())
514 return val->getNodeValue();
515 return OUString();
516 }
517 catch(const css::uno::Exception &)
518 {
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);
523 }
524}
525
526
527std::deque< OUString> BackendDb::readList(
528 Reference<css::xml::dom::XNode> const & parent,
529 std::u16string_view sListTagName,
530 std::u16string_view sMemberTagName)
531{
532 try
533 {
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);
541
542 std::deque<OUString > retList;
543 sal_Int32 length = list->getLength();
544 for (sal_Int32 i = 0; i < length; i++)
545 {
546 const Reference<css::xml::dom::XNode> member = list->item(i);
547 retList.push_back(member->getNodeValue());
548 }
549 return retList;
550 }
551 catch(const css::uno::Exception &)
552 {
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);
557 }
558}
559
560std::vector<OUString> BackendDb::getOneChildFromAllEntries(
561 std::u16string_view name)
562{
563 try
564 {
565 std::vector<OUString> listRet;
566 Reference<css::xml::dom::XDocument> doc = getDocument();
567 Reference<css::xml::dom::XNode> root = doc->getFirstChild();
568
569 Reference<css::xml::xpath::XXPathAPI> xpathApi = getXPathAPI();
570 const OUString sPrefix = getNSPrefix();
571 const OUString sKeyElement = getKeyElementName();
572 OUString sNodeSelectExpr =
573 sPrefix +
574 ":" +
575 sKeyElement +
576 "/" +
577 sPrefix +
578 ":" +
579 name +
580 "/text()";
581
582 Reference<css::xml::dom::XNodeList> nodes =
583 xpathApi->selectNodeList(root, sNodeSelectExpr);
584 if (nodes.is())
585 {
586 sal_Int32 length = nodes->getLength();
587 for (sal_Int32 i = 0; i < length; i++)
588 listRet.push_back(nodes->item(i)->getNodeValue());
589 }
590 return listRet;
591 }
592 catch ( const css::deployment::DeploymentException& )
593 {
594 throw;
595 }
596 catch(const css::uno::Exception &)
597 {
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);
602 }
603}
604
605
606RegisteredDb::RegisteredDb(
607 Reference<XComponentContext> const & xContext,
608 OUString const & url):BackendDb(xContext, url)
609{
610}
611
612void RegisteredDb::addEntry(OUString const & url)
613{
614 try{
615 if (!activateEntry(url))
616 {
617 const OUString sNameSpace = getDbNSName();
618 const OUString sPrefix = getNSPrefix();
619 const OUString sEntry = getKeyElementName();
620
621 Reference<css::xml::dom::XDocument> doc = getDocument();
622 Reference<css::xml::dom::XNode> root = doc->getFirstChild();
623
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());
631#endif
632 Reference<css::xml::dom::XElement> helpElement(
633 doc->createElementNS(sNameSpace, sPrefix + ":" + sEntry));
634
635 helpElement->setAttribute("url", url);
636
637 Reference<css::xml::dom::XNode> helpNode(
638 helpElement, UNO_QUERY_THROW);
639 root->appendChild(helpNode);
640
641 save();
642 }
643 }
644 catch(const css::uno::Exception &)
645 {
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);
650 }
651}
652
653} // namespace dp_registry
654
655/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual OUString getNSPrefix()=0
css::uno::Reference< css::xml::dom::XDocument > const & getDocument()
virtual OUString getDbNSName()=0
BackendDb(BackendDb const &)=delete
css::uno::Reference< css::xml::xpath::XXPathAPI > const & getXPathAPI()
bool activateEntry(std::u16string_view url)
virtual OUString getKeyElementName()=0
void addEntry(OUString const &url)
css::uno::Reference< css::io::XInputStream > openStream()
void writeStream(const css::uno::Reference< css::io::XInputStream > &rStream, bool bReplaceExisting)
Any value
uno::Reference< uno::XComponentContext > m_xContext
OUString sPrefix
const char * name
static uno::Reference< css::uno::XComponentContext > xContext
Definition: init.cxx:2642
err
OUString expandUnoRcUrl(OUString const &url)
Definition: dp_misc.cxx:315
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
int i
constexpr OUStringLiteral first
std::vector< sal_uInt8 > bytes