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 *************************************************************************/
26 #include <osl/diagnose.h>
27 #include <sal/log.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <comphelper/processfactory.hxx>
30 #include <comphelper/interfacecontainer2.hxx>
31 #include <comphelper/propertysequence.hxx>
32 #include <com/sun/star/lang/IllegalArgumentException.hpp>
33 #include <com/sun/star/ucb/DuplicateProviderException.hpp>
34 #include <com/sun/star/ucb/GlobalTransferCommandArgument2.hpp>
35 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
36 #include <com/sun/star/ucb/XCommandInfo.hpp>
37 #include <com/sun/star/ucb/XContentProviderSupplier.hpp>
38 #include <com/sun/star/configuration/theDefaultProvider.hpp>
39 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
40 #include <com/sun/star/container/XNameAccess.hpp>
41 #include <com/sun/star/uno/Any.hxx>
42 #include <cppuhelper/weak.hxx>
43 #include <ucbhelper/cancelcommandexecution.hxx>
44 #include <ucbhelper/macros.hxx>
45 #include <tools/diagnose_ex.h>
46 #include "identify.hxx"
47 #include "ucbcmds.hxx"
51 using namespace comphelper
;
52 using namespace com::sun::star::uno
;
53 using namespace com::sun::star::lang
;
54 using namespace com::sun::star::ucb
;
55 using namespace ucb_impl
;
56 using namespace com::sun::star
;
57 using namespace ucbhelper
;
61 bool fillPlaceholders(OUString
const & rInput
,
62 uno::Sequence
< uno::Any
> const & rReplacements
,
65 sal_Unicode
const * p
= rInput
.getStr();
66 sal_Unicode
const * pEnd
= p
+ rInput
.getLength();
67 sal_Unicode
const * pCopy
= p
;
68 OUStringBuffer aBuffer
;
74 && p
[0] == 'a' && p
[1] == 'm' && p
[2] == 'p'
77 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
82 else if (pEnd
- p
>= 3
83 && p
[0] == 'l' && p
[1] == 't' && p
[2] == ';')
85 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
90 else if (pEnd
- p
>= 3
91 && p
[0] == 'g' && p
[1] == 't' && p
[2] == ';')
93 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
101 sal_Unicode
const * q
= p
;
102 while (q
!= pEnd
&& *q
!= '>')
106 OUString
aKey(p
, q
- p
);
109 for (sal_Int32 i
= 2; i
+ 1 < rReplacements
.getLength();
112 OUString aReplaceKey
;
113 if ((rReplacements
[i
] >>= aReplaceKey
)
114 && aReplaceKey
== aKey
115 && (rReplacements
[i
+ 1] >>= aValue
))
123 aBuffer
.append(pCopy
, p
- 1 - pCopy
);
124 aBuffer
.append(aValue
);
129 aBuffer
.append(pCopy
, pEnd
- pCopy
);
130 *pOutput
= aBuffer
.makeStringAndClear();
134 void makeAndAppendXMLName(
135 OUStringBuffer
& rBuffer
, const OUString
& rIn
)
137 sal_Int32 nCount
= rIn
.getLength();
138 for ( sal_Int32 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 const OUString
& rProvider
,
172 const uno::Reference
< container::XHierarchicalNameAccess
>& rxHierNameAccess
,
173 ContentProviderData
& rInfo
)
175 // Obtain service name.
180 if ( !( rxHierNameAccess
->getByHierarchicalName(
181 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 rProvider
+ "/URLTemplate" ) >>= aValue
) )
199 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
200 "Error getting item value!" );
203 rInfo
.URLTemplate
= aValue
;
207 if ( !( rxHierNameAccess
->getByHierarchicalName(
208 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 OInterfaceContainerHelper2( 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 "com.sun.star.comp.ucb.UniversalContentBroker";
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 { "com.sun.star.ucb.UniversalContentBroker" };
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(static_cast<cppu::OWeakObject
*>(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 "UCB reinitialized with different arguments",
322 static_cast< cppu::OWeakObject
* >(this), 0);
326 if (!aArguments
.hasElements())
328 m_aArguments
.realloc(2);
329 m_aArguments
[0] <<= OUString("Local");
330 m_aArguments
[1] <<= OUString("Office");
334 m_aArguments
= aArguments
;
341 // XContentProviderManager methods.
345 Reference
< XContentProvider
> SAL_CALL
346 UniversalContentBroker::registerContentProvider(
347 const Reference
< XContentProvider
>& Provider
,
348 const OUString
& Scheme
,
349 sal_Bool ReplaceExisting
)
351 osl::MutexGuard
aGuard(m_aMutex
);
353 ProviderMap_Impl::iterator aIt
;
356 aIt
= m_aProviders
.find(Scheme
);
358 catch (const IllegalArgumentException
&)
360 return nullptr; //@@@
363 Reference
< XContentProvider
> xPrevious
;
364 if (aIt
== m_aProviders
.end())
366 ProviderList_Impl aList
;
367 aList
.push_front( ProviderListEntry_Impl(Provider
) );
370 m_aProviders
.add(Scheme
, aList
);
372 catch (const IllegalArgumentException
&)
374 return nullptr; //@@@
379 if (!ReplaceExisting
)
380 throw DuplicateProviderException();
382 ProviderList_Impl
& rList
= aIt
->getValue();
383 xPrevious
= rList
.front().getProvider();
384 rList
.push_front( ProviderListEntry_Impl(Provider
) );
392 void SAL_CALL
UniversalContentBroker::deregisterContentProvider(
393 const Reference
< XContentProvider
>& Provider
,
394 const OUString
& Scheme
)
396 osl::MutexGuard
aGuard(m_aMutex
);
398 ProviderMap_Impl::iterator aMapIt
;
401 aMapIt
= m_aProviders
.find(Scheme
);
403 catch (const IllegalArgumentException
&)
408 if (aMapIt
!= m_aProviders
.end())
410 ProviderList_Impl
& rList
= aMapIt
->getValue();
412 auto aListIt
= std::find_if(rList
.begin(), rList
.end(),
413 [&Provider
](const ProviderListEntry_Impl
& rEntry
) { return rEntry
.getProvider() == Provider
; });
414 if (aListIt
!= rList
.end())
415 rList
.erase(aListIt
);
418 m_aProviders
.erase(aMapIt
);
424 css::uno::Sequence
< ContentProviderInfo
> SAL_CALL
425 UniversalContentBroker::queryContentProviders()
427 // Return a list with information about active(!) content providers.
429 osl::MutexGuard
aGuard(m_aMutex
);
431 css::uno::Sequence
< ContentProviderInfo
> aSeq( m_aProviders
.size() );
432 ContentProviderInfo
* pInfo
= aSeq
.getArray();
434 ProviderMap_Impl::const_iterator end
= m_aProviders
.end();
435 for (ProviderMap_Impl::const_iterator
it(m_aProviders
.begin()); it
!= end
;
438 // Note: Active provider is always the first list element.
439 pInfo
->ContentProvider
= it
->getValue().front().getProvider();
440 pInfo
->Scheme
= it
->getRegexp();
449 Reference
< XContentProvider
> SAL_CALL
450 UniversalContentBroker::queryContentProvider( const OUString
&
453 return queryContentProvider( Identifier
, false );
457 // XContentProvider methods.
461 Reference
< XContent
> SAL_CALL
UniversalContentBroker::queryContent(
462 const Reference
< XContentIdentifier
>& Identifier
)
465 // Let the content provider for the scheme given with the content
466 // identifier create the XContent instance.
469 if ( !Identifier
.is() )
470 return Reference
< XContent
>();
472 Reference
< XContentProvider
> xProv
=
473 queryContentProvider( Identifier
->getContentIdentifier(), true );
475 return xProv
->queryContent( Identifier
);
477 return Reference
< XContent
>();
482 sal_Int32 SAL_CALL
UniversalContentBroker::compareContentIds(
483 const Reference
< XContentIdentifier
>& Id1
,
484 const Reference
< XContentIdentifier
>& Id2
)
486 OUString
aURI1( Id1
->getContentIdentifier() );
487 OUString
aURI2( Id2
->getContentIdentifier() );
489 Reference
< XContentProvider
> xProv1
490 = queryContentProvider( aURI1
, true );
491 Reference
< XContentProvider
> xProv2
492 = queryContentProvider( aURI2
, true );
494 // When both identifiers belong to the same provider, let that provider
495 // compare them; otherwise, simply compare the URI strings (which must
497 if ( xProv1
.is() && ( xProv1
== xProv2
) )
498 return xProv1
->compareContentIds( Id1
, Id2
);
500 return aURI1
.compareTo( aURI2
);
504 // XContentIdentifierFactory methods.
508 Reference
< XContentIdentifier
> SAL_CALL
509 UniversalContentBroker::createContentIdentifier(
510 const OUString
& ContentId
)
513 // Let the content provider for the scheme given with content
514 // identifier create the XContentIdentifier instance, if he supports
515 // the XContentIdentifierFactory interface. Otherwise create standard
516 // implementation object for XContentIdentifier.
519 Reference
< XContentIdentifier
> xIdentifier
;
521 Reference
< XContentProvider
> xProv
522 = queryContentProvider( ContentId
, true );
525 Reference
< XContentIdentifierFactory
> xFac( xProv
, UNO_QUERY
);
527 xIdentifier
= xFac
->createContentIdentifier( ContentId
);
530 if ( !xIdentifier
.is() )
531 xIdentifier
= new ContentIdentifier( ContentId
);
537 // XCommandProcessor methods.
541 sal_Int32 SAL_CALL
UniversalContentBroker::createCommandIdentifier()
543 osl::MutexGuard
aGuard( m_aMutex
);
545 // Just increase counter on every call to generate an identifier.
546 return ++m_nCommandId
;
551 Any SAL_CALL
UniversalContentBroker::execute(
552 const Command
& aCommand
,
554 const Reference
< XCommandEnvironment
>& Environment
)
559 // Note: Don't forget to adapt ucb_commands::CommandProcessorInfo
560 // ctor in ucbcmds.cxx when adding new commands!
563 if ( ( aCommand
.Handle
== GETCOMMANDINFO_HANDLE
) || aCommand
.Name
== GETCOMMANDINFO_NAME
)
569 aRet
<<= getCommandInfo();
571 else if ( ( aCommand
.Handle
== GLOBALTRANSFER_HANDLE
) || aCommand
.Name
== GLOBALTRANSFER_NAME
)
577 GlobalTransferCommandArgument2 aTransferArg
;
578 if ( !( aCommand
.Argument
>>= aTransferArg
) )
580 GlobalTransferCommandArgument aArg
;
581 if ( !( aCommand
.Argument
>>= aArg
) )
583 ucbhelper::cancelCommandExecution(
584 makeAny( IllegalArgumentException(
585 "Wrong argument type!",
586 static_cast< cppu::OWeakObject
* >( this ),
592 // Copy infos into the new structure
593 aTransferArg
.Operation
= aArg
.Operation
;
594 aTransferArg
.SourceURL
= aArg
.SourceURL
;
595 aTransferArg
.TargetURL
= aArg
.TargetURL
;
596 aTransferArg
.NewTitle
= aArg
.NewTitle
;
597 aTransferArg
.NameClash
= aArg
.NameClash
;
600 globalTransfer( aTransferArg
, Environment
);
602 else if ( ( aCommand
.Handle
== CHECKIN_HANDLE
) || aCommand
.Name
== CHECKIN_NAME
)
604 ucb::CheckinArgument aCheckinArg
;
605 if ( !( aCommand
.Argument
>>= aCheckinArg
) )
607 ucbhelper::cancelCommandExecution(
608 makeAny( IllegalArgumentException(
609 "Wrong argument type!",
610 static_cast< cppu::OWeakObject
* >( this ),
615 aRet
= checkIn( aCheckinArg
, Environment
);
623 ucbhelper::cancelCommandExecution(
624 makeAny( UnsupportedCommandException(
626 static_cast< cppu::OWeakObject
* >( this ) ) ),
635 // XCommandProcessor2 methods.
639 void SAL_CALL
UniversalContentBroker::releaseCommandIdentifier(sal_Int32
/*aCommandId*/)
641 // @@@ Not implemented ( yet).
646 void SAL_CALL
UniversalContentBroker::abort( sal_Int32
)
648 // @@@ Not implemented ( yet).
652 // XChangesListener methods
656 void SAL_CALL
UniversalContentBroker::changesOccurred( const util::ChangesEvent
& Event
)
658 if ( !Event
.Changes
.hasElements() )
661 uno::Reference
< container::XHierarchicalNameAccess
> xHierNameAccess
;
662 Event
.Base
>>= xHierNameAccess
;
664 OSL_ASSERT( xHierNameAccess
.is() );
666 ContentProviderDataList aData
;
667 for ( const util::ElementChange
& rElem
: Event
.Changes
)
670 rElem
.Accessor
>>= aKey
;
672 ContentProviderData aInfo
;
674 // Removal of UCPs from the configuration leads to changesOccurred
675 // notifications, too, but it is hard to tell for a given
676 // ElementChange whether it is an addition or a removal, so as a
677 // heuristic consider as removals those that cause a
678 // NoSuchElementException in createContentProviderData.
680 // For now, removal of UCPs from the configuration is simply ignored
681 // (and not reflected in the UCB's data structures):
682 if (createContentProviderData(aKey
, xHierNameAccess
, aInfo
))
684 aData
.push_back(aInfo
);
688 prepareAndRegister(aData
);
692 // XEventListener methods
696 void SAL_CALL
UniversalContentBroker::disposing(const lang::EventObject
&)
698 if ( m_xNotifier
.is() )
700 osl::Guard
< osl::Mutex
> aGuard( m_aMutex
);
702 if ( m_xNotifier
.is() )
708 // Non-interface methods
711 Reference
< XContentProvider
> UniversalContentBroker::queryContentProvider(
712 const OUString
& Identifier
,
715 osl::MutexGuard
aGuard( m_aMutex
);
717 ProviderList_Impl
const * pList
= m_aProviders
.map( Identifier
);
718 return pList
? bResolved
? pList
->front().getResolvedProvider()
719 : pList
->front().getProvider()
720 : Reference
< XContentProvider
>();
723 void UniversalContentBroker::configureUcb()
727 if (m_aArguments
.getLength() < 2
728 || !(m_aArguments
[0] >>= aKey1
) || !(m_aArguments
[1] >>= aKey2
))
730 OSL_FAIL("UniversalContentBroker::configureUcb(): Bad arguments");
734 ContentProviderDataList aData
;
735 if (!getContentProviderData(aKey1
, aKey2
, aData
))
737 SAL_WARN( "ucb", "No configuration");
741 prepareAndRegister(aData
);
744 void UniversalContentBroker::prepareAndRegister(
745 const ContentProviderDataList
& rData
)
747 for (const auto& rContentProviderData
: rData
)
749 OUString aProviderArguments
;
750 if (fillPlaceholders(rContentProviderData
.Arguments
,
752 &aProviderArguments
))
756 rContentProviderData
.ServiceName
,
758 rContentProviderData
.URLTemplate
);
762 OSL_FAIL("UniversalContentBroker::prepareAndRegister(): Bad argument placeholders");
767 bool UniversalContentBroker::getContentProviderData(
768 const OUString
& rKey1
,
769 const OUString
& rKey2
,
770 ContentProviderDataList
& rListToFill
)
772 if ( !m_xContext
.is() || rKey1
.isEmpty() || rKey2
.isEmpty() )
774 OSL_FAIL( "UniversalContentBroker::getContentProviderData - Invalid argument!" );
780 uno::Reference
< lang::XMultiServiceFactory
> xConfigProv
=
781 configuration::theDefaultProvider::get( m_xContext
);
783 OUStringBuffer
aFullPath(128);
785 "/org.openoffice.ucb.Configuration/ContentProviders"
787 makeAndAppendXMLName( aFullPath
, rKey1
);
788 aFullPath
.append( "']/SecondaryKeys/['" );
789 makeAndAppendXMLName( aFullPath
, rKey2
);
790 aFullPath
.append( "']/ProviderData" );
792 uno::Sequence
<uno::Any
> aArguments(comphelper::InitAnyPropertySequence(
794 {"nodepath", uno::Any(aFullPath
.makeStringAndClear())}
797 uno::Reference
< uno::XInterface
> xInterface(
798 xConfigProv
->createInstanceWithArguments(
799 "com.sun.star.configuration.ConfigurationAccess",
802 if ( !m_xNotifier
.is() )
804 m_xNotifier
.set( xInterface
, uno::UNO_QUERY_THROW
);
806 m_xNotifier
->addChangesListener( this );
809 uno::Reference
< container::XNameAccess
> xNameAccess(
810 xInterface
, uno::UNO_QUERY_THROW
);
812 const uno::Sequence
< OUString
> aElems
= xNameAccess
->getElementNames();
814 if ( aElems
.hasElements() )
816 uno::Reference
< container::XHierarchicalNameAccess
>
817 xHierNameAccess( xInterface
, uno::UNO_QUERY_THROW
);
819 // Iterate over children.
820 for ( const auto& rElem
: aElems
)
826 ContentProviderData aInfo
;
828 OUStringBuffer aElemBuffer
;
829 aElemBuffer
.append( "['" );
830 makeAndAppendXMLName( aElemBuffer
, rElem
);
831 aElemBuffer
.append( "']" );
834 createContentProviderData(
835 aElemBuffer
.makeStringAndClear(), xHierNameAccess
,
838 rListToFill
.push_back( aInfo
);
840 catch (const container::NoSuchElementException
&)
842 // getByHierarchicalName
843 OSL_FAIL( "UniversalContentBroker::getContentProviderData - "
844 "caught NoSuchElementException!" );
849 catch (const uno::RuntimeException
&)
851 TOOLS_WARN_EXCEPTION( "ucb", "" );
854 catch (const uno::Exception
&)
856 // createInstance, createInstanceWithArguments
858 TOOLS_WARN_EXCEPTION( "ucb", "" );
866 // ProviderListEntry_Impl implementation.
869 Reference
< XContentProvider
> const & ProviderListEntry_Impl::resolveProvider() const
871 if ( !m_xResolvedProvider
.is() )
873 Reference
< XContentProviderSupplier
> xSupplier(
874 m_xProvider
, UNO_QUERY
);
875 if ( xSupplier
.is() )
876 m_xResolvedProvider
= xSupplier
->getContentProvider();
878 if ( !m_xResolvedProvider
.is() )
879 m_xResolvedProvider
= m_xProvider
;
882 return m_xResolvedProvider
;
885 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */