1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 /**************************************************************************
23 **************************************************************************
25 *************************************************************************/
27 #include <sal/config.h>
29 #include <string_view>
31 #include <osl/diagnose.h>
32 #include <sal/log.hxx>
33 #include <rtl/ustrbuf.hxx>
34 #include <comphelper/processfactory.hxx>
35 #include <comphelper/propertysequence.hxx>
36 #include <com/sun/star/lang/IllegalArgumentException.hpp>
37 #include <com/sun/star/ucb/DuplicateProviderException.hpp>
38 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
39 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
40 #include <com/sun/star/ucb/XCommandInfo.hpp>
41 #include <com/sun/star/ucb/XContentProviderSupplier.hpp>
42 #include <com/sun/star/configuration/theDefaultProvider.hpp>
43 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
44 #include <com/sun/star/container/XNameAccess.hpp>
45 #include <com/sun/star/uno/Any.hxx>
46 #include <cppuhelper/supportsservice.hxx>
47 #include <cppuhelper/weak.hxx>
48 #include <ucbhelper/cancelcommandexecution.hxx>
49 #include <comphelper/diagnose_ex.hxx>
50 #include "identify.hxx"
51 #include "ucbcmds.hxx"
55 using namespace comphelper
;
56 using namespace com::sun::star::uno
;
57 using namespace com::sun::star::lang
;
58 using namespace com::sun::star::ucb
;
59 using namespace ucb_impl
;
60 using namespace com::sun::star
;
61 using namespace ucbhelper
;
65 bool fillPlaceholders(OUString
const & rInput
,
66 uno::Sequence
< uno::Any
> const & rReplacements
,
69 sal_Unicode
const * p
= rInput
.getStr();
70 sal_Unicode
const * pEnd
= p
+ rInput
.getLength();
71 sal_Unicode
const * pCopy
= p
;
72 OUStringBuffer aBuffer
;
78 && p
[0] == 'a' && p
[1] == 'm' && p
[2] == 'p'
81 aBuffer
.append(OUString::Concat(std::u16string_view(pCopy
, p
- 1 - pCopy
)) + "&");
85 else if (pEnd
- p
>= 3
86 && p
[0] == 'l' && p
[1] == 't' && p
[2] == ';')
88 aBuffer
.append(OUString::Concat(std::u16string_view(pCopy
, p
- 1 - pCopy
)) + "<");
92 else if (pEnd
- p
>= 3
93 && p
[0] == 'g' && p
[1] == 't' && p
[2] == ';')
95 aBuffer
.append(OUString::Concat(std::u16string_view(pCopy
, p
- 1 - pCopy
)) + ">");
102 sal_Unicode
const * q
= p
;
103 while (q
!= pEnd
&& *q
!= '>')
107 OUString
aKey(p
, q
- p
);
110 for (sal_Int32 i
= 2; i
+ 1 < rReplacements
.getLength();
113 OUString aReplaceKey
;
114 if ((rReplacements
[i
] >>= aReplaceKey
)
115 && aReplaceKey
== aKey
116 && (rReplacements
[i
+ 1] >>= aValue
))
124 aBuffer
.append(std::u16string_view(pCopy
, p
- 1 - pCopy
) + aValue
);
129 aBuffer
.append(pCopy
, pEnd
- pCopy
);
130 *pOutput
= aBuffer
.makeStringAndClear();
134 void makeAndAppendXMLName(
135 OUStringBuffer
& rBuffer
, std::u16string_view rIn
)
137 size_t nCount
= rIn
.size();
138 for ( size_t n
= 0; n
< nCount
; ++n
)
140 const sal_Unicode c
= rIn
[ n
];
144 rBuffer
.append( "&" );
148 rBuffer
.append( """ );
152 rBuffer
.append( "'" );
156 rBuffer
.append( "<" );
160 rBuffer
.append( ">" );
170 bool createContentProviderData(
171 std::u16string_view rProvider
,
172 const uno::Reference
< container::XHierarchicalNameAccess
>& rxHierNameAccess
,
173 ContentProviderData
& rInfo
)
175 // Obtain service name.
180 if ( !( rxHierNameAccess
->getByHierarchicalName(
181 OUString::Concat(rProvider
) + "/ServiceName" ) >>= aValue
) )
183 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
184 "Error getting item value!" );
187 catch (const container::NoSuchElementException
&)
192 rInfo
.ServiceName
= aValue
;
194 // Obtain URL Template.
196 if ( !( rxHierNameAccess
->getByHierarchicalName(
197 OUString::Concat(rProvider
) + "/URLTemplate" ) >>= aValue
) )
199 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
200 "Error getting item value!" );
203 rInfo
.URLTemplate
= aValue
;
207 if ( !( rxHierNameAccess
->getByHierarchicalName(
208 OUString::Concat(rProvider
) + "/Arguments" ) >>= aValue
) )
210 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
211 "Error getting item value!" );
214 rInfo
.Arguments
= aValue
;
221 // UniversalContentBroker Implementation.
224 UniversalContentBroker::UniversalContentBroker(
225 const Reference
< css::uno::XComponentContext
>& xContext
)
226 : m_xContext( xContext
),
229 OSL_ENSURE( m_xContext
.is(),
230 "UniversalContentBroker ctor: No service manager" );
235 UniversalContentBroker::~UniversalContentBroker()
240 // XComponent methods.
244 void SAL_CALL
UniversalContentBroker::dispose()
246 if ( m_pDisposeEventListeners
&& m_pDisposeEventListeners
->getLength() )
249 aEvt
.Source
= static_cast< XComponent
* >(this);
250 m_pDisposeEventListeners
->disposeAndClear( aEvt
);
253 if ( m_xNotifier
.is() )
254 m_xNotifier
->removeChangesListener( this );
259 void SAL_CALL
UniversalContentBroker::addEventListener(
260 const Reference
< XEventListener
>& Listener
)
262 if ( !m_pDisposeEventListeners
)
263 m_pDisposeEventListeners
.reset( new OInterfaceContainerHelper3
<css::lang::XEventListener
>( m_aMutex
) );
265 m_pDisposeEventListeners
->addInterface( Listener
);
270 void SAL_CALL
UniversalContentBroker::removeEventListener(
271 const Reference
< XEventListener
>& Listener
)
273 if ( m_pDisposeEventListeners
)
274 m_pDisposeEventListeners
->removeInterface( Listener
);
276 // Note: Don't want to delete empty container here -> performance.
280 // XServiceInfo methods.
282 OUString SAL_CALL
UniversalContentBroker::getImplementationName()
284 return u
"com.sun.star.comp.ucb.UniversalContentBroker"_ustr
;
286 sal_Bool SAL_CALL
UniversalContentBroker::supportsService( const OUString
& ServiceName
)
288 return cppu::supportsService( this, ServiceName
);
290 css::uno::Sequence
< OUString
> SAL_CALL
UniversalContentBroker::getSupportedServiceNames()
292 return { u
"com.sun.star.ucb.UniversalContentBroker"_ustr
};
296 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
297 ucb_UniversalContentBroker_get_implementation(
298 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
300 return cppu::acquire(new UniversalContentBroker(context
));
304 // XInitialization methods.
308 void SAL_CALL
UniversalContentBroker::initialize( const css::uno::Sequence
< Any
>& aArguments
)
311 osl::MutexGuard
aGuard(m_aMutex
);
312 if (m_aArguments
.hasElements())
314 if (aArguments
.hasElements()
315 && !(m_aArguments
.getLength() == 2
316 && aArguments
.getLength() == 2
317 && m_aArguments
[0] == aArguments
[0]
318 && m_aArguments
[1] == aArguments
[1]))
320 throw IllegalArgumentException(
321 u
"UCB reinitialized with different arguments"_ustr
,
326 if (!aArguments
.hasElements())
328 m_aArguments
= { Any(u
"Local"_ustr
), Any(u
"Office"_ustr
) };
332 m_aArguments
= aArguments
;
339 // XContentProviderManager methods.
343 Reference
< XContentProvider
> SAL_CALL
344 UniversalContentBroker::registerContentProvider(
345 const Reference
< XContentProvider
>& Provider
,
346 const OUString
& Scheme
,
347 sal_Bool ReplaceExisting
)
349 osl::MutexGuard
aGuard(m_aMutex
);
351 ProviderMap_Impl::iterator aIt
;
354 aIt
= m_aProviders
.find(Scheme
);
356 catch (const IllegalArgumentException
&)
358 return nullptr; //@@@
361 Reference
< XContentProvider
> xPrevious
;
362 if (aIt
== m_aProviders
.end())
364 ProviderList_Impl aList
;
365 aList
.push_front( ProviderListEntry_Impl(Provider
) );
368 m_aProviders
.add(Scheme
, aList
);
370 catch (const IllegalArgumentException
&)
372 return nullptr; //@@@
377 if (!ReplaceExisting
)
378 throw DuplicateProviderException();
380 ProviderList_Impl
& rList
= aIt
->getValue();
381 xPrevious
= rList
.front().getProvider();
382 rList
.push_front( ProviderListEntry_Impl(Provider
) );
390 void SAL_CALL
UniversalContentBroker::deregisterContentProvider(
391 const Reference
< XContentProvider
>& Provider
,
392 const OUString
& Scheme
)
394 osl::MutexGuard
aGuard(m_aMutex
);
396 ProviderMap_Impl::iterator aMapIt
;
399 aMapIt
= m_aProviders
.find(Scheme
);
401 catch (const IllegalArgumentException
&)
406 if (aMapIt
!= m_aProviders
.end())
408 ProviderList_Impl
& rList
= aMapIt
->getValue();
410 auto aListIt
= std::find_if(rList
.begin(), rList
.end(),
411 [&Provider
](const ProviderListEntry_Impl
& rEntry
) { return rEntry
.getProvider() == Provider
; });
412 if (aListIt
!= rList
.end())
413 rList
.erase(aListIt
);
416 m_aProviders
.erase(aMapIt
);
422 css::uno::Sequence
< ContentProviderInfo
> SAL_CALL
423 UniversalContentBroker::queryContentProviders()
425 // Return a list with information about active(!) content providers.
427 osl::MutexGuard
aGuard(m_aMutex
);
429 css::uno::Sequence
< ContentProviderInfo
> aSeq( m_aProviders
.size() );
430 ContentProviderInfo
* pInfo
= aSeq
.getArray();
432 ProviderMap_Impl::const_iterator end
= m_aProviders
.end();
433 for (ProviderMap_Impl::const_iterator
it(m_aProviders
.begin()); it
!= end
;
436 // Note: Active provider is always the first list element.
437 pInfo
->ContentProvider
= it
->getValue().front().getProvider();
438 pInfo
->Scheme
= it
->getRegexp();
447 Reference
< XContentProvider
> SAL_CALL
448 UniversalContentBroker::queryContentProvider( const OUString
&
451 return queryContentProvider( Identifier
, false );
455 // XContentProvider methods.
459 Reference
< XContent
> SAL_CALL
UniversalContentBroker::queryContent(
460 const Reference
< XContentIdentifier
>& Identifier
)
463 // Let the content provider for the scheme given with the content
464 // identifier create the XContent instance.
467 if ( !Identifier
.is() )
468 return Reference
< XContent
>();
470 Reference
< XContentProvider
> xProv
=
471 queryContentProvider( Identifier
->getContentIdentifier(), true );
473 return xProv
->queryContent( Identifier
);
475 return Reference
< XContent
>();
480 sal_Int32 SAL_CALL
UniversalContentBroker::compareContentIds(
481 const Reference
< XContentIdentifier
>& Id1
,
482 const Reference
< XContentIdentifier
>& Id2
)
484 OUString
aURI1( Id1
->getContentIdentifier() );
485 OUString
aURI2( Id2
->getContentIdentifier() );
487 Reference
< XContentProvider
> xProv1
488 = queryContentProvider( aURI1
, true );
489 Reference
< XContentProvider
> xProv2
490 = queryContentProvider( aURI2
, true );
492 // When both identifiers belong to the same provider, let that provider
493 // compare them; otherwise, simply compare the URI strings (which must
495 if ( xProv1
.is() && ( xProv1
== xProv2
) )
496 return xProv1
->compareContentIds( Id1
, Id2
);
498 return aURI1
.compareTo( aURI2
);
502 // XContentIdentifierFactory methods.
506 Reference
< XContentIdentifier
> SAL_CALL
507 UniversalContentBroker::createContentIdentifier(
508 const OUString
& ContentId
)
511 // Let the content provider for the scheme given with content
512 // identifier create the XContentIdentifier instance, if he supports
513 // the XContentIdentifierFactory interface. Otherwise create standard
514 // implementation object for XContentIdentifier.
517 Reference
< XContentIdentifier
> xIdentifier
;
519 Reference
< XContentProvider
> xProv
520 = queryContentProvider( ContentId
, true );
523 Reference
< XContentIdentifierFactory
> xFac( xProv
, UNO_QUERY
);
525 xIdentifier
= xFac
->createContentIdentifier( ContentId
);
528 if ( !xIdentifier
.is() )
529 xIdentifier
= new ContentIdentifier( ContentId
);
535 // XCommandProcessor methods.
539 sal_Int32 SAL_CALL
UniversalContentBroker::createCommandIdentifier()
541 osl::MutexGuard
aGuard( m_aMutex
);
543 // Just increase counter on every call to generate an identifier.
544 return ++m_nCommandId
;
549 Any SAL_CALL
UniversalContentBroker::execute(
550 const Command
& aCommand
,
552 const Reference
< XCommandEnvironment
>& Environment
)
557 // Note: Don't forget to adapt ucb_commands::CommandProcessorInfo
558 // ctor in ucbcmds.cxx when adding new commands!
561 if ( ( aCommand
.Handle
== GETCOMMANDINFO_HANDLE
) || aCommand
.Name
== GETCOMMANDINFO_NAME
)
567 aRet
<<= getCommandInfo();
569 else if ( ( aCommand
.Handle
== GLOBALTRANSFER_HANDLE
) || aCommand
.Name
== GLOBALTRANSFER_NAME
)
575 GlobalTransferCommandArgument2 aTransferArg
;
576 if ( !( aCommand
.Argument
>>= aTransferArg
) )
578 GlobalTransferCommandArgument aArg
;
579 if ( !( aCommand
.Argument
>>= aArg
) )
581 ucbhelper::cancelCommandExecution(
582 Any( IllegalArgumentException(
583 u
"Wrong argument type!"_ustr
,
590 // Copy infos into the new structure
591 aTransferArg
.Operation
= aArg
.Operation
;
592 aTransferArg
.SourceURL
= aArg
.SourceURL
;
593 aTransferArg
.TargetURL
= aArg
.TargetURL
;
594 aTransferArg
.NewTitle
= aArg
.NewTitle
;
595 aTransferArg
.NameClash
= aArg
.NameClash
;
598 globalTransfer( aTransferArg
, Environment
);
600 else if ( ( aCommand
.Handle
== CHECKIN_HANDLE
) || aCommand
.Name
== CHECKIN_NAME
)
602 ucb::CheckinArgument aCheckinArg
;
603 if ( !( aCommand
.Argument
>>= aCheckinArg
) )
605 ucbhelper::cancelCommandExecution(
606 Any( IllegalArgumentException(
607 u
"Wrong argument type!"_ustr
,
613 aRet
= checkIn( aCheckinArg
, Environment
);
621 ucbhelper::cancelCommandExecution(
622 Any( UnsupportedCommandException(
633 // XCommandProcessor2 methods.
637 void SAL_CALL
UniversalContentBroker::releaseCommandIdentifier(sal_Int32
/*aCommandId*/)
639 // @@@ Not implemented ( yet).
644 void SAL_CALL
UniversalContentBroker::abort( sal_Int32
)
646 // @@@ Not implemented ( yet).
650 // XChangesListener methods
654 void SAL_CALL
UniversalContentBroker::changesOccurred( const util::ChangesEvent
& Event
)
656 if ( !Event
.Changes
.hasElements() )
659 uno::Reference
< container::XHierarchicalNameAccess
> xHierNameAccess
;
660 Event
.Base
>>= xHierNameAccess
;
662 OSL_ASSERT( xHierNameAccess
.is() );
664 ContentProviderDataList aData
;
665 for ( const util::ElementChange
& rElem
: Event
.Changes
)
668 rElem
.Accessor
>>= aKey
;
670 ContentProviderData aInfo
;
672 // Removal of UCPs from the configuration leads to changesOccurred
673 // notifications, too, but it is hard to tell for a given
674 // ElementChange whether it is an addition or a removal, so as a
675 // heuristic consider as removals those that cause a
676 // NoSuchElementException in createContentProviderData.
678 // For now, removal of UCPs from the configuration is simply ignored
679 // (and not reflected in the UCB's data structures):
680 if (createContentProviderData(aKey
, xHierNameAccess
, aInfo
))
682 aData
.push_back(aInfo
);
686 prepareAndRegister(aData
);
690 // XEventListener methods
694 void SAL_CALL
UniversalContentBroker::disposing(const lang::EventObject
&)
696 if ( m_xNotifier
.is() )
698 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
700 if ( m_xNotifier
.is() )
706 // Non-interface methods
709 Reference
< XContentProvider
> UniversalContentBroker::queryContentProvider(
710 const OUString
& Identifier
,
713 osl::MutexGuard
aGuard( m_aMutex
);
715 ProviderList_Impl
const * pList
= m_aProviders
.map( Identifier
);
716 return pList
? bResolved
? pList
->front().getResolvedProvider()
717 : pList
->front().getProvider()
718 : Reference
< XContentProvider
>();
721 void UniversalContentBroker::configureUcb()
725 if (m_aArguments
.getLength() < 2
726 || !(m_aArguments
[0] >>= aKey1
) || !(m_aArguments
[1] >>= aKey2
))
728 OSL_FAIL("UniversalContentBroker::configureUcb(): Bad arguments");
732 ContentProviderDataList aData
;
733 if (!getContentProviderData(aKey1
, aKey2
, aData
))
735 SAL_WARN( "ucb", "No configuration");
739 prepareAndRegister(aData
);
742 void UniversalContentBroker::prepareAndRegister(
743 const ContentProviderDataList
& rData
)
745 for (const auto& rContentProviderData
: rData
)
747 OUString aProviderArguments
;
748 if (fillPlaceholders(rContentProviderData
.Arguments
,
750 &aProviderArguments
))
754 rContentProviderData
.ServiceName
,
756 rContentProviderData
.URLTemplate
);
760 OSL_FAIL("UniversalContentBroker::prepareAndRegister(): Bad argument placeholders");
765 bool UniversalContentBroker::getContentProviderData(
766 std::u16string_view rKey1
,
767 std::u16string_view rKey2
,
768 ContentProviderDataList
& rListToFill
)
770 if ( !m_xContext
.is() || rKey1
.empty() || rKey2
.empty() )
772 OSL_FAIL( "UniversalContentBroker::getContentProviderData - Invalid argument!" );
778 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
779 configuration::theDefaultProvider::get( m_xContext
);
781 OUStringBuffer
aFullPath(128);
783 "/org.openoffice.ucb.Configuration/ContentProviders"
785 makeAndAppendXMLName( aFullPath
, rKey1
);
786 aFullPath
.append( "']/SecondaryKeys/['" );
787 makeAndAppendXMLName( aFullPath
, rKey2
);
788 aFullPath
.append( "']/ProviderData" );
790 uno::Sequence
<uno::Any
> aArguments(comphelper::InitAnyPropertySequence(
792 {"nodepath", uno::Any(aFullPath
.makeStringAndClear())}
795 uno::Reference
< uno::XInterface
> xInterface(
796 xConfigProv
->createInstanceWithArguments(
797 u
"com.sun.star.configuration.ConfigurationAccess"_ustr
,
800 if ( !m_xNotifier
.is() )
802 m_xNotifier
.set( xInterface
, uno::UNO_QUERY_THROW
);
804 m_xNotifier
->addChangesListener( this );
807 uno::Reference
< container::XNameAccess
> xNameAccess(
808 xInterface
, uno::UNO_QUERY_THROW
);
810 const uno::Sequence
< OUString
> aElems
= xNameAccess
->getElementNames();
812 if ( aElems
.hasElements() )
814 uno::Reference
< container::XHierarchicalNameAccess
>
815 xHierNameAccess( xInterface
, uno::UNO_QUERY_THROW
);
817 // Iterate over children.
818 for ( const auto& rElem
: aElems
)
824 ContentProviderData aInfo
;
826 OUStringBuffer
aElemBuffer( "['" );
827 makeAndAppendXMLName( aElemBuffer
, rElem
);
828 aElemBuffer
.append( "']" );
831 createContentProviderData(
832 aElemBuffer
, xHierNameAccess
,
835 rListToFill
.push_back( aInfo
);
837 catch (const container::NoSuchElementException
&)
839 // getByHierarchicalName
840 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
841 "caught NoSuchElementException!" );
846 catch (const uno::RuntimeException
&)
848 TOOLS_WARN_EXCEPTION( "ucb", "" );
851 catch (const uno::Exception
&)
853 // createInstance, createInstanceWithArguments
855 TOOLS_WARN_EXCEPTION( "ucb", "" );
863 // ProviderListEntry_Impl implementation.
866 Reference
< XContentProvider
> const & ProviderListEntry_Impl::resolveProvider() const
868 if ( !m_xResolvedProvider
.is() )
870 Reference
< XContentProviderSupplier
> xSupplier(
871 m_xProvider
, UNO_QUERY
);
872 if ( xSupplier
.is() )
873 m_xResolvedProvider
= xSupplier
->getContentProvider();
875 if ( !m_xResolvedProvider
.is() )
876 m_xResolvedProvider
= m_xProvider
;
879 return m_xResolvedProvider
;
882 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */