/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star::uno; namespace dp_registry::backend { BackendDb::BackendDb( Reference const & xContext, OUString const & url): m_xContext(xContext) { m_urlDb = dp_misc::expandUnoRcUrl(url); } void BackendDb::save() { const Reference xDataSource(m_doc,css::uno::UNO_QUERY_THROW); std::vector bytes; xDataSource->setOutputStream(::xmlscript::createOutputStream(&bytes)); const Reference xDataControl(m_doc,css::uno::UNO_QUERY_THROW); xDataControl->start(); const Reference xData( ::xmlscript::createInputStream(bytes)); ::ucbhelper::Content ucbDb(m_urlDb, nullptr, m_xContext); ucbDb.writeStream(xData, true /*replace existing*/); } css::uno::Reference const & BackendDb::getDocument() { if (!m_doc.is()) { const Reference xDocBuilder( css::xml::dom::DocumentBuilder::create(m_xContext) ); ::osl::DirectoryItem item; ::osl::File::RC err = ::osl::DirectoryItem::get(m_urlDb, item); if (err == ::osl::File::E_None) { ::ucbhelper::Content descContent( m_urlDb, css::uno::Reference(), m_xContext); Reference xIn = descContent.openStream(); m_doc = xDocBuilder->parse(xIn); } else if (err == ::osl::File::E_NOENT) { //Create a new document and insert some basic stuff m_doc = xDocBuilder->newDocument(); const Reference rootNode = m_doc->createElementNS(getDbNSName(), getNSPrefix() + ":" + getRootElementName()); m_doc->appendChild(Reference( rootNode, UNO_QUERY_THROW)); save(); } else throw css::uno::RuntimeException( "Extension manager could not access database file:" + m_urlDb, nullptr); if (!m_doc.is()) throw css::uno::RuntimeException( "Extension manager could not get root node of data base file: " + m_urlDb, nullptr); } return m_doc; } Reference const & BackendDb::getXPathAPI() { if (!m_xpathApi.is()) { m_xpathApi = css::xml::xpath::XPathAPI::create( m_xContext ); m_xpathApi->registerNS( getNSPrefix(), getDbNSName() ); } return m_xpathApi; } void BackendDb::removeElement(OUString const & sXPathExpression) { try { const Reference doc = getDocument(); const Reference root = doc->getFirstChild(); const Reference xpathApi = getXPathAPI(); //find the extension element that is to be removed const Reference aNode = xpathApi->selectSingleNode(root, sXPathExpression); if (aNode.is()) { root->removeChild(aNode); save(); } #if OSL_DEBUG_LEVEL > 0 //There must not be any other entry with the same url const Reference nextNode = xpathApi->selectSingleNode(root, sXPathExpression); OSL_ASSERT(! nextNode.is()); #endif } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write data entry in backend db: " + m_urlDb, nullptr, exc); } } void BackendDb::removeEntry(OUString const & url) { const OUString sKeyElement = getKeyElementName(); const OUString sPrefix = getNSPrefix(); OUString sExpression = sPrefix + ":" + sKeyElement + "[@url = \"" + url + "\"]"; removeElement(sExpression); } void BackendDb::revokeEntry(OUString const & url) { try { Reference entry(getKeyElement(url), UNO_QUERY); if (entry.is()) { entry->setAttribute("revoked", "true"); save(); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to revoke data entry in backend db: " + m_urlDb, nullptr, exc); } } bool BackendDb::activateEntry(OUString const & url) { try { bool ret = false; Reference entry(getKeyElement(url), UNO_QUERY); if (entry.is()) { //no attribute "active" means it is active, that is, registered. entry->removeAttribute("revoked"); save(); ret = true; } return ret; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to revoke data entry in backend db: " + m_urlDb, nullptr, exc); } } bool BackendDb::hasActiveEntry(OUString const & url) { try { bool ret = false; Reference entry(getKeyElement(url), UNO_QUERY); if (entry.is()) { OUString sActive = entry->getAttribute("revoked"); if (!(sActive == "true")) ret = true; } return ret; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to determine an active entry in backend db: " + m_urlDb, nullptr, exc); } } Reference BackendDb::getKeyElement( OUString const & url) { try { const OUString sPrefix = getNSPrefix(); const OUString sKeyElement = getKeyElementName(); OUString sExpression = sPrefix + ":" + sKeyElement + "[@url = \"" + url + "\"]"; const Reference doc = getDocument(); const Reference root = doc->getFirstChild(); const Reference xpathApi = getXPathAPI(); return xpathApi->selectSingleNode(root, sExpression); } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to read key element in backend db: " + m_urlDb, nullptr, exc); } } //Only writes the data if there is at least one entry void BackendDb::writeVectorOfPair( std::vector< std::pair< OUString, OUString > > const & vecPairs, OUString const & sVectorTagName, OUString const & sPairTagName, OUString const & sFirstTagName, OUString const & sSecondTagName, css::uno::Reference const & xParent) { try{ if (vecPairs.empty()) return; const OUString sNameSpace = getDbNSName(); OSL_ASSERT(!sNameSpace.isEmpty()); const OUString sPrefix(getNSPrefix() + ":"); const Reference doc = getDocument(); const Reference root = doc->getFirstChild(); const Reference vectorNode( doc->createElementNS(sNameSpace, sPrefix + sVectorTagName)); xParent->appendChild( Reference( vectorNode, css::uno::UNO_QUERY_THROW)); for (auto const& vecPair : vecPairs) { const Reference pairNode( doc->createElementNS(sNameSpace, sPrefix + sPairTagName)); vectorNode->appendChild( Reference( pairNode, css::uno::UNO_QUERY_THROW)); const Reference firstNode( doc->createElementNS(sNameSpace, sPrefix + sFirstTagName)); pairNode->appendChild( Reference( firstNode, css::uno::UNO_QUERY_THROW)); const Reference firstTextNode( doc->createTextNode( vecPair.first)); firstNode->appendChild( Reference( firstTextNode, css::uno::UNO_QUERY_THROW)); const Reference secondNode( doc->createElementNS(sNameSpace, sPrefix + sSecondTagName)); pairNode->appendChild( Reference( secondNode, css::uno::UNO_QUERY_THROW)); const Reference secondTextNode( doc->createTextNode( vecPair.second)); secondNode->appendChild( Reference( secondTextNode, css::uno::UNO_QUERY_THROW)); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write data entry in backend db: " + m_urlDb, nullptr, exc); } } std::vector< std::pair< OUString, OUString > > BackendDb::readVectorOfPair( Reference const & parent, OUString const & sListTagName, OUString const & sPairTagName, OUString const & sFirstTagName, OUString const & sSecondTagName) { try { OSL_ASSERT(parent.is()); const OUString sPrefix(getNSPrefix() + ":"); const Reference xpathApi = getXPathAPI(); const OUString sExprPairs( sPrefix + sListTagName + "/" + sPrefix + sPairTagName); const Reference listPairs = xpathApi->selectNodeList(parent, sExprPairs); std::vector< std::pair< OUString, OUString > > retVector; sal_Int32 length = listPairs->getLength(); for (sal_Int32 i = 0; i < length; i++) { const Reference aPair = listPairs->item(i); const OUString sExprFirst(sPrefix + sFirstTagName + "/text()"); const Reference first = xpathApi->selectSingleNode(aPair, sExprFirst); const OUString sExprSecond(sPrefix + sSecondTagName + "/text()"); const Reference second = xpathApi->selectSingleNode(aPair, sExprSecond); OSL_ASSERT(first.is() && second.is()); retVector.emplace_back( first->getNodeValue(), second->getNodeValue()); } return retVector; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to read data entry in backend db: " + m_urlDb, nullptr, exc); } } //Only writes the data if there is at least one entry void BackendDb::writeSimpleList( std::deque< OUString> const & list, OUString const & sListTagName, OUString const & sMemberTagName, Reference const & xParent) { try { if (list.empty()) return; const OUString sNameSpace = getDbNSName(); const OUString sPrefix(getNSPrefix() + ":"); const Reference doc = getDocument(); const Reference listNode( doc->createElementNS(sNameSpace, sPrefix + sListTagName)); xParent->appendChild( Reference( listNode, css::uno::UNO_QUERY_THROW)); for (auto const& elem : list) { const Reference memberNode( doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW); listNode->appendChild(memberNode); const Reference textNode( doc->createTextNode(elem), css::uno::UNO_QUERY_THROW); memberNode->appendChild(textNode); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write data entry in backend db: " + m_urlDb, nullptr, exc); } } //Writes only the element if is has a value. //The prefix is automatically added to the element name void BackendDb::writeSimpleElement( OUString const & sElementName, OUString const & value, Reference const & xParent) { try { if (value.isEmpty()) return; const OUString sPrefix = getNSPrefix(); const Reference doc = getDocument(); const OUString sNameSpace = getDbNSName(); const Reference dataNode( doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName), UNO_QUERY_THROW); xParent->appendChild(dataNode); const Reference dataValue( doc->createTextNode(value), UNO_QUERY_THROW); dataNode->appendChild(dataValue); } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write data entry(writeSimpleElement) in backend db: " + m_urlDb, nullptr, exc); } } /// The key elements have a url attribute and are always children of the root element. Reference BackendDb::writeKeyElement( OUString const & url) { try { const OUString sNameSpace = getDbNSName(); const OUString sPrefix = getNSPrefix(); const OUString sElementName = getKeyElementName(); const Reference doc = getDocument(); const Reference root = doc->getFirstChild(); //Check if there are an entry with the same url. This can be the case if the //status of an XPackage is ambiguous. In this case a call to activateExtension //(dp_extensionmanager.cxx), will register the package again. See also //Package::processPackage_impl in dp_backend.cxx. //A package can become //invalid after its successful registration, for example if a second extension with //the same service is installed. const OUString sExpression( sPrefix + ":" + sElementName + "[@url = \"" + url + "\"]"); const Reference existingNode = getXPathAPI()->selectSingleNode(root, sExpression); if (existingNode.is()) { OSL_ASSERT(false); //replace the existing entry. removeEntry(url); } const Reference keyElement( doc->createElementNS(sNameSpace, sPrefix + ":" + sElementName)); keyElement->setAttribute("url", url); const Reference keyNode( keyElement, UNO_QUERY_THROW); root->appendChild(keyNode); return keyNode; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write key element in backend db: " + m_urlDb, nullptr, exc); } } OUString BackendDb::readSimpleElement( OUString const & sElementName, Reference const & xParent) { try { const OUString sPrefix = getNSPrefix(); const OUString sExpr(sPrefix + ":" + sElementName + "/text()"); const Reference xpathApi = getXPathAPI(); const Reference val = xpathApi->selectSingleNode(xParent, sExpr); if (val.is()) return val->getNodeValue(); return OUString(); } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to read data (readSimpleElement) in backend db: " + m_urlDb, nullptr, exc); } } std::deque< OUString> BackendDb::readList( Reference const & parent, OUString const & sListTagName, OUString const & sMemberTagName) { try { OSL_ASSERT(parent.is()); const OUString sPrefix(getNSPrefix() + ":"); const Reference xpathApi = getXPathAPI(); const OUString sExprList( sPrefix + sListTagName + "/" + sPrefix + sMemberTagName + "/text()"); const Reference list = xpathApi->selectNodeList(parent, sExprList); std::deque retList; sal_Int32 length = list->getLength(); for (sal_Int32 i = 0; i < length; i++) { const Reference member = list->item(i); retList.push_back(member->getNodeValue()); } return retList; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to read data entry in backend db: " + m_urlDb, nullptr, exc); } } std::vector BackendDb::getOneChildFromAllEntries( OUString const & name) { try { std::vector listRet; Reference doc = getDocument(); Reference root = doc->getFirstChild(); Reference xpathApi = getXPathAPI(); const OUString sPrefix = getNSPrefix(); const OUString sKeyElement = getKeyElementName(); OUString sNodeSelectExpr = sPrefix + ":" + sKeyElement + "/" + sPrefix + ":" + name + "/text()"; Reference nodes = xpathApi->selectNodeList(root, sNodeSelectExpr); if (nodes.is()) { sal_Int32 length = nodes->getLength(); for (sal_Int32 i = 0; i < length; i++) listRet.push_back(nodes->item(i)->getNodeValue()); } return listRet; } catch ( const css::deployment::DeploymentException& ) { throw; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to read data entry in backend db: " + m_urlDb, nullptr, exc); } } RegisteredDb::RegisteredDb( Reference const & xContext, OUString const & url):BackendDb(xContext, url) { } void RegisteredDb::addEntry(OUString const & url) { try{ if (!activateEntry(url)) { const OUString sNameSpace = getDbNSName(); const OUString sPrefix = getNSPrefix(); const OUString sEntry = getKeyElementName(); Reference doc = getDocument(); Reference root = doc->getFirstChild(); #if OSL_DEBUG_LEVEL > 0 //There must not be yet an entry with the same url OUString sExpression( sPrefix + ":" + sEntry + "[@url = \"" + url + "\"]"); Reference _extensionNode = getXPathAPI()->selectSingleNode(root, sExpression); OSL_ASSERT(! _extensionNode.is()); #endif Reference helpElement( doc->createElementNS(sNameSpace, sPrefix + ":" + sEntry)); helpElement->setAttribute("url", url); Reference helpNode( helpElement, UNO_QUERY_THROW); root->appendChild(helpNode); save(); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( "Extension Manager: failed to write data entry in backend db: " + m_urlDb, nullptr, exc); } } } // namespace dp_registry /* vim:set shiftwidth=4 softtabstop=4 expandtab: */