LibreOffice Module ucb (master) 1
cmis_repo_content.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
10#include <sal/config.h>
11
12#include <boost/make_shared.hpp>
13
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>
20#ifndef SYSTEM_CURL
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>
24#endif
25
26#include <config_oauth2.h>
27#include <rtl/uri.hxx>
28#include <sal/log.hxx>
29#include <tools/urlobj.hxx>
34#include <ucbhelper/macros.hxx>
35
36#include "auth_provider.hxx"
38#include "cmis_content.hxx"
39#include "cmis_provider.hxx"
40#include "cmis_repo_content.hxx"
41#include "cmis_resultset.hxx"
42#include <memory>
43
44#define OUSTR_TO_STDSTR(s) std::string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() )
45#define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
46
47using namespace com::sun::star;
48
49namespace cmis
50{
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) )
58 {
59 // Split the URL into bits
60 OUString sURL = m_xIdentifier->getContentIdentifier( );
61 SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL );
62
64 if (!m_sRepositoryId.isEmpty() && m_sRepositoryId[0] == '/')
66 }
67
69 {
70 }
71
73 {
74 return uno::Any( lang::IllegalArgumentException(
75 "Wrong argument type!",
76 static_cast< cppu::OWeakObject * >( this ), -1) );
77 }
78
79 uno::Reference< sdbc::XRow > RepoContent::getPropertyValues(
80 const uno::Sequence< beans::Property >& rProperties,
81 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
82 {
83 rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
84
85 for( const beans::Property& rProp : rProperties )
86 {
87 try
88 {
89 if ( rProp.Name == "IsDocument" )
90 {
91 xRow->appendBoolean( rProp, false );
92 }
93 else if ( rProp.Name == "IsFolder" )
94 {
95 xRow->appendBoolean( rProp, true );
96 }
97 else if ( rProp.Name == "Title" )
98 {
99 xRow->appendString( rProp, STD_TO_OUSTR( getRepository( xEnv )->getName( ) ) );
100 }
101 else if ( rProp.Name == "IsReadOnly" )
102 {
103 xRow->appendBoolean( rProp, true );
104 }
105 else
106 {
107 xRow->appendVoid( rProp );
108 SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
109 }
110 }
111 catch (const libcmis::Exception&)
112 {
113 xRow->appendVoid( rProp );
114 }
115 }
116
117 return xRow;
118 }
119
120 void RepoContent::getRepositories( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
121 {
122#ifndef SYSTEM_CURL
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 );
127
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 );
132#endif
133
134 // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
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() );
143
144 if ( !m_aRepositories.empty() )
145 return;
146
147 // Set the SSL Validation handler
148 libcmis::CertValidationHandlerPtr certHandler(
149 new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
150 libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
151
152 // Get the auth credentials
153 AuthProvider authProvider( xEnv, m_xIdentifier->getContentIdentifier( ), m_aURL.getBindingUrl( ) );
154 AuthProvider::setXEnv( xEnv );
155
156 std::string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
157 std::string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
158
159 bool bIsDone = false;
160
161 while( !bIsDone )
162 {
163 if ( authProvider.authenticationQuery( rUsername, rPassword ) )
164 {
165 try
166 {
167 // Create a session to get repositories
168 libcmis::OAuth2DataPtr oauth2Data;
169 if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
170 {
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 );
176 }
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 )
183 {
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 );
189 }
190
191 std::unique_ptr<libcmis::Session> session(libcmis::SessionFactory::createSession(
193 rUsername, rPassword, "", false, oauth2Data ));
194 if (!session)
196 ucb::IOErrorCode_INVALID_DEVICE,
197 uno::Sequence< uno::Any >( 0 ),
198 xEnv );
199 m_aRepositories = session->getRepositories( );
200
201 bIsDone = true;
202 }
203 catch ( const libcmis::Exception& e )
204 {
205 SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e.what() );
206
207 if ( e.getType() != "permissionDenied" )
208 {
210 ucb::IOErrorCode_INVALID_DEVICE,
211 uno::Sequence< uno::Any >( 0 ),
212 xEnv );
213 }
214 }
215 }
216 else
217 {
218 // Throw user cancelled exception
220 ucb::IOErrorCode_ABORT,
221 uno::Sequence< uno::Any >( 0 ),
222 xEnv,
223 "Authentication cancelled" );
224 }
225 }
226 }
227
228 libcmis::RepositoryPtr RepoContent::getRepository( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
229 {
230 // Ensure we have the repositories extracted
231 getRepositories( xEnv );
232
233 libcmis::RepositoryPtr repo;
234
235 if ( !m_sRepositoryId.isEmpty() )
236 {
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())
240 repo = *it;
241 }
242 else
243 repo = m_aRepositories.front( );
244 return repo;
245 }
246
247 uno::Sequence< beans::Property > RepoContent::getProperties(
248 const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
249 {
250 static const beans::Property aGenericProperties[] =
251 {
252 beans::Property( "IsDocument",
254 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
255 beans::Property( "IsFolder",
257 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
258 beans::Property( "Title",
260 beans::PropertyAttribute::BOUND ),
261 beans::Property( "IsReadOnly",
263 beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
264 };
265
266 const int nProps = SAL_N_ELEMENTS(aGenericProperties);
267 return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
268 }
269
270 uno::Sequence< ucb::CommandInfo > RepoContent::getCommands(
271 const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
272 {
273 static const ucb::CommandInfo aCommandInfoTable[] =
274 {
275 // Required commands
276 ucb::CommandInfo
277 ( "getCommandInfo",
279 ucb::CommandInfo
280 ( "getPropertySetInfo",
282 ucb::CommandInfo
283 ( "getPropertyValues",
284 -1, cppu::UnoType<uno::Sequence< beans::Property >>::get() ),
285 ucb::CommandInfo
286 ( "setPropertyValues",
287 -1, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() ),
288
289 // Optional standard commands
290 ucb::CommandInfo
291 ( "open",
293 };
294
295 const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
296 return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, nProps );
297 }
298
300 {
301 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
302
303 // TODO Implement me
304
305 return OUString();
306 }
307
309
311 {
312 return "com.sun.star.comp.CmisRepoContent";
313 }
314
315 uno::Sequence< OUString > SAL_CALL RepoContent::getSupportedServiceNames()
316 {
317 return { "com.sun.star.ucb.Content" };
318 }
319
320 OUString SAL_CALL RepoContent::getContentType()
321 {
322 return CMIS_REPO_TYPE;
323 }
324
326 const ucb::Command& aCommand,
327 sal_Int32 /*CommandId*/,
328 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
329 {
330 SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand.Name );
331
332 uno::Any aRet;
333
334 if ( aCommand.Name == "getPropertyValues" )
335 {
336 uno::Sequence< beans::Property > Properties;
337 if ( !( aCommand.Argument >>= Properties ) )
339 aRet <<= getPropertyValues( Properties, xEnv );
340 }
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" )
346 {
347 ucb::OpenCommandArgument2 aOpenCommand;
348 if ( !( aCommand.Argument >>= aOpenCommand ) )
350 const ucb::OpenCommandArgument2& rOpenCommand = aOpenCommand;
351
352 getRepositories( xEnv );
353 uno::Reference< ucb::XDynamicResultSet > xSet
354 = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
355 aRet <<= xSet;
356 }
357 else
358 {
359 SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
360 }
361
362 return aRet;
363 }
364
365 void SAL_CALL RepoContent::abort( sal_Int32 /*CommandId*/ )
366 {
367 SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
368 // TODO Implement me
369 }
370
371 uno::Sequence< uno::Type > SAL_CALL RepoContent::getTypes()
372 {
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();
385 }
386
387 std::vector< uno::Reference< ucb::XContent > > RepoContent::getChildren( )
388 {
389 std::vector< uno::Reference< ucb::XContent > > result;
390
391 // TODO Cache the results somehow
392 SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
393
394 if ( m_sRepositoryId.isEmpty( ) )
395 {
396 for ( const auto& rRepo : m_aRepositories )
397 {
398 URL aUrl( m_aURL );
399 aUrl.setObjectPath( STD_TO_OUSTR( rRepo->getId( ) ) );
400
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) );
403
404 result.push_back( xContent );
405 }
406 }
407 else
408 {
409 // Return the repository root as child
410 OUString sUrl;
411 OUString sEncodedBinding = rtl::Uri::encode(
413 rtl_UriCharClassRelSegment,
414 rtl_UriEncodeKeepEscapes,
415 RTL_TEXTENCODING_UTF8 );
416 sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
417
418 uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( sUrl );
419 uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId );
420
421 result.push_back( xContent );
422 }
423 return result;
424 }
425}
426
427/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::util::URL m_aURL
sal_uInt32 GetPort() const
static OUString GetScheme(INetProtocol eTheScheme)
OUString GetHost(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
INetProtocol GetProtocol() const
bool authenticationQuery(std::string &username, std::string &password) override
static void setXEnv(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
static char * copyWebAuthCodeFallback(const char *url, const char *, const char *)
virtual css::uno::Sequence< css::beans::Property > getProperties(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv) override
virtual std::vector< css::uno::Reference< css::ucb::XContent > > getChildren() override
css::uno::Reference< css::sdbc::XRow > getPropertyValues(const css::uno::Sequence< css::beans::Property > &rProperties, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
RepoContent(const css::uno::Reference< css::uno::XComponentContext > &rxContext, ContentProvider *pProvider, const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier, std::vector< libcmis::RepositoryPtr > &&aRepos=std::vector< libcmis::RepositoryPtr >())
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
virtual OUString SAL_CALL getImplementationName() override
ContentProvider * m_pProvider
virtual css::uno::Sequence< css::ucb::CommandInfo > getCommands(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv) override
std::vector< libcmis::RepositoryPtr > m_aRepositories
void getRepositories(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
css::uno::Any getBadArgExcept()
virtual void SAL_CALL abort(sal_Int32 CommandId) override
virtual ~RepoContent() override
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
libcmis::RepositoryPtr getRepository(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual OUString getParentURL() override
virtual OUString SAL_CALL getContentType() override
const OUString & getPassword() const
Definition: cmis_url.hxx:33
const OUString & getUsername() const
Definition: cmis_url.hxx:32
void setObjectPath(const OUString &sPath)
Definition: cmis_url.cxx:41
const OUString & getObjectPath() const
Definition: cmis_url.hxx:28
const OUString & getBindingUrl() const
Definition: cmis_url.hxx:30
OUString asString() const
Definition: cmis_url.cxx:56
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
css::uno::Reference< css::ucb::XContentIdentifier > m_xIdentifier
css::uno::Reference< css::uno::XComponentContext > m_xContext
InternetProxyServer getProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
#define OUSTR_TO_STDSTR(s)
#define STD_TO_OUSTR(str)
#define SAL_INFO(area, stream)
#define SAL_N_ELEMENTS(arr)
#define CPPU_TYPE_REF(T)
::rtl::Reference< OContentHelper > xContent
Reference< XRow > xRow
Reference< XContentIdentifier > xId
XTYPEPROVIDER_COMMON_IMPL(Content)
constexpr OUStringLiteral CMIS_REPO_TYPE
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
OUString aCommand
Any result