diff options
Diffstat (limited to '')
-rw-r--r-- | ucb/source/core/ucb.cxx | 887 |
1 files changed, 887 insertions, 0 deletions
diff --git a/ucb/source/core/ucb.cxx b/ucb/source/core/ucb.cxx new file mode 100644 index 000000000..4e41e8cff --- /dev/null +++ b/ucb/source/core/ucb.cxx @@ -0,0 +1,887 @@ +/* -*- 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 . + */ + + +/************************************************************************** + TODO + ************************************************************************** + + *************************************************************************/ + +#include <sal/config.h> + +#include <string_view> + +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <rtl/ustrbuf.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/ucb/DuplicateProviderException.hpp> +#include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp> +#include <com/sun/star/ucb/UnsupportedCommandException.hpp> +#include <com/sun/star/ucb/XCommandInfo.hpp> +#include <com/sun/star/ucb/XContentProviderSupplier.hpp> +#include <com/sun/star/configuration/theDefaultProvider.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <ucbhelper/cancelcommandexecution.hxx> +#include <tools/diagnose_ex.h> +#include "identify.hxx" +#include "ucbcmds.hxx" + +#include "ucb.hxx" + +using namespace comphelper; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::ucb; +using namespace ucb_impl; +using namespace com::sun::star; +using namespace ucbhelper; + +namespace { + +bool fillPlaceholders(OUString const & rInput, + uno::Sequence< uno::Any > const & rReplacements, + OUString * pOutput) +{ + sal_Unicode const * p = rInput.getStr(); + sal_Unicode const * pEnd = p + rInput.getLength(); + sal_Unicode const * pCopy = p; + OUStringBuffer aBuffer; + while (p != pEnd) + switch (*p++) + { + case '&': + if (pEnd - p >= 4 + && p[0] == 'a' && p[1] == 'm' && p[2] == 'p' + && p[3] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('&'); + p += 4; + pCopy = p; + } + else if (pEnd - p >= 3 + && p[0] == 'l' && p[1] == 't' && p[2] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('<'); + p += 3; + pCopy = p; + } + else if (pEnd - p >= 3 + && p[0] == 'g' && p[1] == 't' && p[2] == ';') + { + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append('>'); + p += 3; + pCopy = p; + } + break; + + case '<': + sal_Unicode const * q = p; + while (q != pEnd && *q != '>') + ++q; + if (q == pEnd) + break; + OUString aKey(p, q - p); + OUString aValue; + bool bFound = false; + for (sal_Int32 i = 2; i + 1 < rReplacements.getLength(); + i += 2) + { + OUString aReplaceKey; + if ((rReplacements[i] >>= aReplaceKey) + && aReplaceKey == aKey + && (rReplacements[i + 1] >>= aValue)) + { + bFound = true; + break; + } + } + if (!bFound) + return false; + aBuffer.append(pCopy, p - 1 - pCopy); + aBuffer.append(aValue); + p = q + 1; + pCopy = p; + break; + } + aBuffer.append(pCopy, pEnd - pCopy); + *pOutput = aBuffer.makeStringAndClear(); + return true; +} + +void makeAndAppendXMLName( + OUStringBuffer & rBuffer, const OUString & rIn ) +{ + sal_Int32 nCount = rIn.getLength(); + for ( sal_Int32 n = 0; n < nCount; ++n ) + { + const sal_Unicode c = rIn[ n ]; + switch ( c ) + { + case '&': + rBuffer.append( "&" ); + break; + + case '"': + rBuffer.append( """ ); + break; + + case '\'': + rBuffer.append( "'" ); + break; + + case '<': + rBuffer.append( "<" ); + break; + + case '>': + rBuffer.append( ">" ); + break; + + default: + rBuffer.append( c ); + break; + } + } +} + +bool createContentProviderData( + std::u16string_view rProvider, + const uno::Reference< container::XHierarchicalNameAccess >& rxHierNameAccess, + ContentProviderData & rInfo) +{ + // Obtain service name. + + OUString aValue; + try + { + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/ServiceName" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + } + catch (const container::NoSuchElementException&) + { + return false; + } + + rInfo.ServiceName = aValue; + + // Obtain URL Template. + + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/URLTemplate" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + + rInfo.URLTemplate = aValue; + + // Obtain Arguments. + + if ( !( rxHierNameAccess->getByHierarchicalName( + OUString::Concat(rProvider) + "/Arguments" ) >>= aValue ) ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "Error getting item value!" ); + } + + rInfo.Arguments = aValue; + return true; +} + +} + + +// UniversalContentBroker Implementation. + + +UniversalContentBroker::UniversalContentBroker( + const Reference< css::uno::XComponentContext >& xContext ) +: m_xContext( xContext ), + m_nCommandId( 0 ) +{ + OSL_ENSURE( m_xContext.is(), + "UniversalContentBroker ctor: No service manager" ); +} + + +// virtual +UniversalContentBroker::~UniversalContentBroker() +{ +} + + +// XComponent methods. + + +// virtual +void SAL_CALL UniversalContentBroker::dispose() +{ + if ( m_pDisposeEventListeners && m_pDisposeEventListeners->getLength() ) + { + EventObject aEvt; + aEvt.Source = static_cast< XComponent* >(this); + m_pDisposeEventListeners->disposeAndClear( aEvt ); + } + + if ( m_xNotifier.is() ) + m_xNotifier->removeChangesListener( this ); +} + + +// virtual +void SAL_CALL UniversalContentBroker::addEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( !m_pDisposeEventListeners ) + m_pDisposeEventListeners.reset( new OInterfaceContainerHelper3<css::lang::XEventListener>( m_aMutex ) ); + + m_pDisposeEventListeners->addInterface( Listener ); +} + + +// virtual +void SAL_CALL UniversalContentBroker::removeEventListener( + const Reference< XEventListener >& Listener ) +{ + if ( m_pDisposeEventListeners ) + m_pDisposeEventListeners->removeInterface( Listener ); + + // Note: Don't want to delete empty container here -> performance. +} + + +// XServiceInfo methods. + +OUString SAL_CALL UniversalContentBroker::getImplementationName() +{ + return "com.sun.star.comp.ucb.UniversalContentBroker"; +} +sal_Bool SAL_CALL UniversalContentBroker::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} +css::uno::Sequence< OUString > SAL_CALL UniversalContentBroker::getSupportedServiceNames() +{ + return { "com.sun.star.ucb.UniversalContentBroker" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ucb_UniversalContentBroker_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new UniversalContentBroker(context)); +} + + +// XInitialization methods. + + +// virtual +void SAL_CALL UniversalContentBroker::initialize( const css::uno::Sequence< Any >& aArguments ) +{ + { + osl::MutexGuard aGuard(m_aMutex); + if (m_aArguments.hasElements()) + { + if (aArguments.hasElements() + && !(m_aArguments.getLength() == 2 + && aArguments.getLength() == 2 + && m_aArguments[0] == aArguments[0] + && m_aArguments[1] == aArguments[1])) + { + throw IllegalArgumentException( + "UCB reinitialized with different arguments", + static_cast< cppu::OWeakObject * >(this), 0); + } + return; + } + if (!aArguments.hasElements()) + { + m_aArguments = { Any(OUString("Local")), Any(OUString("Office")) }; + } + else + { + m_aArguments = aArguments; + } + } + configureUcb(); +} + + +// XContentProviderManager methods. + + +// virtual +Reference< XContentProvider > SAL_CALL +UniversalContentBroker::registerContentProvider( + const Reference< XContentProvider >& Provider, + const OUString& Scheme, + sal_Bool ReplaceExisting ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ProviderMap_Impl::iterator aIt; + try + { + aIt = m_aProviders.find(Scheme); + } + catch (const IllegalArgumentException&) + { + return nullptr; //@@@ + } + + Reference< XContentProvider > xPrevious; + if (aIt == m_aProviders.end()) + { + ProviderList_Impl aList; + aList.push_front( ProviderListEntry_Impl(Provider) ); + try + { + m_aProviders.add(Scheme, aList); + } + catch (const IllegalArgumentException&) + { + return nullptr; //@@@ + } + } + else + { + if (!ReplaceExisting) + throw DuplicateProviderException(); + + ProviderList_Impl & rList = aIt->getValue(); + xPrevious = rList.front().getProvider(); + rList.push_front( ProviderListEntry_Impl(Provider) ); + } + + return xPrevious; +} + + +// virtual +void SAL_CALL UniversalContentBroker::deregisterContentProvider( + const Reference< XContentProvider >& Provider, + const OUString& Scheme ) +{ + osl::MutexGuard aGuard(m_aMutex); + + ProviderMap_Impl::iterator aMapIt; + try + { + aMapIt = m_aProviders.find(Scheme); + } + catch (const IllegalArgumentException&) + { + return; //@@@ + } + + if (aMapIt != m_aProviders.end()) + { + ProviderList_Impl & rList = aMapIt->getValue(); + + auto aListIt = std::find_if(rList.begin(), rList.end(), + [&Provider](const ProviderListEntry_Impl& rEntry) { return rEntry.getProvider() == Provider; }); + if (aListIt != rList.end()) + rList.erase(aListIt); + + if (rList.empty()) + m_aProviders.erase(aMapIt); + } +} + + +// virtual +css::uno::Sequence< ContentProviderInfo > SAL_CALL + UniversalContentBroker::queryContentProviders() +{ + // Return a list with information about active(!) content providers. + + osl::MutexGuard aGuard(m_aMutex); + + css::uno::Sequence< ContentProviderInfo > aSeq( m_aProviders.size() ); + ContentProviderInfo* pInfo = aSeq.getArray(); + + ProviderMap_Impl::const_iterator end = m_aProviders.end(); + for (ProviderMap_Impl::const_iterator it(m_aProviders.begin()); it != end; + ++it) + { + // Note: Active provider is always the first list element. + pInfo->ContentProvider = it->getValue().front().getProvider(); + pInfo->Scheme = it->getRegexp(); + ++pInfo; + } + + return aSeq; +} + + +// virtual +Reference< XContentProvider > SAL_CALL + UniversalContentBroker::queryContentProvider( const OUString& + Identifier ) +{ + return queryContentProvider( Identifier, false ); +} + + +// XContentProvider methods. + + +// virtual +Reference< XContent > SAL_CALL UniversalContentBroker::queryContent( + const Reference< XContentIdentifier >& Identifier ) +{ + + // Let the content provider for the scheme given with the content + // identifier create the XContent instance. + + + if ( !Identifier.is() ) + return Reference< XContent >(); + + Reference< XContentProvider > xProv = + queryContentProvider( Identifier->getContentIdentifier(), true ); + if ( xProv.is() ) + return xProv->queryContent( Identifier ); + + return Reference< XContent >(); +} + + +// virtual +sal_Int32 SAL_CALL UniversalContentBroker::compareContentIds( + const Reference< XContentIdentifier >& Id1, + const Reference< XContentIdentifier >& Id2 ) +{ + OUString aURI1( Id1->getContentIdentifier() ); + OUString aURI2( Id2->getContentIdentifier() ); + + Reference< XContentProvider > xProv1 + = queryContentProvider( aURI1, true ); + Reference< XContentProvider > xProv2 + = queryContentProvider( aURI2, true ); + + // When both identifiers belong to the same provider, let that provider + // compare them; otherwise, simply compare the URI strings (which must + // be different): + if ( xProv1.is() && ( xProv1 == xProv2 ) ) + return xProv1->compareContentIds( Id1, Id2 ); + else + return aURI1.compareTo( aURI2 ); +} + + +// XContentIdentifierFactory methods. + + +// virtual +Reference< XContentIdentifier > SAL_CALL + UniversalContentBroker::createContentIdentifier( + const OUString& ContentId ) +{ + + // Let the content provider for the scheme given with content + // identifier create the XContentIdentifier instance, if he supports + // the XContentIdentifierFactory interface. Otherwise create standard + // implementation object for XContentIdentifier. + + + Reference< XContentIdentifier > xIdentifier; + + Reference< XContentProvider > xProv + = queryContentProvider( ContentId, true ); + if ( xProv.is() ) + { + Reference< XContentIdentifierFactory > xFac( xProv, UNO_QUERY ); + if ( xFac.is() ) + xIdentifier = xFac->createContentIdentifier( ContentId ); + } + + if ( !xIdentifier.is() ) + xIdentifier = new ContentIdentifier( ContentId ); + + return xIdentifier; +} + + +// XCommandProcessor methods. + + +// virtual +sal_Int32 SAL_CALL UniversalContentBroker::createCommandIdentifier() +{ + osl::MutexGuard aGuard( m_aMutex ); + + // Just increase counter on every call to generate an identifier. + return ++m_nCommandId; +} + + +// virtual +Any SAL_CALL UniversalContentBroker::execute( + const Command& aCommand, + sal_Int32, + const Reference< XCommandEnvironment >& Environment ) +{ + Any aRet; + + + // Note: Don't forget to adapt ucb_commands::CommandProcessorInfo + // ctor in ucbcmds.cxx when adding new commands! + + + if ( ( aCommand.Handle == GETCOMMANDINFO_HANDLE ) || aCommand.Name == GETCOMMANDINFO_NAME ) + { + + // getCommandInfo + + + aRet <<= getCommandInfo(); + } + else if ( ( aCommand.Handle == GLOBALTRANSFER_HANDLE ) || aCommand.Name == GLOBALTRANSFER_NAME ) + { + + // globalTransfer + + + GlobalTransferCommandArgument2 aTransferArg; + if ( !( aCommand.Argument >>= aTransferArg ) ) + { + GlobalTransferCommandArgument aArg; + if ( !( aCommand.Argument >>= aArg ) ) + { + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + "Wrong argument type!", + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + + // Copy infos into the new structure + aTransferArg.Operation = aArg.Operation; + aTransferArg.SourceURL = aArg.SourceURL; + aTransferArg.TargetURL = aArg.TargetURL; + aTransferArg.NewTitle = aArg.NewTitle; + aTransferArg.NameClash = aArg.NameClash; + } + + globalTransfer( aTransferArg, Environment ); + } + else if ( ( aCommand.Handle == CHECKIN_HANDLE ) || aCommand.Name == CHECKIN_NAME ) + { + ucb::CheckinArgument aCheckinArg; + if ( !( aCommand.Argument >>= aCheckinArg ) ) + { + ucbhelper::cancelCommandExecution( + Any( IllegalArgumentException( + "Wrong argument type!", + static_cast< cppu::OWeakObject * >( this ), + -1 ) ), + Environment ); + // Unreachable + } + aRet = checkIn( aCheckinArg, Environment ); + } + else + { + + // Unknown command + + + ucbhelper::cancelCommandExecution( + Any( UnsupportedCommandException( + OUString(), + static_cast< cppu::OWeakObject * >( this ) ) ), + Environment ); + // Unreachable + } + + return aRet; +} + + +// XCommandProcessor2 methods. + + +// virtual +void SAL_CALL UniversalContentBroker::releaseCommandIdentifier(sal_Int32 /*aCommandId*/) +{ + // @@@ Not implemented ( yet). +} + + +// virtual +void SAL_CALL UniversalContentBroker::abort( sal_Int32 ) +{ + // @@@ Not implemented ( yet). +} + + +// XChangesListener methods + + +// virtual +void SAL_CALL UniversalContentBroker::changesOccurred( const util::ChangesEvent& Event ) +{ + if ( !Event.Changes.hasElements() ) + return; + + uno::Reference< container::XHierarchicalNameAccess > xHierNameAccess; + Event.Base >>= xHierNameAccess; + + OSL_ASSERT( xHierNameAccess.is() ); + + ContentProviderDataList aData; + for ( const util::ElementChange& rElem : Event.Changes ) + { + OUString aKey; + rElem.Accessor >>= aKey; + + ContentProviderData aInfo; + + // Removal of UCPs from the configuration leads to changesOccurred + // notifications, too, but it is hard to tell for a given + // ElementChange whether it is an addition or a removal, so as a + // heuristic consider as removals those that cause a + // NoSuchElementException in createContentProviderData. + + // For now, removal of UCPs from the configuration is simply ignored + // (and not reflected in the UCB's data structures): + if (createContentProviderData(aKey, xHierNameAccess, aInfo)) + { + aData.push_back(aInfo); + } + } + + prepareAndRegister(aData); +} + + +// XEventListener methods + + +// virtual +void SAL_CALL UniversalContentBroker::disposing(const lang::EventObject&) +{ + if ( m_xNotifier.is() ) + { + osl::Guard< osl::Mutex > aGuard( m_aMutex ); + + if ( m_xNotifier.is() ) + m_xNotifier.clear(); + } +} + + +// Non-interface methods + + +Reference< XContentProvider > UniversalContentBroker::queryContentProvider( + const OUString& Identifier, + bool bResolved ) +{ + osl::MutexGuard aGuard( m_aMutex ); + + ProviderList_Impl const * pList = m_aProviders.map( Identifier ); + return pList ? bResolved ? pList->front().getResolvedProvider() + : pList->front().getProvider() + : Reference< XContentProvider >(); +} + +void UniversalContentBroker::configureUcb() +{ + OUString aKey1; + OUString aKey2; + if (m_aArguments.getLength() < 2 + || !(m_aArguments[0] >>= aKey1) || !(m_aArguments[1] >>= aKey2)) + { + OSL_FAIL("UniversalContentBroker::configureUcb(): Bad arguments"); + return; + } + + ContentProviderDataList aData; + if (!getContentProviderData(aKey1, aKey2, aData)) + { + SAL_WARN( "ucb", "No configuration"); + return; + } + + prepareAndRegister(aData); +} + +void UniversalContentBroker::prepareAndRegister( + const ContentProviderDataList& rData) +{ + for (const auto& rContentProviderData : rData) + { + OUString aProviderArguments; + if (fillPlaceholders(rContentProviderData.Arguments, + m_aArguments, + &aProviderArguments)) + { + registerAtUcb(this, + m_xContext, + rContentProviderData.ServiceName, + aProviderArguments, + rContentProviderData.URLTemplate); + + } + else + OSL_FAIL("UniversalContentBroker::prepareAndRegister(): Bad argument placeholders"); + } +} + + +bool UniversalContentBroker::getContentProviderData( + const OUString & rKey1, + const OUString & rKey2, + ContentProviderDataList & rListToFill ) +{ + if ( !m_xContext.is() || rKey1.isEmpty() || rKey2.isEmpty() ) + { + OSL_FAIL( "UniversalContentBroker::getContentProviderData - Invalid argument!" ); + return false; + } + + try + { + uno::Reference< lang::XMultiServiceFactory > xConfigProv = + configuration::theDefaultProvider::get( m_xContext ); + + OUStringBuffer aFullPath(128); + aFullPath.append( + "/org.openoffice.ucb.Configuration/ContentProviders" + "/['" ); + makeAndAppendXMLName( aFullPath, rKey1 ); + aFullPath.append( "']/SecondaryKeys/['" ); + makeAndAppendXMLName( aFullPath, rKey2 ); + aFullPath.append( "']/ProviderData" ); + + uno::Sequence<uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(aFullPath.makeStringAndClear())} + })); + + uno::Reference< uno::XInterface > xInterface( + xConfigProv->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments ) ); + + if ( !m_xNotifier.is() ) + { + m_xNotifier.set( xInterface, uno::UNO_QUERY_THROW ); + + m_xNotifier->addChangesListener( this ); + } + + uno::Reference< container::XNameAccess > xNameAccess( + xInterface, uno::UNO_QUERY_THROW ); + + const uno::Sequence< OUString > aElems = xNameAccess->getElementNames(); + + if ( aElems.hasElements() ) + { + uno::Reference< container::XHierarchicalNameAccess > + xHierNameAccess( xInterface, uno::UNO_QUERY_THROW ); + + // Iterate over children. + for ( const auto& rElem : aElems ) + { + + try + { + + ContentProviderData aInfo; + + OUStringBuffer aElemBuffer; + aElemBuffer.append( "['" ); + makeAndAppendXMLName( aElemBuffer, rElem ); + aElemBuffer.append( "']" ); + + OSL_VERIFY( + createContentProviderData( + aElemBuffer.makeStringAndClear(), xHierNameAccess, + aInfo)); + + rListToFill.push_back( aInfo ); + } + catch (const container::NoSuchElementException&) + { + // getByHierarchicalName + OSL_FAIL( "UniversalContentBroker::getContentProviderData - " + "caught NoSuchElementException!" ); + } + } + } + } + catch (const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION( "ucb", "" ); + return false; + } + catch (const uno::Exception&) + { + // createInstance, createInstanceWithArguments + + TOOLS_WARN_EXCEPTION( "ucb", "" ); + return false; + } + + return true; +} + + +// ProviderListEntry_Impl implementation. + + +Reference< XContentProvider > const & ProviderListEntry_Impl::resolveProvider() const +{ + if ( !m_xResolvedProvider.is() ) + { + Reference< XContentProviderSupplier > xSupplier( + m_xProvider, UNO_QUERY ); + if ( xSupplier.is() ) + m_xResolvedProvider = xSupplier->getContentProvider(); + + if ( !m_xResolvedProvider.is() ) + m_xResolvedProvider = m_xProvider; + } + + return m_xResolvedProvider; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |