Line data Source code
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 <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>
18 : #ifndef SYSTEM_CURL
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>
23 : #endif
24 :
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>
33 :
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>
41 :
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 )
44 :
45 : using namespace com::sun::star;
46 : using namespace std;
47 :
48 : namespace cmis
49 : {
50 0 : 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 0 : m_aURL( Identifier->getContentIdentifier( ) ),
57 : m_sRepositoryId( ),
58 0 : m_aRepositories( aRepos )
59 : {
60 : // Split the URL into bits
61 0 : OUString sURL = m_xIdentifier->getContentIdentifier( );
62 : SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL );
63 :
64 0 : m_sRepositoryId = m_aURL.getObjectPath();
65 0 : if (!m_sRepositoryId.isEmpty() && m_sRepositoryId[0] == '/')
66 0 : m_sRepositoryId = m_sRepositoryId.copy(1);
67 0 : }
68 :
69 0 : RepoContent::~RepoContent()
70 : {
71 0 : }
72 :
73 0 : uno::Any RepoContent::getBadArgExcept()
74 : {
75 : return uno::makeAny( lang::IllegalArgumentException(
76 : OUString("Wrong argument type!"),
77 0 : static_cast< cppu::OWeakObject * >( this ), -1) );
78 : }
79 :
80 0 : uno::Reference< sdbc::XRow > RepoContent::getPropertyValues(
81 : const uno::Sequence< beans::Property >& rProperties,
82 : const uno::Reference< ucb::XCommandEnvironment >& xEnv )
83 : {
84 0 : rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
85 :
86 : sal_Int32 nProps;
87 : const beans::Property* pProps;
88 :
89 0 : nProps = rProperties.getLength();
90 0 : pProps = rProperties.getConstArray();
91 :
92 0 : for( sal_Int32 n = 0; n < nProps; ++n )
93 : {
94 0 : const beans::Property& rProp = pProps[ n ];
95 :
96 : try
97 : {
98 0 : if ( rProp.Name == "IsDocument" )
99 : {
100 0 : xRow->appendBoolean( rProp, false );
101 : }
102 0 : else if ( rProp.Name == "IsFolder" )
103 : {
104 0 : xRow->appendBoolean( rProp, true );
105 : }
106 0 : else if ( rProp.Name == "Title" )
107 : {
108 0 : xRow->appendString( rProp, STD_TO_OUSTR( getRepository( xEnv )->getName( ) ) );
109 : }
110 0 : else if ( rProp.Name == "IsReadOnly" )
111 : {
112 0 : xRow->appendBoolean( rProp, true );
113 : }
114 : else
115 : {
116 0 : xRow->appendVoid( rProp );
117 : SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
118 : }
119 : }
120 0 : catch (const libcmis::Exception&)
121 : {
122 0 : xRow->appendVoid( rProp );
123 : }
124 : }
125 :
126 0 : return uno::Reference< sdbc::XRow >( xRow.get() );
127 : }
128 :
129 0 : void RepoContent::getRepositories( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
130 : {
131 : #ifndef SYSTEM_CURL
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 );
136 :
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 );
141 : #endif
142 :
143 : // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
144 0 : ucbhelper::InternetProxyDecider aProxyDecider( m_xContext );
145 0 : INetURLObject aBindingUrl( m_aURL.getBindingUrl( ) );
146 : const ucbhelper::InternetProxyServer& rProxy = aProxyDecider.getProxy(
147 0 : INetURLObject::GetScheme( aBindingUrl.GetProtocol( ) ), aBindingUrl.GetHost(), aBindingUrl.GetPort() );
148 0 : OUString sProxy = rProxy.aName;
149 0 : if ( rProxy.nPort > 0 )
150 0 : sProxy += ":" + OUString::number( rProxy.nPort );
151 0 : libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy ), string(), string(), string() );
152 :
153 0 : if ( m_aRepositories.empty() )
154 : {
155 : // Set the SSL Validation handler
156 : libcmis::CertValidationHandlerPtr certHandler(
157 0 : new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
158 0 : libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
159 :
160 : // Get the auth credentials
161 0 : AuthProvider authProvider( xEnv, m_xIdentifier->getContentIdentifier( ), m_aURL.getBindingUrl( ) );
162 0 : AuthProvider::setXEnv( xEnv );
163 :
164 0 : string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
165 0 : string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
166 0 : if ( authProvider.authenticationQuery( rUsername, rPassword ) )
167 : {
168 : try
169 : {
170 : // Create a session to get repositories
171 0 : libcmis::OAuth2DataPtr oauth2Data;
172 0 : 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 0 : GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
177 0 : 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 0 : ALFRESCO_CLOUD_CLIENT_ID, ALFRESCO_CLOUD_CLIENT_SECRET ) );
182 0 : if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL )
183 : {
184 0 : 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 0 : ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET ) );
189 : }
190 :
191 : boost::scoped_ptr<libcmis::Session> session(libcmis::SessionFactory::createSession(
192 0 : OUSTR_TO_STDSTR( m_aURL.getBindingUrl( ) ),
193 0 : rUsername, rPassword, "", false, oauth2Data ));
194 0 : if (!session)
195 : ucbhelper::cancelCommandExecution(
196 : ucb::IOErrorCode_INVALID_DEVICE,
197 : uno::Sequence< uno::Any >( 0 ),
198 : xEnv,
199 0 : OUString( ) );
200 0 : m_aRepositories = session->getRepositories( );
201 : }
202 0 : catch (const libcmis::Exception& e)
203 : {
204 : SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e.what() );
205 : }
206 : }
207 : else
208 : {
209 : // Throw user cancelled exception
210 : ucbhelper::cancelCommandExecution(
211 : ucb::IOErrorCode_ABORT,
212 : uno::Sequence< uno::Any >( 0 ),
213 : xEnv,
214 0 : OUString( "Authentication cancelled" ) );
215 0 : }
216 0 : }
217 0 : }
218 :
219 0 : libcmis::RepositoryPtr RepoContent::getRepository( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
220 : {
221 : // Ensure we have the repositories extracted
222 0 : getRepositories( xEnv );
223 :
224 0 : libcmis::RepositoryPtr repo;
225 :
226 0 : if ( !m_sRepositoryId.isEmpty() )
227 : {
228 0 : for ( vector< libcmis::RepositoryPtr >::iterator it = m_aRepositories.begin( );
229 0 : it != m_aRepositories.end( ) && NULL == repo.get( ); ++it )
230 : {
231 0 : if ( STD_TO_OUSTR( ( *it )->getId( ) ) == m_sRepositoryId )
232 0 : repo = *it;
233 : }
234 : }
235 : else
236 0 : repo = m_aRepositories.front( );
237 0 : return repo;
238 : }
239 :
240 0 : uno::Sequence< beans::Property > RepoContent::getProperties(
241 : const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
242 : {
243 : static const beans::Property aGenericProperties[] =
244 : {
245 : beans::Property( OUString( "IsDocument" ),
246 0 : -1, cppu::UnoType<bool>::get(),
247 : beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
248 : beans::Property( OUString( "IsFolder" ),
249 0 : -1, cppu::UnoType<bool>::get(),
250 : beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
251 : beans::Property( OUString( "Title" ),
252 0 : -1, cppu::UnoType<OUString>::get(),
253 : beans::PropertyAttribute::BOUND ),
254 : beans::Property( OUString( "IsReadOnly" ),
255 0 : -1, cppu::UnoType<bool>::get(),
256 : beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
257 0 : };
258 :
259 0 : const int nProps = SAL_N_ELEMENTS(aGenericProperties);
260 0 : return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
261 : }
262 :
263 0 : uno::Sequence< ucb::CommandInfo > RepoContent::getCommands(
264 : const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
265 : {
266 : static const ucb::CommandInfo aCommandInfoTable[] =
267 : {
268 : // Required commands
269 : ucb::CommandInfo
270 : ( OUString( "getCommandInfo" ),
271 0 : -1, cppu::UnoType<void>::get() ),
272 : ucb::CommandInfo
273 : ( OUString( "getPropertySetInfo" ),
274 0 : -1, cppu::UnoType<void>::get() ),
275 : ucb::CommandInfo
276 : ( OUString( "getPropertyValues" ),
277 0 : -1, cppu::UnoType<uno::Sequence< beans::Property >>::get() ),
278 : ucb::CommandInfo
279 : ( OUString( "setPropertyValues" ),
280 0 : -1, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() ),
281 :
282 : // Optional standard commands
283 : ucb::CommandInfo
284 : ( OUString( "open" ),
285 0 : -1, cppu::UnoType<ucb::OpenCommandArgument2>::get() ),
286 0 : };
287 :
288 0 : const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
289 0 : return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, nProps );
290 : }
291 :
292 0 : OUString RepoContent::getParentURL( )
293 : {
294 0 : OUString sRet;
295 :
296 : SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
297 :
298 : // TODO Implement me
299 :
300 0 : return sRet;
301 : }
302 :
303 0 : XTYPEPROVIDER_COMMON_IMPL( RepoContent );
304 :
305 0 : void SAL_CALL RepoContent::acquire() throw()
306 : {
307 0 : ContentImplHelper::acquire();
308 0 : }
309 :
310 0 : void SAL_CALL RepoContent::release() throw()
311 : {
312 0 : ContentImplHelper::release();
313 0 : }
314 :
315 0 : uno::Any SAL_CALL RepoContent::queryInterface( const uno::Type & rType ) throw ( uno::RuntimeException, std::exception )
316 : {
317 0 : return ContentImplHelper::queryInterface(rType);
318 : }
319 :
320 0 : OUString SAL_CALL RepoContent::getImplementationName() throw( uno::RuntimeException, std::exception )
321 : {
322 0 : return OUString("com.sun.star.comp.CmisRepoContent");
323 : }
324 :
325 0 : uno::Sequence< OUString > SAL_CALL RepoContent::getSupportedServiceNames()
326 : throw( uno::RuntimeException, std::exception )
327 : {
328 0 : uno::Sequence< OUString > aSNS( 1 );
329 0 : aSNS.getArray()[ 0 ] = "com.sun.star.ucb.Content";
330 0 : return aSNS;
331 : }
332 :
333 0 : OUString SAL_CALL RepoContent::getContentType() throw( uno::RuntimeException, std::exception )
334 : {
335 0 : return OUString( CMIS_REPO_TYPE );
336 : }
337 :
338 0 : 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 )
343 : {
344 : SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand.Name );
345 :
346 0 : uno::Any aRet;
347 :
348 0 : if ( aCommand.Name == "getPropertyValues" )
349 : {
350 0 : uno::Sequence< beans::Property > Properties;
351 0 : if ( !( aCommand.Argument >>= Properties ) )
352 0 : ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
353 0 : aRet <<= getPropertyValues( Properties, xEnv );
354 : }
355 0 : else if ( aCommand.Name == "getPropertySetInfo" )
356 0 : aRet <<= getPropertySetInfo( xEnv, false );
357 0 : else if ( aCommand.Name == "getCommandInfo" )
358 0 : aRet <<= getCommandInfo( xEnv, false );
359 0 : else if ( aCommand.Name == "open" )
360 : {
361 0 : ucb::OpenCommandArgument2 aOpenCommand;
362 0 : if ( !( aCommand.Argument >>= aOpenCommand ) )
363 0 : ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
364 0 : const ucb::OpenCommandArgument2& rOpenCommand = aOpenCommand;
365 :
366 0 : getRepositories( xEnv );
367 : uno::Reference< ucb::XDynamicResultSet > xSet
368 0 : = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
369 0 : aRet <<= xSet;
370 : }
371 : else
372 : {
373 : SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
374 : }
375 :
376 0 : return aRet;
377 : }
378 :
379 0 : void SAL_CALL RepoContent::abort( sal_Int32 /*CommandId*/ ) throw( uno::RuntimeException, std::exception )
380 : {
381 : SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
382 : // TODO Implement me
383 0 : }
384 :
385 0 : uno::Sequence< uno::Type > SAL_CALL RepoContent::getTypes() throw( uno::RuntimeException, std::exception )
386 : {
387 : static cppu::OTypeCollection aFolderCollection
388 0 : (CPPU_TYPE_REF( lang::XTypeProvider ),
389 0 : CPPU_TYPE_REF( lang::XServiceInfo ),
390 0 : CPPU_TYPE_REF( lang::XComponent ),
391 0 : CPPU_TYPE_REF( ucb::XContent ),
392 0 : CPPU_TYPE_REF( ucb::XCommandProcessor ),
393 0 : CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
394 0 : CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
395 0 : CPPU_TYPE_REF( beans::XPropertyContainer ),
396 0 : CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
397 0 : CPPU_TYPE_REF( container::XChild ) );
398 0 : return aFolderCollection.getTypes();
399 : }
400 :
401 0 : list< uno::Reference< ucb::XContent > > RepoContent::getChildren( )
402 : {
403 0 : list< uno::Reference< ucb::XContent > > result;
404 :
405 : // TODO Cache the results somehow
406 : SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
407 :
408 0 : if ( m_sRepositoryId.isEmpty( ) )
409 : {
410 0 : for ( vector< libcmis::RepositoryPtr >::iterator it = m_aRepositories.begin( );
411 0 : it != m_aRepositories.end(); ++it )
412 : {
413 0 : URL aUrl( m_aURL );
414 0 : aUrl.setObjectPath( STD_TO_OUSTR( ( *it )->getId( ) ) );
415 :
416 0 : uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( aUrl.asString( ) );
417 0 : uno::Reference< ucb::XContent > xContent = new RepoContent( m_xContext, m_pProvider, xId, m_aRepositories );
418 :
419 0 : result.push_back( xContent );
420 0 : }
421 : }
422 : else
423 : {
424 : // Return the repository root as child
425 0 : OUString sUrl;
426 : OUString sEncodedBinding = rtl::Uri::encode(
427 0 : m_aURL.getBindingUrl( ) + "#" + m_sRepositoryId,
428 : rtl_UriCharClassRelSegment,
429 : rtl_UriEncodeKeepEscapes,
430 0 : RTL_TEXTENCODING_UTF8 );
431 0 : sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
432 :
433 0 : uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( sUrl );
434 0 : uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId );
435 :
436 0 : result.push_back( xContent );
437 : }
438 0 : return result;
439 : }
440 0 : }
441 :
442 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|