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/.
10 #include <com/sun/star/beans/PropertyAttribute.hpp>
11 #include <com/sun/star/beans/PropertyValue.hpp>
12 #include <com/sun/star/beans/XPropertySetInfo.hpp>
13 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
14 #include <com/sun/star/task/XInteractionHandler.hpp>
15 #include <com/sun/star/ucb/XCommandInfo.hpp>
16 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
17 #include <com/sun/star/ucb/XProgressHandler.hpp>
19 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
20 #include <com/sun/star/xml/crypto/XDigestContextSupplier.hpp>
21 #include <com/sun/star/xml/crypto/DigestID.hpp>
22 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
25 #include <comphelper/processfactory.hxx>
26 #include <config_oauth2.h>
27 #include <rtl/uri.hxx>
28 #include <ucbhelper/cancelcommandexecution.hxx>
29 #include <ucbhelper/commandenvironment.hxx>
30 #include <ucbhelper/contentidentifier.hxx>
31 #include <ucbhelper/propertyvalueset.hxx>
32 #include <ucbhelper/proxydecider.hxx>
34 #include "auth_provider.hxx"
35 #include "certvalidation_handler.hxx"
36 #include "cmis_content.hxx"
37 #include "cmis_provider.hxx"
38 #include "cmis_repo_content.hxx"
39 #include "cmis_resultset.hxx"
40 #include <boost/scoped_ptr.hpp>
42 #define OUSTR_TO_STDSTR(s) string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() )
43 #define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
45 using namespace com::sun::star
;
50 RepoContent::RepoContent( const uno::Reference
< uno::XComponentContext
>& rxContext
,
51 ContentProvider
*pProvider
, const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
52 vector
< libcmis::RepositoryPtr
> aRepos
)
53 throw ( ucb::ContentCreationException
)
54 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
55 m_pProvider( pProvider
),
56 m_aURL( Identifier
->getContentIdentifier( ) ),
58 m_aRepositories( aRepos
)
60 // Split the URL into bits
61 OUString sURL
= m_xIdentifier
->getContentIdentifier( );
62 SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL
);
64 m_sRepositoryId
= m_aURL
.getObjectPath();
65 if (!m_sRepositoryId
.isEmpty() && m_sRepositoryId
[0] == '/')
66 m_sRepositoryId
= m_sRepositoryId
.copy(1);
69 RepoContent::~RepoContent()
73 uno::Any
RepoContent::getBadArgExcept()
75 return uno::makeAny( lang::IllegalArgumentException(
76 OUString("Wrong argument type!"),
77 static_cast< cppu::OWeakObject
* >( this ), -1) );
80 uno::Reference
< sdbc::XRow
> RepoContent::getPropertyValues(
81 const uno::Sequence
< beans::Property
>& rProperties
,
82 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
84 rtl::Reference
< ::ucbhelper::PropertyValueSet
> xRow
= new ::ucbhelper::PropertyValueSet( m_xContext
);
87 const beans::Property
* pProps
;
89 nProps
= rProperties
.getLength();
90 pProps
= rProperties
.getConstArray();
92 for( sal_Int32 n
= 0; n
< nProps
; ++n
)
94 const beans::Property
& rProp
= pProps
[ n
];
98 if ( rProp
.Name
== "IsDocument" )
100 xRow
->appendBoolean( rProp
, false );
102 else if ( rProp
.Name
== "IsFolder" )
104 xRow
->appendBoolean( rProp
, true );
106 else if ( rProp
.Name
== "Title" )
108 xRow
->appendString( rProp
, STD_TO_OUSTR( getRepository( xEnv
)->getName( ) ) );
110 else if ( rProp
.Name
== "IsReadOnly" )
112 xRow
->appendBoolean( rProp
, true );
116 xRow
->appendVoid( rProp
);
117 SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp
.Name
);
120 catch (const libcmis::Exception
&)
122 xRow
->appendVoid( rProp
);
126 return uno::Reference
< sdbc::XRow
>( xRow
.get() );
129 void RepoContent::getRepositories( const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
132 // Initialize NSS library to make sure libcmis (and curl) can access CACERTs using NSS
133 // when using internal libcurl.
134 uno::Reference
< com::sun::star::xml::crypto::XNSSInitializer
>
135 xNSSInitializer
= com::sun::star::xml::crypto::NSSInitializer::create( m_xContext
);
137 uno::Reference
< com::sun::star::xml::crypto::XDigestContext
> xDigestContext(
138 xNSSInitializer
->getDigestContext( com::sun::star::xml::crypto::DigestID::SHA256
,
139 uno::Sequence
< beans::NamedValue
>() ),
140 uno::UNO_SET_THROW
);
143 // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
144 ucbhelper::InternetProxyDecider
aProxyDecider( m_xContext
);
145 INetURLObject
aBindingUrl( m_aURL
.getBindingUrl( ) );
146 const ucbhelper::InternetProxyServer
& rProxy
= aProxyDecider
.getProxy(
147 INetURLObject::GetScheme( aBindingUrl
.GetProtocol( ) ), aBindingUrl
.GetHost(), aBindingUrl
.GetPort() );
148 OUString sProxy
= rProxy
.aName
;
149 if ( rProxy
.nPort
> 0 )
150 sProxy
+= ":" + OUString::number( rProxy
.nPort
);
151 libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy
), string(), string(), string() );
153 if ( m_aRepositories
.empty() )
155 // Set the SSL Validation handler
156 libcmis::CertValidationHandlerPtr
certHandler(
157 new CertValidationHandler( xEnv
, m_xContext
, aBindingUrl
.GetHost( ) ) );
158 libcmis::SessionFactory::setCertificateValidationHandler( certHandler
);
160 // Get the auth credentials
161 AuthProvider
authProvider( xEnv
, m_xIdentifier
->getContentIdentifier( ), m_aURL
.getBindingUrl( ) );
162 AuthProvider::setXEnv( xEnv
);
164 string rUsername
= OUSTR_TO_STDSTR( m_aURL
.getUsername( ) );
165 string rPassword
= OUSTR_TO_STDSTR( m_aURL
.getPassword( ) );
166 if ( authProvider
.authenticationQuery( rUsername
, rPassword
) )
170 // Create a session to get repositories
171 libcmis::OAuth2DataPtr oauth2Data
;
172 if ( m_aURL
.getBindingUrl( ) == GDRIVE_BASE_URL
)
173 oauth2Data
.reset( new libcmis::OAuth2Data(
174 GDRIVE_AUTH_URL
, GDRIVE_TOKEN_URL
,
175 GDRIVE_SCOPE
, GDRIVE_REDIRECT_URI
,
176 GDRIVE_CLIENT_ID
, GDRIVE_CLIENT_SECRET
) );
177 if ( m_aURL
.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL
) )
178 oauth2Data
.reset( new libcmis::OAuth2Data(
179 ALFRESCO_CLOUD_AUTH_URL
, ALFRESCO_CLOUD_TOKEN_URL
,
180 ALFRESCO_CLOUD_SCOPE
, ALFRESCO_CLOUD_REDIRECT_URI
,
181 ALFRESCO_CLOUD_CLIENT_ID
, ALFRESCO_CLOUD_CLIENT_SECRET
) );
182 if ( m_aURL
.getBindingUrl( ) == ONEDRIVE_BASE_URL
)
184 libcmis::SessionFactory::setOAuth2AuthCodeProvider( authProvider
.onedriveAuthCodeFallback
);
185 oauth2Data
.reset( new libcmis::OAuth2Data(
186 ONEDRIVE_AUTH_URL
, ONEDRIVE_TOKEN_URL
,
187 ONEDRIVE_SCOPE
, ONEDRIVE_REDIRECT_URI
,
188 ONEDRIVE_CLIENT_ID
, ONEDRIVE_CLIENT_SECRET
) );
191 boost::scoped_ptr
<libcmis::Session
> session(libcmis::SessionFactory::createSession(
192 OUSTR_TO_STDSTR( m_aURL
.getBindingUrl( ) ),
193 rUsername
, rPassword
, "", false, oauth2Data
));
195 ucbhelper::cancelCommandExecution(
196 ucb::IOErrorCode_INVALID_DEVICE
,
197 uno::Sequence
< uno::Any
>( 0 ),
200 m_aRepositories
= session
->getRepositories( );
202 catch (const libcmis::Exception
& e
)
204 SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e
.what() );
209 // Throw user cancelled exception
210 ucbhelper::cancelCommandExecution(
211 ucb::IOErrorCode_ABORT
,
212 uno::Sequence
< uno::Any
>( 0 ),
214 OUString( "Authentication cancelled" ) );
219 libcmis::RepositoryPtr
RepoContent::getRepository( const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
221 // Ensure we have the repositories extracted
222 getRepositories( xEnv
);
224 libcmis::RepositoryPtr repo
;
226 if ( !m_sRepositoryId
.isEmpty() )
228 for ( vector
< libcmis::RepositoryPtr
>::iterator it
= m_aRepositories
.begin( );
229 it
!= m_aRepositories
.end( ) && NULL
== repo
.get( ); ++it
)
231 if ( STD_TO_OUSTR( ( *it
)->getId( ) ) == m_sRepositoryId
)
236 repo
= m_aRepositories
.front( );
240 uno::Sequence
< beans::Property
> RepoContent::getProperties(
241 const uno::Reference
< ucb::XCommandEnvironment
> & /*xEnv*/ )
243 static const beans::Property aGenericProperties
[] =
245 beans::Property( OUString( "IsDocument" ),
246 -1, cppu::UnoType
<bool>::get(),
247 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
248 beans::Property( OUString( "IsFolder" ),
249 -1, cppu::UnoType
<bool>::get(),
250 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
251 beans::Property( OUString( "Title" ),
252 -1, cppu::UnoType
<OUString
>::get(),
253 beans::PropertyAttribute::BOUND
),
254 beans::Property( OUString( "IsReadOnly" ),
255 -1, cppu::UnoType
<bool>::get(),
256 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
259 const int nProps
= SAL_N_ELEMENTS(aGenericProperties
);
260 return uno::Sequence
< beans::Property
> ( aGenericProperties
, nProps
);
263 uno::Sequence
< ucb::CommandInfo
> RepoContent::getCommands(
264 const uno::Reference
< ucb::XCommandEnvironment
> & /*xEnv*/ )
266 static const ucb::CommandInfo aCommandInfoTable
[] =
270 ( OUString( "getCommandInfo" ),
271 -1, cppu::UnoType
<void>::get() ),
273 ( OUString( "getPropertySetInfo" ),
274 -1, cppu::UnoType
<void>::get() ),
276 ( OUString( "getPropertyValues" ),
277 -1, cppu::UnoType
<uno::Sequence
< beans::Property
>>::get() ),
279 ( OUString( "setPropertyValues" ),
280 -1, cppu::UnoType
<uno::Sequence
< beans::PropertyValue
>>::get() ),
282 // Optional standard commands
284 ( OUString( "open" ),
285 -1, cppu::UnoType
<ucb::OpenCommandArgument2
>::get() ),
288 const int nProps
= SAL_N_ELEMENTS(aCommandInfoTable
);
289 return uno::Sequence
< ucb::CommandInfo
>(aCommandInfoTable
, nProps
);
292 OUString
RepoContent::getParentURL( )
296 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
303 XTYPEPROVIDER_COMMON_IMPL( RepoContent
);
305 void SAL_CALL
RepoContent::acquire() throw()
307 ContentImplHelper::acquire();
310 void SAL_CALL
RepoContent::release() throw()
312 ContentImplHelper::release();
315 uno::Any SAL_CALL
RepoContent::queryInterface( const uno::Type
& rType
) throw ( uno::RuntimeException
, std::exception
)
317 return ContentImplHelper::queryInterface(rType
);
320 OUString SAL_CALL
RepoContent::getImplementationName() throw( uno::RuntimeException
, std::exception
)
322 return OUString("com.sun.star.comp.CmisRepoContent");
325 uno::Sequence
< OUString
> SAL_CALL
RepoContent::getSupportedServiceNames()
326 throw( uno::RuntimeException
, std::exception
)
328 uno::Sequence
< OUString
> aSNS( 1 );
329 aSNS
.getArray()[ 0 ] = "com.sun.star.ucb.Content";
333 OUString SAL_CALL
RepoContent::getContentType() throw( uno::RuntimeException
, std::exception
)
335 return OUString( CMIS_REPO_TYPE
);
338 uno::Any SAL_CALL
RepoContent::execute(
339 const ucb::Command
& aCommand
,
340 sal_Int32
/*CommandId*/,
341 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
342 throw( uno::Exception
, ucb::CommandAbortedException
, uno::RuntimeException
, std::exception
)
344 SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand
.Name
);
348 if ( aCommand
.Name
== "getPropertyValues" )
350 uno::Sequence
< beans::Property
> Properties
;
351 if ( !( aCommand
.Argument
>>= Properties
) )
352 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv
);
353 aRet
<<= getPropertyValues( Properties
, xEnv
);
355 else if ( aCommand
.Name
== "getPropertySetInfo" )
356 aRet
<<= getPropertySetInfo( xEnv
, false );
357 else if ( aCommand
.Name
== "getCommandInfo" )
358 aRet
<<= getCommandInfo( xEnv
, false );
359 else if ( aCommand
.Name
== "open" )
361 ucb::OpenCommandArgument2 aOpenCommand
;
362 if ( !( aCommand
.Argument
>>= aOpenCommand
) )
363 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv
);
364 const ucb::OpenCommandArgument2
& rOpenCommand
= aOpenCommand
;
366 getRepositories( xEnv
);
367 uno::Reference
< ucb::XDynamicResultSet
> xSet
368 = new DynamicResultSet(m_xContext
, this, rOpenCommand
, xEnv
);
373 SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
379 void SAL_CALL
RepoContent::abort( sal_Int32
/*CommandId*/ ) throw( uno::RuntimeException
, std::exception
)
381 SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
385 uno::Sequence
< uno::Type
> SAL_CALL
RepoContent::getTypes() throw( uno::RuntimeException
, std::exception
)
387 static cppu::OTypeCollection aFolderCollection
388 (CPPU_TYPE_REF( lang::XTypeProvider
),
389 CPPU_TYPE_REF( lang::XServiceInfo
),
390 CPPU_TYPE_REF( lang::XComponent
),
391 CPPU_TYPE_REF( ucb::XContent
),
392 CPPU_TYPE_REF( ucb::XCommandProcessor
),
393 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
394 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
395 CPPU_TYPE_REF( beans::XPropertyContainer
),
396 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
397 CPPU_TYPE_REF( container::XChild
) );
398 return aFolderCollection
.getTypes();
401 list
< uno::Reference
< ucb::XContent
> > RepoContent::getChildren( )
403 list
< uno::Reference
< ucb::XContent
> > result
;
405 // TODO Cache the results somehow
406 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
408 if ( m_sRepositoryId
.isEmpty( ) )
410 for ( vector
< libcmis::RepositoryPtr
>::iterator it
= m_aRepositories
.begin( );
411 it
!= m_aRepositories
.end(); ++it
)
414 aUrl
.setObjectPath( STD_TO_OUSTR( ( *it
)->getId( ) ) );
416 uno::Reference
< ucb::XContentIdentifier
> xId
= new ucbhelper::ContentIdentifier( aUrl
.asString( ) );
417 uno::Reference
< ucb::XContent
> xContent
= new RepoContent( m_xContext
, m_pProvider
, xId
, m_aRepositories
);
419 result
.push_back( xContent
);
424 // Return the repository root as child
426 OUString sEncodedBinding
= rtl::Uri::encode(
427 m_aURL
.getBindingUrl( ) + "#" + m_sRepositoryId
,
428 rtl_UriCharClassRelSegment
,
429 rtl_UriEncodeKeepEscapes
,
430 RTL_TEXTENCODING_UTF8
);
431 sUrl
= "vnd.libreoffice.cmis://" + sEncodedBinding
;
433 uno::Reference
< ucb::XContentIdentifier
> xId
= new ucbhelper::ContentIdentifier( sUrl
);
434 uno::Reference
< ucb::XContent
> xContent
= new Content( m_xContext
, m_pProvider
, xId
);
436 result
.push_back( xContent
);
442 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */