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 <sal/config.h>
12 #include <boost/make_shared.hpp>
14 #include <com/sun/star/beans/PropertyAttribute.hpp>
15 #include <com/sun/star/beans/PropertyValue.hpp>
16 #include <com/sun/star/beans/XPropertySetInfo.hpp>
17 #include <com/sun/star/lang/IllegalArgumentException.hpp>
18 #include <com/sun/star/ucb/XCommandInfo.hpp>
19 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
21 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
22 #include <com/sun/star/xml/crypto/DigestID.hpp>
23 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
26 #include <config_oauth2.h>
27 #include <rtl/uri.hxx>
28 #include <sal/log.hxx>
29 #include <tools/urlobj.hxx>
30 #include <ucbhelper/cancelcommandexecution.hxx>
31 #include <ucbhelper/contentidentifier.hxx>
32 #include <ucbhelper/propertyvalueset.hxx>
33 #include <ucbhelper/proxydecider.hxx>
34 #include <ucbhelper/macros.hxx>
36 #include "auth_provider.hxx"
37 #include "certvalidation_handler.hxx"
38 #include "cmis_content.hxx"
39 #include "cmis_provider.hxx"
40 #include "cmis_repo_content.hxx"
41 #include "cmis_resultset.hxx"
44 #define OUSTR_TO_STDSTR(s) std::string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ) )
45 #define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
47 using namespace com::sun::star
;
51 RepoContent::RepoContent( const uno::Reference
< uno::XComponentContext
>& rxContext
,
52 ContentProvider
*pProvider
, const uno::Reference
< ucb::XContentIdentifier
>& Identifier
,
53 std::vector
< libcmis::RepositoryPtr
> && aRepos
)
54 : ContentImplHelper( rxContext
, pProvider
, Identifier
),
55 m_pProvider( pProvider
),
56 m_aURL( Identifier
->getContentIdentifier( ) ),
57 m_aRepositories( std::move(aRepos
) )
59 // Split the URL into bits
60 OUString sURL
= m_xIdentifier
->getContentIdentifier( );
61 SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL
);
63 m_sRepositoryId
= m_aURL
.getObjectPath();
64 if (!m_sRepositoryId
.isEmpty() && m_sRepositoryId
[0] == '/')
65 m_sRepositoryId
= m_sRepositoryId
.copy(1);
68 RepoContent::~RepoContent()
72 uno::Any
RepoContent::getBadArgExcept()
74 return uno::Any( lang::IllegalArgumentException(
75 "Wrong argument type!",
76 static_cast< cppu::OWeakObject
* >( this ), -1) );
79 uno::Reference
< sdbc::XRow
> RepoContent::getPropertyValues(
80 const uno::Sequence
< beans::Property
>& rProperties
,
81 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
83 rtl::Reference
< ::ucbhelper::PropertyValueSet
> xRow
= new ::ucbhelper::PropertyValueSet( m_xContext
);
85 for( const beans::Property
& rProp
: rProperties
)
89 if ( rProp
.Name
== "IsDocument" )
91 xRow
->appendBoolean( rProp
, false );
93 else if ( rProp
.Name
== "IsFolder" )
95 xRow
->appendBoolean( rProp
, true );
97 else if ( rProp
.Name
== "Title" )
99 xRow
->appendString( rProp
, STD_TO_OUSTR( getRepository( xEnv
)->getName( ) ) );
101 else if ( rProp
.Name
== "IsReadOnly" )
103 xRow
->appendBoolean( rProp
, true );
107 xRow
->appendVoid( rProp
);
108 SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp
.Name
);
111 catch (const libcmis::Exception
&)
113 xRow
->appendVoid( rProp
);
120 void RepoContent::getRepositories( const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
123 // Initialize NSS library to make sure libcmis (and curl) can access CACERTs using NSS
124 // when using internal libcurl.
125 uno::Reference
< css::xml::crypto::XNSSInitializer
>
126 xNSSInitializer
= css::xml::crypto::NSSInitializer::create( m_xContext
);
128 uno::Reference
< css::xml::crypto::XDigestContext
> xDigestContext(
129 xNSSInitializer
->getDigestContext( css::xml::crypto::DigestID::SHA256
,
130 uno::Sequence
< beans::NamedValue
>() ),
131 uno::UNO_SET_THROW
);
134 // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
135 ucbhelper::InternetProxyDecider
aProxyDecider( m_xContext
);
136 INetURLObject
aBindingUrl( m_aURL
.getBindingUrl( ) );
137 const ucbhelper::InternetProxyServer
& rProxy
= aProxyDecider
.getProxy(
138 INetURLObject::GetScheme( aBindingUrl
.GetProtocol( ) ), aBindingUrl
.GetHost(), aBindingUrl
.GetPort() );
139 OUString sProxy
= rProxy
.aName
;
140 if ( rProxy
.nPort
> 0 )
141 sProxy
+= ":" + OUString::number( rProxy
.nPort
);
142 libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy
), std::string(), std::string(), std::string() );
144 if ( !m_aRepositories
.empty() )
147 // Set the SSL Validation handler
148 libcmis::CertValidationHandlerPtr
certHandler(
149 new CertValidationHandler( xEnv
, m_xContext
, aBindingUrl
.GetHost( ) ) );
150 libcmis::SessionFactory::setCertificateValidationHandler( certHandler
);
152 // Get the auth credentials
153 AuthProvider
authProvider( xEnv
, m_xIdentifier
->getContentIdentifier( ), m_aURL
.getBindingUrl( ) );
154 AuthProvider::setXEnv( xEnv
);
156 std::string rUsername
= OUSTR_TO_STDSTR( m_aURL
.getUsername( ) );
157 std::string rPassword
= OUSTR_TO_STDSTR( m_aURL
.getPassword( ) );
159 bool bIsDone
= false;
163 if ( authProvider
.authenticationQuery( rUsername
, rPassword
) )
167 // Create a session to get repositories
168 libcmis::OAuth2DataPtr oauth2Data
;
169 if ( m_aURL
.getBindingUrl( ) == GDRIVE_BASE_URL
)
171 libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthProvider::copyWebAuthCodeFallback
);
172 oauth2Data
= boost::make_shared
<libcmis::OAuth2Data
>(
173 GDRIVE_AUTH_URL
, GDRIVE_TOKEN_URL
,
174 GDRIVE_SCOPE
, GDRIVE_REDIRECT_URI
,
175 GDRIVE_CLIENT_ID
, GDRIVE_CLIENT_SECRET
);
177 if ( m_aURL
.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL
) )
178 oauth2Data
= boost::make_shared
<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::copyWebAuthCodeFallback
);
185 oauth2Data
= boost::make_shared
<libcmis::OAuth2Data
>(
186 ONEDRIVE_AUTH_URL
, ONEDRIVE_TOKEN_URL
,
187 ONEDRIVE_SCOPE
, ONEDRIVE_REDIRECT_URI
,
188 ONEDRIVE_CLIENT_ID
, ONEDRIVE_CLIENT_SECRET
);
191 std::unique_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 ),
199 m_aRepositories
= session
->getRepositories( );
203 catch ( const libcmis::Exception
& e
)
205 SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e
.what() );
207 if ( e
.getType() != "permissionDenied" )
209 ucbhelper::cancelCommandExecution(
210 ucb::IOErrorCode_INVALID_DEVICE
,
211 uno::Sequence
< uno::Any
>( 0 ),
218 // Throw user cancelled exception
219 ucbhelper::cancelCommandExecution(
220 ucb::IOErrorCode_ABORT
,
221 uno::Sequence
< uno::Any
>( 0 ),
223 "Authentication cancelled" );
228 libcmis::RepositoryPtr
RepoContent::getRepository( const uno::Reference
< ucb::XCommandEnvironment
> & xEnv
)
230 // Ensure we have the repositories extracted
231 getRepositories( xEnv
);
233 libcmis::RepositoryPtr repo
;
235 if ( !m_sRepositoryId
.isEmpty() )
237 auto it
= std::find_if(m_aRepositories
.begin(), m_aRepositories
.end(),
238 [&](const libcmis::RepositoryPtr
& rRepo
) { return STD_TO_OUSTR(rRepo
->getId()) == m_sRepositoryId
; });
239 if (it
!= m_aRepositories
.end())
243 repo
= m_aRepositories
.front( );
247 uno::Sequence
< beans::Property
> RepoContent::getProperties(
248 const uno::Reference
< ucb::XCommandEnvironment
> & /*xEnv*/ )
250 static const beans::Property aGenericProperties
[] =
252 beans::Property( "IsDocument",
253 -1, cppu::UnoType
<bool>::get(),
254 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
255 beans::Property( "IsFolder",
256 -1, cppu::UnoType
<bool>::get(),
257 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
258 beans::Property( "Title",
259 -1, cppu::UnoType
<OUString
>::get(),
260 beans::PropertyAttribute::BOUND
),
261 beans::Property( "IsReadOnly",
262 -1, cppu::UnoType
<bool>::get(),
263 beans::PropertyAttribute::BOUND
| beans::PropertyAttribute::READONLY
),
266 const int nProps
= SAL_N_ELEMENTS(aGenericProperties
);
267 return uno::Sequence
< beans::Property
> ( aGenericProperties
, nProps
);
270 uno::Sequence
< ucb::CommandInfo
> RepoContent::getCommands(
271 const uno::Reference
< ucb::XCommandEnvironment
> & /*xEnv*/ )
273 static const ucb::CommandInfo aCommandInfoTable
[] =
278 -1, cppu::UnoType
<void>::get() ),
280 ( "getPropertySetInfo",
281 -1, cppu::UnoType
<void>::get() ),
283 ( "getPropertyValues",
284 -1, cppu::UnoType
<uno::Sequence
< beans::Property
>>::get() ),
286 ( "setPropertyValues",
287 -1, cppu::UnoType
<uno::Sequence
< beans::PropertyValue
>>::get() ),
289 // Optional standard commands
292 -1, cppu::UnoType
<ucb::OpenCommandArgument2
>::get() ),
295 const int nProps
= SAL_N_ELEMENTS(aCommandInfoTable
);
296 return uno::Sequence
< ucb::CommandInfo
>(aCommandInfoTable
, nProps
);
299 OUString
RepoContent::getParentURL( )
301 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
308 XTYPEPROVIDER_COMMON_IMPL( RepoContent
);
310 OUString SAL_CALL
RepoContent::getImplementationName()
312 return "com.sun.star.comp.CmisRepoContent";
315 uno::Sequence
< OUString
> SAL_CALL
RepoContent::getSupportedServiceNames()
317 return { "com.sun.star.ucb.Content" };
320 OUString SAL_CALL
RepoContent::getContentType()
322 return CMIS_REPO_TYPE
;
325 uno::Any SAL_CALL
RepoContent::execute(
326 const ucb::Command
& aCommand
,
327 sal_Int32
/*CommandId*/,
328 const uno::Reference
< ucb::XCommandEnvironment
>& xEnv
)
330 SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand
.Name
);
334 if ( aCommand
.Name
== "getPropertyValues" )
336 uno::Sequence
< beans::Property
> Properties
;
337 if ( !( aCommand
.Argument
>>= Properties
) )
338 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv
);
339 aRet
<<= getPropertyValues( Properties
, xEnv
);
341 else if ( aCommand
.Name
== "getPropertySetInfo" )
342 aRet
<<= getPropertySetInfo( xEnv
, false );
343 else if ( aCommand
.Name
== "getCommandInfo" )
344 aRet
<<= getCommandInfo( xEnv
, false );
345 else if ( aCommand
.Name
== "open" )
347 ucb::OpenCommandArgument2 aOpenCommand
;
348 if ( !( aCommand
.Argument
>>= aOpenCommand
) )
349 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv
);
350 const ucb::OpenCommandArgument2
& rOpenCommand
= aOpenCommand
;
352 getRepositories( xEnv
);
353 uno::Reference
< ucb::XDynamicResultSet
> xSet
354 = new DynamicResultSet(m_xContext
, this, rOpenCommand
, xEnv
);
359 SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
365 void SAL_CALL
RepoContent::abort( sal_Int32
/*CommandId*/ )
367 SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
371 uno::Sequence
< uno::Type
> SAL_CALL
RepoContent::getTypes()
373 static cppu::OTypeCollection s_aFolderCollection
374 (CPPU_TYPE_REF( lang::XTypeProvider
),
375 CPPU_TYPE_REF( lang::XServiceInfo
),
376 CPPU_TYPE_REF( lang::XComponent
),
377 CPPU_TYPE_REF( ucb::XContent
),
378 CPPU_TYPE_REF( ucb::XCommandProcessor
),
379 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier
),
380 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier
),
381 CPPU_TYPE_REF( beans::XPropertyContainer
),
382 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier
),
383 CPPU_TYPE_REF( container::XChild
) );
384 return s_aFolderCollection
.getTypes();
387 std::vector
< uno::Reference
< ucb::XContent
> > RepoContent::getChildren( )
389 std::vector
< uno::Reference
< ucb::XContent
> > result
;
391 // TODO Cache the results somehow
392 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
394 if ( m_sRepositoryId
.isEmpty( ) )
396 for ( const auto& rRepo
: m_aRepositories
)
399 aUrl
.setObjectPath( STD_TO_OUSTR( rRepo
->getId( ) ) );
401 uno::Reference
< ucb::XContentIdentifier
> xId
= new ucbhelper::ContentIdentifier( aUrl
.asString( ) );
402 uno::Reference
< ucb::XContent
> xContent
= new RepoContent( m_xContext
, m_pProvider
, xId
, std::vector(m_aRepositories
) );
404 result
.push_back( xContent
);
409 // Return the repository root as child
411 OUString sEncodedBinding
= rtl::Uri::encode(
412 m_aURL
.getBindingUrl( ) + "#" + m_sRepositoryId
,
413 rtl_UriCharClassRelSegment
,
414 rtl_UriEncodeKeepEscapes
,
415 RTL_TEXTENCODING_UTF8
);
416 sUrl
= "vnd.libreoffice.cmis://" + sEncodedBinding
;
418 uno::Reference
< ucb::XContentIdentifier
> xId
= new ucbhelper::ContentIdentifier( sUrl
);
419 uno::Reference
< ucb::XContent
> xContent
= new Content( m_xContext
, m_pProvider
, xId
);
421 result
.push_back( xContent
);
427 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */