/* -*- 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 "rtl/string.h" #include "rtl/strbuf.hxx" #include "rtl/bootstrap.hxx" #include "cppuhelper/exc_hlp.hxx" #include "osl/file.hxx" #include "com/sun/star/uno/XComponentContext.hpp" #include "com/sun/star/xml/dom/DocumentBuilder.hpp" #include "com/sun/star/xml/xpath/XXPathAPI.hpp" #include "com/sun/star/io/XActiveDataSource.hpp" #include "com/sun/star/io/XActiveDataControl.hpp" #include "dp_ucb.h" #include "dp_misc.h" #include "ucbhelper/content.hxx" #include "xmlscript/xml_helper.hxx" #include "dp_backenddb.hxx" using namespace ::com::sun::star::uno; using ::rtl::OUString; namespace dp_registry { namespace backend { BackendDb::BackendDb( Reference const & xContext, ::rtl::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); ::rtl::ByteSequence 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, 0, m_xContext); ucbDb.writeStream(xData, true /*replace existing*/); } css::uno::Reference 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() + OUSTR(":") + getRootElementName()); m_doc->appendChild(Reference( rootNode, UNO_QUERY_THROW)); save(); } else throw css::uno::RuntimeException( OUSTR("Extension manager could not access database file:" ) + m_urlDb, 0); if (!m_doc.is()) throw css::uno::RuntimeException( OUSTR("Extension manager could not get root node of data base file: ") + m_urlDb, 0); } return m_doc; } Reference BackendDb::getXPathAPI() { if (!m_xpathApi.is()) { m_xpathApi = Reference< css::xml::xpath::XXPathAPI >( m_xContext->getServiceManager()->createInstanceWithContext( OUSTR("com.sun.star.xml.xpath.XPathAPI"), m_xContext), css::uno::UNO_QUERY); if (!m_xpathApi.is()) throw css::uno::RuntimeException( OUSTR(" Could not create service com.sun.star.xml.xpath.XPathAPI"), 0); m_xpathApi->registerNS( getNSPrefix(), getDbNSName()); } return m_xpathApi; } void BackendDb::removeElement(::rtl::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( OUSTR("Extension Manager: failed to write data entry in backend db: ") + m_urlDb, 0, exc); } } void BackendDb::removeEntry(::rtl::OUString const & url) { const OUString sKeyElement = getKeyElementName(); const OUString sPrefix = getNSPrefix(); ::rtl::OUStringBuffer sExpression(500); sExpression.append(sPrefix); sExpression.appendAscii(":"); sExpression.append(sKeyElement); sExpression.append(OUSTR("[@url = \"")); sExpression.append(url); sExpression.appendAscii("\"]"); removeElement(sExpression.makeStringAndClear()); } void BackendDb::revokeEntry(::rtl::OUString const & url) { try { Reference entry = Reference(getKeyElement(url), UNO_QUERY); if (entry.is()) { entry->setAttribute(OUSTR("revoked"), OUSTR("true")); save(); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to revoke data entry in backend db: ") + m_urlDb, 0, exc); } } bool BackendDb::activateEntry(::rtl::OUString const & url) { try { bool ret = false; Reference entry = Reference(getKeyElement(url), UNO_QUERY); if (entry.is()) { //no attribute "active" means it is active, that is, registered. entry->removeAttribute(OUSTR("revoked")); save(); ret = true; } return ret; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to revoke data entry in backend db: ") + m_urlDb, 0, exc); } } bool BackendDb::hasActiveEntry(::rtl::OUString const & url) { try { bool ret = false; Reference entry = Reference(getKeyElement(url), UNO_QUERY); if (entry.is()) { OUString sActive = entry->getAttribute(OUSTR("revoked")); if (!sActive.equals(OUSTR("true"))) ret = true; } return ret; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to determine an active entry in backend db: ") + m_urlDb, 0, exc); } } Reference BackendDb::getKeyElement( ::rtl::OUString const & url) { try { const OUString sPrefix = getNSPrefix(); const OUString sKeyElement = getKeyElementName(); ::rtl::OUStringBuffer sExpression(500); sExpression.append(sPrefix); sExpression.appendAscii(":"); sExpression.append(sKeyElement); sExpression.append(OUSTR("[@url = \"")); sExpression.append(url); sExpression.appendAscii("\"]"); const Reference doc = getDocument(); const Reference root = doc->getFirstChild(); const Reference xpathApi = getXPathAPI(); return xpathApi->selectSingleNode(root, sExpression.makeStringAndClear()); } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to read key element in backend db: ") + m_urlDb, 0, exc); } } //Only writes the data if there is at least one entry void BackendDb::writeVectorOfPair( ::std::vector< ::std::pair< ::rtl::OUString, ::rtl::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() + OUSTR(":")); 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)); typedef ::std::vector< ::std::pair< OUString, OUString > >::const_iterator CIT; for (CIT i = vecPairs.begin(); i != vecPairs.end(); ++i) { 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( i->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( i->second)); secondNode->appendChild( Reference( secondTextNode, css::uno::UNO_QUERY_THROW)); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to write data entry in backend db: ") + m_urlDb, 0, 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() + OUSTR(":")); const Reference xpathApi = getXPathAPI(); const OUString sExprPairs( sPrefix + sListTagName + OUSTR("/") + 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 + OUSTR("/text()")); const Reference first = xpathApi->selectSingleNode(aPair, sExprFirst); const OUString sExprSecond(sPrefix + sSecondTagName + OUSTR("/text()")); const Reference second = xpathApi->selectSingleNode(aPair, sExprSecond); OSL_ASSERT(first.is() && second.is()); retVector.push_back(::std::make_pair( first->getNodeValue(), second->getNodeValue())); } return retVector; } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to read data entry in backend db: ") + m_urlDb, 0, exc); } } //Only writes the data if there is at least one entry void BackendDb::writeSimpleList( ::std::list< ::rtl::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() + OUSTR(":")); const Reference doc = getDocument(); const Reference listNode( doc->createElementNS(sNameSpace, sPrefix + sListTagName)); xParent->appendChild( Reference( listNode, css::uno::UNO_QUERY_THROW)); typedef ::std::list::const_iterator ITC_ITEMS; for (ITC_ITEMS i = list.begin(); i != list.end(); ++i) { const Reference memberNode( doc->createElementNS(sNameSpace, sPrefix + sMemberTagName), css::uno::UNO_QUERY_THROW); listNode->appendChild(memberNode); const Reference textNode( doc->createTextNode( *i), css::uno::UNO_QUERY_THROW); memberNode->appendChild(textNode); } } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to write data entry in backend db: ") + m_urlDb, 0, 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 + OUSTR(":") + 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( OUSTR("Extension Manager: failed to write data entry(writeSimpleElement) in backend db: ") + m_urlDb, 0, exc); } } /** The key elements have an url attribute and are always children of the root element. */ Reference BackendDb::writeKeyElement( ::rtl::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 //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 + OUSTR(":") + sElementName + OUSTR("[@url = \"") + url + OUSTR("\"]")); const Reference existingNode = getXPathAPI()->selectSingleNode(root, sExpression); if (existingNode.is()) { OSL_ASSERT(0); //replace the existing entry. removeEntry(url); } const Reference keyElement( doc->createElementNS(sNameSpace, sPrefix + OUSTR(":") + sElementName)); keyElement->setAttribute(OUSTR("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( OUSTR("Extension Manager: failed to write key element in backend db: ") + m_urlDb, 0, exc); } } OUString BackendDb::readSimpleElement( OUString const & sElementName, Reference const & xParent) { try { const OUString sPrefix = getNSPrefix(); const OUString sExpr(sPrefix + OUSTR(":") + sElementName + OUSTR("/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( OUSTR("Extension Manager: failed to read data (readSimpleElement) in backend db: ") + m_urlDb, 0, exc); } } ::std::list< OUString> BackendDb::readList( Reference const & parent, OUString const & sListTagName, OUString const & sMemberTagName) { try { OSL_ASSERT(parent.is()); const OUString sPrefix(getNSPrefix() + OUSTR(":")); const Reference xpathApi = getXPathAPI(); const OUString sExprList( sPrefix + sListTagName + OUSTR("/") + sPrefix + sMemberTagName + OUSTR("/text()")); const Reference list = xpathApi->selectNodeList(parent, sExprList); ::std::list 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( OUSTR("Extension Manager: failed to read data entry in backend db: ") + m_urlDb, 0, exc); } } ::std::list BackendDb::getOneChildFromAllEntries( OUString const & name) { try { ::std::list listRet; Reference doc = getDocument(); Reference root = doc->getFirstChild(); Reference xpathApi = getXPathAPI(); const OUString sPrefix = getNSPrefix(); const OUString sKeyElement = getKeyElementName(); ::rtl::OUStringBuffer buf(512); buf.append(sPrefix); buf.appendAscii(":"); buf.append(sKeyElement); buf.appendAscii("/"); buf.append(sPrefix); buf.appendAscii(":"); buf.append(name); buf.append(OUSTR("/text()")); Reference nodes = xpathApi->selectNodeList(root, buf.makeStringAndClear()); 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( OUSTR("Extension Manager: failed to read data entry in backend db: ") + m_urlDb, 0, exc); } } RegisteredDb::RegisteredDb( Reference const & xContext, ::rtl::OUString const & url):BackendDb(xContext, url) { } void RegisteredDb::addEntry(::rtl::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 + OUSTR(":") + sEntry + OUSTR("[@url = \"") + url + OUSTR("\"]")); Reference _extensionNode = getXPathAPI()->selectSingleNode(root, sExpression); OSL_ASSERT(! _extensionNode.is()); #endif Reference helpElement( doc->createElementNS(sNameSpace, sPrefix + OUSTR(":") + sEntry)); helpElement->setAttribute(OUSTR("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( OUSTR("Extension Manager: failed to write data entry in backend db: ") + m_urlDb, 0, exc); } } bool RegisteredDb::getEntry(::rtl::OUString const & url) { try { const OUString sPrefix = getNSPrefix(); const OUString sEntry = getKeyElementName(); const OUString sExpression( sPrefix + OUSTR(":") + sEntry + OUSTR("[@url = \"") + url + OUSTR("\"]")); Reference doc = getDocument(); Reference root = doc->getFirstChild(); Reference xpathApi = getXPathAPI(); Reference aNode = xpathApi->selectSingleNode(root, sExpression); return aNode.is(); } catch(const css::uno::Exception &) { Any exc( ::cppu::getCaughtException() ); throw css::deployment::DeploymentException( OUSTR("Extension Manager: failed to read data entry in backend db: ") + m_urlDb, 0, exc); } } } // namespace backend } // namespace dp_registry /* vim:set shiftwidth=4 softtabstop=4 expandtab: */