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 
47 using namespace com::sun::star;
48 
49 namespace 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::makeAny( 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",
278  -1, cppu::UnoType<void>::get() ),
279  ucb::CommandInfo
280  ( "getPropertySetInfo",
281  -1, cppu::UnoType<void>::get() ),
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::uno::Reference< css::uno::XComponentContext > m_xContext
Reference< XRow > xRow
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 >())
ContentProvider * 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:33
XTYPEPROVIDER_COMMON_IMPL(Content)
const OUString & getBindingUrl() const
Definition: cmis_url.hxx:30
#define OUSTR_TO_STDSTR(s)
libcmis::RepositoryPtr getRepository(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual OUString SAL_CALL getContentType() override
constexpr OUStringLiteral CMIS_REPO_TYPE
OUString asString() const
Definition: cmis_url.cxx:56
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)
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
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::util::URL m_aURL
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)
const OUString & getObjectPath() const
Definition: cmis_url.hxx:28
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:32
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)
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
void setObjectPath(const OUString &sPath)
Definition: cmis_url.cxx:41
::rtl::Reference< OContentHelper > xContent
Any result
#define STD_TO_OUSTR(str)
static char * copyWebAuthCodeFallback(const char *url, const char *, const char *)
Reference< XContentIdentifier > xId