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 <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
21 
22 #include <config_oauth2.h>
23 #include <rtl/uri.hxx>
24 #include <sal/log.hxx>
25 #include <tools/urlobj.hxx>
30 #include <ucbhelper/macros.hxx>
31 
32 #include "auth_provider.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>
39 
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 )
42 
43 using namespace com::sun::star;
44 
45 namespace cmis
46 {
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 )
55  {
56  // Split the URL into bits
57  OUString sURL = m_xIdentifier->getContentIdentifier( );
58  SAL_INFO( "ucb.ucp.cmis", "RepoContent::RepoContent() " << sURL );
59 
61  if (!m_sRepositoryId.isEmpty() && m_sRepositoryId[0] == '/')
63  }
64 
66  {
67  }
68 
70  {
71  return uno::makeAny( lang::IllegalArgumentException(
72  "Wrong argument type!",
73  static_cast< cppu::OWeakObject * >( this ), -1) );
74  }
75 
76  uno::Reference< sdbc::XRow > RepoContent::getPropertyValues(
77  const uno::Sequence< beans::Property >& rProperties,
78  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
79  {
80  rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
81 
82  for( const beans::Property& rProp : rProperties )
83  {
84  try
85  {
86  if ( rProp.Name == "IsDocument" )
87  {
88  xRow->appendBoolean( rProp, false );
89  }
90  else if ( rProp.Name == "IsFolder" )
91  {
92  xRow->appendBoolean( rProp, true );
93  }
94  else if ( rProp.Name == "Title" )
95  {
96  xRow->appendString( rProp, STD_TO_OUSTR( getRepository( xEnv )->getName( ) ) );
97  }
98  else if ( rProp.Name == "IsReadOnly" )
99  {
100  xRow->appendBoolean( rProp, true );
101  }
102  else
103  {
104  xRow->appendVoid( rProp );
105  SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
106  }
107  }
108  catch (const libcmis::Exception&)
109  {
110  xRow->appendVoid( rProp );
111  }
112  }
113 
114  return uno::Reference< sdbc::XRow >( xRow.get() );
115  }
116 
117  void RepoContent::getRepositories( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
118  {
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 );
124 
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
130 
131  // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
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() );
140 
141  if ( m_aRepositories.empty() )
142  {
143  // Set the SSL Validation handler
144  libcmis::CertValidationHandlerPtr certHandler(
145  new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
146  libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
147 
148  // Get the auth credentials
149  AuthProvider authProvider( xEnv, m_xIdentifier->getContentIdentifier( ), m_aURL.getBindingUrl( ) );
150  AuthProvider::setXEnv( xEnv );
151 
152  std::string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
153  std::string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
154 
155  bool bIsDone = false;
156 
157  while( !bIsDone )
158  {
159  if ( authProvider.authenticationQuery( rUsername, rPassword ) )
160  {
161  try
162  {
163  // Create a session to get repositories
164  libcmis::OAuth2DataPtr oauth2Data;
165  if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
166  {
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 ) );
172  }
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 )
179  {
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 ) );
185  }
186 
187  std::unique_ptr<libcmis::Session> session(libcmis::SessionFactory::createSession(
189  rUsername, rPassword, "", false, oauth2Data ));
190  if (!session)
192  ucb::IOErrorCode_INVALID_DEVICE,
193  uno::Sequence< uno::Any >( 0 ),
194  xEnv );
195  m_aRepositories = session->getRepositories( );
196 
197  bIsDone = true;
198  }
199  catch ( const libcmis::Exception& e )
200  {
201  SAL_INFO( "ucb.ucp.cmis", "Error getting repositories: " << e.what() );
202 
203  if ( e.getType() != "permissionDenied" )
204  {
206  ucb::IOErrorCode_INVALID_DEVICE,
207  uno::Sequence< uno::Any >( 0 ),
208  xEnv );
209  }
210  }
211  }
212  else
213  {
214  // Throw user cancelled exception
216  ucb::IOErrorCode_ABORT,
217  uno::Sequence< uno::Any >( 0 ),
218  xEnv,
219  "Authentication cancelled" );
220  }
221  }
222  }
223  }
224 
225  libcmis::RepositoryPtr RepoContent::getRepository( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
226  {
227  // Ensure we have the repositories extracted
228  getRepositories( xEnv );
229 
230  libcmis::RepositoryPtr repo;
231 
232  if ( !m_sRepositoryId.isEmpty() )
233  {
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;
238  }
239  else
240  repo = m_aRepositories.front( );
241  return repo;
242  }
243 
244  uno::Sequence< beans::Property > RepoContent::getProperties(
245  const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
246  {
247  static const beans::Property aGenericProperties[] =
248  {
249  beans::Property( "IsDocument",
251  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
252  beans::Property( "IsFolder",
254  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
255  beans::Property( "Title",
257  beans::PropertyAttribute::BOUND ),
258  beans::Property( "IsReadOnly",
260  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
261  };
262 
263  const int nProps = SAL_N_ELEMENTS(aGenericProperties);
264  return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
265  }
266 
267  uno::Sequence< ucb::CommandInfo > RepoContent::getCommands(
268  const uno::Reference< ucb::XCommandEnvironment > & /*xEnv*/ )
269  {
270  static const ucb::CommandInfo aCommandInfoTable[] =
271  {
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() ),
285 
286  // Optional standard commands
287  ucb::CommandInfo
288  ( "open",
290  };
291 
292  const int nProps = SAL_N_ELEMENTS(aCommandInfoTable);
293  return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, nProps );
294  }
295 
297  {
298  SAL_INFO( "ucb.ucp.cmis", "RepoContent::getParentURL()" );
299 
300  // TODO Implement me
301 
302  return OUString();
303  }
304 
306 
308  {
309  return "com.sun.star.comp.CmisRepoContent";
310  }
311 
312  uno::Sequence< OUString > SAL_CALL RepoContent::getSupportedServiceNames()
313  {
314  uno::Sequence<OUString> aSNS { "com.sun.star.ucb.Content" };
315  return aSNS;
316  }
317 
318  OUString SAL_CALL RepoContent::getContentType()
319  {
320  return CMIS_REPO_TYPE;
321  }
322 
324  const ucb::Command& aCommand,
325  sal_Int32 /*CommandId*/,
326  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
327  {
328  SAL_INFO( "ucb.ucp.cmis", "RepoContent::execute( ) - " << aCommand.Name );
329 
330  uno::Any aRet;
331 
332  if ( aCommand.Name == "getPropertyValues" )
333  {
334  uno::Sequence< beans::Property > Properties;
335  if ( !( aCommand.Argument >>= Properties ) )
337  aRet <<= getPropertyValues( Properties, xEnv );
338  }
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" )
344  {
345  ucb::OpenCommandArgument2 aOpenCommand;
346  if ( !( aCommand.Argument >>= aOpenCommand ) )
348  const ucb::OpenCommandArgument2& rOpenCommand = aOpenCommand;
349 
350  getRepositories( xEnv );
351  uno::Reference< ucb::XDynamicResultSet > xSet
352  = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
353  aRet <<= xSet;
354  }
355  else
356  {
357  SAL_INFO( "ucb.ucp.cmis", "Command not allowed" );
358  }
359 
360  return aRet;
361  }
362 
363  void SAL_CALL RepoContent::abort( sal_Int32 /*CommandId*/ )
364  {
365  SAL_INFO( "ucb.ucp.cmis", "TODO - RepoContent::abort()" );
366  // TODO Implement me
367  }
368 
369  uno::Sequence< uno::Type > SAL_CALL RepoContent::getTypes()
370  {
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();
383  }
384 
385  std::vector< uno::Reference< ucb::XContent > > RepoContent::getChildren( )
386  {
387  std::vector< uno::Reference< ucb::XContent > > result;
388 
389  // TODO Cache the results somehow
390  SAL_INFO( "ucb.ucp.cmis", "RepoContent::getChildren" );
391 
392  if ( m_sRepositoryId.isEmpty( ) )
393  {
394  for ( const auto& rRepo : m_aRepositories )
395  {
396  URL aUrl( m_aURL );
397  aUrl.setObjectPath( STD_TO_OUSTR( rRepo->getId( ) ) );
398 
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 );
401 
402  result.push_back( xContent );
403  }
404  }
405  else
406  {
407  // Return the repository root as child
408  OUString sUrl;
409  OUString sEncodedBinding = rtl::Uri::encode(
411  rtl_UriCharClassRelSegment,
412  rtl_UriEncodeKeepEscapes,
413  RTL_TEXTENCODING_UTF8 );
414  sUrl = "vnd.libreoffice.cmis://" + sEncodedBinding;
415 
416  uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( sUrl );
417  uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId );
418 
419  result.push_back( xContent );
420  }
421  return result;
422  }
423 }
424 
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::uno::XComponentContext > m_xContext
ContentProvider *const m_pProvider
std::vector< libcmis::RepositoryPtr > m_aRepositories
static void setXEnv(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
const OUString & getPassword() const
Definition: cmis_url.hxx:34
XTYPEPROVIDER_COMMON_IMPL(Content)
const OUString & getBindingUrl() const
Definition: cmis_url.hxx:31
#define OUSTR_TO_STDSTR(s)
static char * gdriveAuthCodeFallback(const char *, const char *, const char *)
#define CMIS_REPO_TYPE
libcmis::RepositoryPtr getRepository(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual OUString SAL_CALL getContentType() override
virtual void SAL_CALL abort(sal_Int32 CommandId) override
virtual ~RepoContent() override
css::uno::Any getBadArgExcept()
css::uno::Reference< css::sdbc::XRow > getPropertyValues(const css::uno::Sequence< css::beans::Property > &rProperties, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
#define SAL_N_ELEMENTS(arr)
static char * onedriveAuthCodeFallback(const char *url, const char *, const char *)
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
virtual OUString getParentURL() override
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
css::uno::Reference< css::ucb::XContentIdentifier > m_xIdentifier
MetadataImporterPluginType * result
virtual std::vector< css::uno::Reference< css::ucb::XContent > > getChildren() override
virtual css::uno::Sequence< css::ucb::CommandInfo > getCommands(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv) override
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
static OUString GetScheme(INetProtocol eTheScheme)
OUString asString()
Definition: cmis_url.cxx:58
const OUString & getObjectPath() const
Definition: cmis_url.hxx:29
virtual OUString SAL_CALL getImplementationName() override
virtual css::uno::Sequence< css::beans::Property > getProperties(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv) override
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
const OUString & getUsername() const
Definition: cmis_url.hxx:33
void getRepositories(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
#define CPPU_TYPE_REF(T)
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
#define SAL_INFO(area, stream)
RepoContent(const css::uno::Reference< css::uno::XComponentContext > &rxContext, ContentProvider *pProvider, const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier, std::vector< libcmis::RepositoryPtr > const &aRepos=std::vector< libcmis::RepositoryPtr >())
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
void setObjectPath(const OUString &sPath)
Definition: cmis_url.cxx:43
#define STD_TO_OUSTR(str)