/* -*- 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 <comphelper/diagnose_ex.hxx>
#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(OUString::Concat(std::u16string_view(pCopy, p - 1 - pCopy)) + "&");
p += 4;
pCopy = p;
}
else if (pEnd - p >= 3
&& p[0] == 'l' && p[1] == 't' && p[2] == ';')
{
aBuffer.append(OUString::Concat(std::u16string_view(pCopy, p - 1 - pCopy)) + "<");
p += 3;
pCopy = p;
}
else if (pEnd - p >= 3
&& p[0] == 'g' && p[1] == 't' && p[2] == ';')
{
aBuffer.append(OUString::Concat(std::u16string_view(pCopy, p - 1 - pCopy)) + ">");
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(std::u16string_view(pCopy, p - 1 - pCopy) + aValue);
p = q + 1;
pCopy = p;
break;
}
aBuffer.append(pCopy, pEnd - pCopy);
*pOutput = aBuffer.makeStringAndClear();
return true;
}
void makeAndAppendXMLName(
OUStringBuffer & rBuffer, std::u16string_view rIn )
{
size_t nCount = rIn.size();
for ( size_t 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 u"com.sun.star.comp.ucb.UniversalContentBroker"_ustr;
}
sal_Bool SAL_CALL UniversalContentBroker::supportsService( const OUString& ServiceName )
{
return cppu::supportsService( this, ServiceName );
}
css::uno::Sequence< OUString > SAL_CALL UniversalContentBroker::getSupportedServiceNames()
{
return { u"com.sun.star.ucb.UniversalContentBroker"_ustr };
}
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(
u"UCB reinitialized with different arguments"_ustr,
getXWeak(), 0);
}
return;
}
if (!aArguments.hasElements())
{
m_aArguments = { Any(u"Local"_ustr), Any(u"Office"_ustr) };
}
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(
u"Wrong argument type!"_ustr,
getXWeak(),
-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(
u"Wrong argument type!"_ustr,
getXWeak(),
-1 ) ),
Environment );
// Unreachable
}
aRet = checkIn( aCheckinArg, Environment );
}
else
{
// Unknown command
ucbhelper::cancelCommandExecution(
Any( UnsupportedCommandException(
OUString(),
getXWeak() ) ),
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(
std::u16string_view rKey1,
std::u16string_view rKey2,
ContentProviderDataList & rListToFill )
{
if ( !m_xContext.is() || rKey1.empty() || rKey2.empty() )
{
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(
u"com.sun.star.configuration.ConfigurationAccess"_ustr,
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( "['" );
makeAndAppendXMLName( aElemBuffer, rElem );
aElemBuffer.append( "']" );
OSL_VERIFY(
createContentProviderData(
aElemBuffer, 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: */
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V530 The return value of function 'append' is required to be utilized.
↑ V1019 Compound assignment expression 'm_aArguments[0] >>= aKey1' is used inside condition.
↑ V1019 Compound assignment expression 'm_aArguments[1] >>= aKey2' is used inside condition.