Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / ucb / source / ucp / cmis / cmis_repo_content.cxx
blob7225d51502aaff5fedba3a0bc56ff9718b815252
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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/IllegalArgumentException.hpp>
14 #include <com/sun/star/ucb/XCommandInfo.hpp>
15 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
16 #ifndef SYSTEM_CURL
17 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
18 #include <com/sun/star/xml/crypto/DigestID.hpp>
19 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
20 #endif
22 #include <config_oauth2.h>
23 #include <rtl/uri.hxx>
24 #include <sal/log.hxx>
25 #include <tools/urlobj.hxx>
26 #include <ucbhelper/cancelcommandexecution.hxx>
27 #include <ucbhelper/contentidentifier.hxx>
28 #include <ucbhelper/propertyvalueset.hxx>
29 #include <ucbhelper/proxydecider.hxx>
30 #include <ucbhelper/macros.hxx>
32 #include "auth_provider.hxx"
33 #include "certvalidation_handler.hxx"
34 #include "cmis_content.hxx"
35 #include "cmis_provider.hxx"
36 #include "cmis_repo_content.hxx"
37 #include "cmis_resultset.hxx"
38 #include <memory>
40 #define OUSTR_TO_STDSTR(s) std::string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() )
41 #define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
43 using namespace com::sun::star;
45 namespace cmis
47 RepoContent::RepoContent( const uno::Reference< uno::XComponentContext >& rxContext,
48 ContentProvider *pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier,
49 std::vector< libcmis::RepositoryPtr > const & aRepos )
50 : ContentImplHelper( rxContext, pProvider, Identifier ),
51 m_pProvider( pProvider ),
52 m_aURL( Identifier->getContentIdentifier( ) ),
53 m_sRepositoryId( ),
54 m_aRepositories( aRepos )
56 // Split the URL into bits
57 OUString sURL = m_xIdentifier->getContentIdentifier( );
58 SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL );
60 m_sRepositoryId = m_aURL.getObjectPath();
61 if (!m_sRepositoryId.isEmpty() && m_sRepositoryId[0] == '/')
62 m_sRepositoryId = m_sRepositoryId.copy(1);
65 RepoContent::~RepoContent()
69 uno::Any RepoContent::getBadArgExcept()
71 return uno::makeAny( lang::IllegalArgumentException(
72 "Wrong argument type!",
73 static_cast< cppu::OWeakObject * >( this ), -1) );
76 uno::Reference< sdbc::XRow > RepoContent::getPropertyValues(
77 const uno::Sequence< beans::Property >& rProperties,
78 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
80 rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
82 for( const beans::Property& rProp : rProperties )
84 try
86 if ( rProp.Name == "IsDocument" )
88 xRow->appendBoolean( rProp, false );
90 else if ( rProp.Name == "IsFolder" )
92 xRow->appendBoolean( rProp, true );
94 else if ( rProp.Name == "Title" )
96 xRow->appendString( rProp, STD_TO_OUSTR( getRepository( xEnv )->getName( ) ) );
98 else if ( rProp.Name == "IsReadOnly" )
100 xRow->appendBoolean( rProp, true );
102 else
104 xRow->appendVoid( rProp );
105 SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
108 catch (const libcmis::Exception&)
110 xRow->appendVoid( rProp );
114 return uno::Reference< sdbc::XRow >( xRow.get() );
117 void RepoContent::getRepositories( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
119 #ifndef SYSTEM_CURL
120 // Initialize NSS library to make sure libcmis (and curl) can access CACERTs using NSS
121 // when using internal libcurl.
122 uno::Reference< css::xml::crypto::XNSSInitializer >
123 xNSSInitializer = css::xml::crypto::NSSInitializer::create( m_xContext );
125 uno::Reference< css::xml::crypto::XDigestContext > xDigestContext(
126 xNSSInitializer->getDigestContext( css::xml::crypto::DigestID::SHA256,
127 uno::Sequence< beans::NamedValue >() ),
128 uno::UNO_SET_THROW );
129 #endif
131 // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
132 ucbhelper::InternetProxyDecider aProxyDecider( m_xContext );
133 INetURLObject aBindingUrl( m_aURL.getBindingUrl( ) );
134 const ucbhelper::InternetProxyServer& rProxy = aProxyDecider.getProxy(
135 INetURLObject::GetScheme( aBindingUrl.GetProtocol( ) ), aBindingUrl.GetHost(), aBindingUrl.GetPort() );
136 OUString sProxy = rProxy.aName;
137 if ( rProxy.nPort > 0 )
138 sProxy += ":" + OUString::number( rProxy.nPort );
139 libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy ), std::string(), std::string(), std::string() );
141 if ( m_aRepositories.empty() )
143 // Set the SSL Validation handler
144 libcmis::CertValidationHandlerPtr certHandler(
145 new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
146 libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
148 // Get the auth credentials
149 AuthProvider authProvider( xEnv, m_xIdentifier->getContentIdentifier( ), m_aURL.getBindingUrl( ) );
150 AuthProvider::setXEnv( xEnv );
152 std::string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
153 std::string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
155 bool bIsDone = false;
157 while( !bIsDone )
159 if ( authProvider.authenticationQuery( rUsername, rPassword ) )
163 // Create a session to get repositories
164 libcmis::OAuth2DataPtr oauth2Data;
165 if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
167 libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthProvider::gdriveAuthCodeFallback );
168 oauth2Data.reset( new libcmis::OAuth2Data(
169 GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
170 GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
171 GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
173 if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
174 oauth2Data.reset( new libcmis::OAuth2Data(
175 ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
176 ALFRESCO_CLOUD_SCOPE, ALFRESCO_CLOUD_REDIRECT_URI,
177 ALFRESCO_CLOUD_CLIENT_ID, ALFRESCO_CLOUD_CLIENT_SECRET ) );
178 if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL )
180 libcmis::SessionFactory::setOAuth2AuthCodeProvider( AuthProvider::onedriveAuthCodeFallback );
181 oauth2Data.reset( new libcmis::OAuth2Data(
182 ONEDRIVE_AUTH_URL, ONEDRIVE_TOKEN_URL,
183 ONEDRIVE_SCOPE, ONEDRIVE_REDIRECT_URI,
184 ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET ) );
187 std::unique_ptr<libcmis::Session> session(libcmis::SessionFactory::createSession(
188 OUSTR_TO_STDSTR( m_aURL.getBindingUrl( ) ),
189 rUsername, rPassword, "", false, oauth2Data ));
190 if (!session)
191 ucbhelper::cancelCommandExecution(
192 ucb::IOErrorCode_INVALID_DEVICE,
193 uno::Sequence< uno::Any >( 0 ),
194 xEnv );
195 m_aRepositories = session->getRepositories( );
197 bIsDone = true;
199 catch ( const libcmis::Exception& e )
201 SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e.what() );
203 if ( e.getType() != "permissionDenied" )
205 ucbhelper::cancelCommandExecution(
206 ucb::IOErrorCode_INVALID_DEVICE,
207 uno::Sequence< uno::Any >( 0 ),
208 xEnv );
212 else
214 // Throw user cancelled exception
215 ucbhelper::cancelCommandExecution(
216 ucb::IOErrorCode_ABORT,
217 uno::Sequence< uno::Any >( 0 ),
218 xEnv,
219 "Authentication cancelled" );
225 libcmis::RepositoryPtr RepoContent::getRepository( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
227 // Ensure we have the repositories extracted
228 getRepositories( xEnv );
230 libcmis::RepositoryPtr repo;
232 if ( !m_sRepositoryId.isEmpty() )
234 auto it = std::find_if(m_aRepositories.begin(), m_aRepositories.end(),
235 [&](const libcmis::RepositoryPtr& rRepo) { return STD_TO_OUSTR(rRepo->getId()) == m_sRepositoryId; });
236 if (it != m_aRepositories.end())
237 repo = *it;
239 else
240 repo = m_aRepositories.front( );
241 return repo;
244 uno::Sequence< beans::Property > RepoContent::getProperties(
245 const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
247 static const beans::Property aGenericProperties[] =
249 beans::Property( "IsDocument",
250 -1, cppu::UnoType<bool>::get(),
251 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
252 beans::Property( "IsFolder",
253 -1, cppu::UnoType<bool>::get(),
254 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
255 beans::Property( "Title",
256 -1, cppu::UnoType<OUString>::get(),
257 beans::PropertyAttribute::BOUND ),
258 beans::Property( "IsReadOnly",
259 -1, cppu::UnoType<bool>::get(),
260 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
263 const int nProps = SAL_N_ELEMENTS(aGenericProperties);
264 return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
267 uno::Sequence< ucb::CommandInfo > RepoContent::getCommands(
268 const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
270 static const ucb::CommandInfo aCommandInfoTable[] =
272 // Required commands
273 ucb::CommandInfo
274 ( "getCommandInfo",
275 -1, cppu::UnoType<void>::get() ),
276 ucb::CommandInfo
277 ( "getPropertySetInfo",
278 -1, cppu::UnoType<void>::get() ),
279 ucb::CommandInfo
280 ( "getPropertyValues",
281 -1, cppu::UnoType<uno::Sequence< beans::Property >>::get() ),
282 ucb::CommandInfo
283 ( "setPropertyValues",
284 -1, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() ),
286 // Optional standard commands
287 ucb::CommandInfo
288 ( "open",
289 -1, cppu::UnoType<ucb::OpenCommandArgument2>::get() ),
292 const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
293 return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, nProps );
296 OUString RepoContent::getParentURL( )
298 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
300 // TODO Implement me
302 return OUString();
305 XTYPEPROVIDER_COMMON_IMPL( RepoContent );
307 OUString SAL_CALL RepoContent::getImplementationName()
309 return "com.sun.star.comp.CmisRepoContent";
312 uno::Sequence< OUString > SAL_CALL RepoContent::getSupportedServiceNames()
314 uno::Sequence<OUString> aSNS { "com.sun.star.ucb.Content" };
315 return aSNS;
318 OUString SAL_CALL RepoContent::getContentType()
320 return CMIS_REPO_TYPE;
323 uno::Any SAL_CALL RepoContent::execute(
324 const ucb::Command& aCommand,
325 sal_Int32 /*CommandId*/,
326 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
328 SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand.Name );
330 uno::Any aRet;
332 if ( aCommand.Name == "getPropertyValues" )
334 uno::Sequence< beans::Property > Properties;
335 if ( !( aCommand.Argument >>= Properties ) )
336 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
337 aRet <<= getPropertyValues( Properties, xEnv );
339 else if ( aCommand.Name == "getPropertySetInfo" )
340 aRet <<= getPropertySetInfo( xEnv, false );
341 else if ( aCommand.Name == "getCommandInfo" )
342 aRet <<= getCommandInfo( xEnv, false );
343 else if ( aCommand.Name == "open" )
345 ucb::OpenCommandArgument2 aOpenCommand;
346 if ( !( aCommand.Argument >>= aOpenCommand ) )
347 ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
348 const ucb::OpenCommandArgument2& rOpenCommand = aOpenCommand;
350 getRepositories( xEnv );
351 uno::Reference< ucb::XDynamicResultSet > xSet
352 = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
353 aRet <<= xSet;
355 else
357 SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
360 return aRet;
363 void SAL_CALL RepoContent::abort( sal_Int32 /*CommandId*/ )
365 SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
366 // TODO Implement me
369 uno::Sequence< uno::Type > SAL_CALL RepoContent::getTypes()
371 static cppu::OTypeCollection s_aFolderCollection
372 (CPPU_TYPE_REF( lang::XTypeProvider ),
373 CPPU_TYPE_REF( lang::XServiceInfo ),
374 CPPU_TYPE_REF( lang::XComponent ),
375 CPPU_TYPE_REF( ucb::XContent ),
376 CPPU_TYPE_REF( ucb::XCommandProcessor ),
377 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
378 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
379 CPPU_TYPE_REF( beans::XPropertyContainer ),
380 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
381 CPPU_TYPE_REF( container::XChild ) );
382 return s_aFolderCollection.getTypes();
385 std::vector< uno::Reference< ucb::XContent > > RepoContent::getChildren( )
387 std::vector< uno::Reference< ucb::XContent > > result;
389 // TODO Cache the results somehow
390 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
392 if ( m_sRepositoryId.isEmpty( ) )
394 for ( const auto& rRepo : m_aRepositories )
396 URL aUrl( m_aURL );
397 aUrl.setObjectPath( STD_TO_OUSTR( rRepo->getId( ) ) );
399 uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( aUrl.asString( ) );
400 uno::Reference< ucb::XContent > xContent = new RepoContent( m_xContext, m_pProvider, xId, m_aRepositories );
402 result.push_back( xContent );
405 else
407 // Return the repository root as child
408 OUString sUrl;
409 OUString sEncodedBinding = rtl::Uri::encode(
410 m_aURL.getBindingUrl( ) + "#" + m_sRepositoryId,
411 rtl_UriCharClassRelSegment,
412 rtl_UriEncodeKeepEscapes,
413 RTL_TEXTENCODING_UTF8 );
414 sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
416 uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( sUrl );
417 uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId );
419 result.push_back( xContent );
421 return result;
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */