LibreOffice Module ucb (master)  1
cmis_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 <cstdio>
11 #include <string_view>
12 
13 #include <com/sun/star/beans/IllegalTypeException.hpp>
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/document/CmisProperty.hpp>
18 #include <com/sun/star/io/XActiveDataSink.hpp>
19 #include <com/sun/star/io/XActiveDataStreamer.hpp>
20 #include <com/sun/star/lang/IllegalAccessException.hpp>
21 #include <com/sun/star/lang/IllegalArgumentException.hpp>
22 #include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
23 #include <com/sun/star/task/InteractionClassification.hpp>
24 #include <com/sun/star/ucb/ContentInfo.hpp>
25 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
26 #include <com/sun/star/ucb/InsertCommandArgument2.hpp>
27 #include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
28 #include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp>
29 #include <com/sun/star/ucb/MissingInputStreamException.hpp>
30 #include <com/sun/star/ucb/OpenMode.hpp>
31 #include <com/sun/star/ucb/UnsupportedCommandException.hpp>
32 #include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
33 #include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
34 #include <com/sun/star/ucb/XCommandInfo.hpp>
35 #include <com/sun/star/ucb/XDynamicResultSet.hpp>
36 #ifndef SYSTEM_CURL
37 #include <com/sun/star/xml/crypto/XDigestContext.hpp>
38 #include <com/sun/star/xml/crypto/DigestID.hpp>
39 #include <com/sun/star/xml/crypto/NSSInitializer.hpp>
40 #endif
41 
43 #include <comphelper/sequence.hxx>
44 #include <cppuhelper/exc_hlp.hxx>
46 #include <config_oauth2.h>
48 #include <sal/log.hxx>
49 #include <tools/urlobj.hxx>
50 #include <tools/long.hxx>
52 #include <ucbhelper/content.hxx>
56 #include <ucbhelper/macros.hxx>
57 #include <sax/tools/converter.hxx>
58 
59 #include "auth_provider.hxx"
61 #include "cmis_content.hxx"
62 #include "cmis_provider.hxx"
63 #include "cmis_resultset.hxx"
64 #include "cmis_strings.hxx"
65 #include "std_inputstream.hxx"
66 #include "std_outputstream.hxx"
67 
68 #define OUSTR_TO_STDSTR(s) string( OUStringToOString( s, RTL_TEXTENCODING_UTF8 ).getStr() )
69 #define STD_TO_OUSTR( str ) OUString( str.c_str(), str.length( ), RTL_TEXTENCODING_UTF8 )
70 
71 using namespace com::sun::star;
72 using namespace std;
73 
74 namespace
75 {
76  util::DateTime lcl_boostToUnoTime(const boost::posix_time::ptime& boostTime)
77  {
78  util::DateTime unoTime;
79  unoTime.Year = boostTime.date().year();
80  unoTime.Month = boostTime.date().month();
81  unoTime.Day = boostTime.date().day();
82  unoTime.Hours = boostTime.time_of_day().hours();
83  unoTime.Minutes = boostTime.time_of_day().minutes();
84  unoTime.Seconds = boostTime.time_of_day().seconds();
85 
86  // TODO FIXME maybe we should compile with BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG
87  // to actually get nanosecond precision in boostTime?
88  // use this way rather than total_nanos to avoid overflows with 32-bit long
89  const tools::Long ticks = boostTime.time_of_day().fractional_seconds();
90  tools::Long nanoSeconds = ticks * ( 1000000000 / boost::posix_time::time_duration::ticks_per_second());
91 
92  unoTime.NanoSeconds = nanoSeconds;
93 
94  return unoTime;
95  }
96 
97  uno::Any lcl_cmisPropertyToUno( const libcmis::PropertyPtr& pProperty )
98  {
99  uno::Any aValue;
100  switch ( pProperty->getPropertyType( )->getType( ) )
101  {
102  default:
103  case libcmis::PropertyType::String:
104  {
105  vector< string > aCmisStrings = pProperty->getStrings( );
106  uno::Sequence< OUString > aStrings( aCmisStrings.size( ) );
107  OUString* aStringsArr = aStrings.getArray( );
108  sal_Int32 i = 0;
109  for ( const auto& rCmisStr : aCmisStrings )
110  {
111  aStringsArr[i++] = STD_TO_OUSTR( rCmisStr );
112  }
113  aValue <<= aStrings;
114  }
115  break;
116  case libcmis::PropertyType::Integer:
117  {
118  vector< long > aCmisLongs = pProperty->getLongs( );
119  uno::Sequence< sal_Int64 > aLongs( aCmisLongs.size( ) );
120  sal_Int64* aLongsArr = aLongs.getArray( );
121  sal_Int32 i = 0;
122  for ( const auto& rCmisLong : aCmisLongs )
123  {
124  aLongsArr[i++] = rCmisLong;
125  }
126  aValue <<= aLongs;
127  }
128  break;
129  case libcmis::PropertyType::Decimal:
130  {
131  vector< double > aCmisDoubles = pProperty->getDoubles( );
132  uno::Sequence< double > aDoubles = comphelper::containerToSequence(aCmisDoubles);
133  aValue <<= aDoubles;
134  }
135  break;
136  case libcmis::PropertyType::Bool:
137  {
138  vector< bool > aCmisBools = pProperty->getBools( );
139  uno::Sequence< sal_Bool > aBools( aCmisBools.size( ) );
140  sal_Bool* aBoolsArr = aBools.getArray( );
141  sal_Int32 i = 0;
142  for ( bool bCmisBool : aCmisBools )
143  {
144  aBoolsArr[i++] = bCmisBool;
145  }
146  aValue <<= aBools;
147  }
148  break;
149  case libcmis::PropertyType::DateTime:
150  {
151  vector< boost::posix_time::ptime > aCmisTimes = pProperty->getDateTimes( );
152  uno::Sequence< util::DateTime > aTimes( aCmisTimes.size( ) );
153  util::DateTime* aTimesArr = aTimes.getArray( );
154  sal_Int32 i = 0;
155  for ( const auto& rCmisTime : aCmisTimes )
156  {
157  aTimesArr[i++] = lcl_boostToUnoTime( rCmisTime );
158  }
159  aValue <<= aTimes;
160  }
161  break;
162  }
163  return aValue;
164  }
165 
166  libcmis::PropertyPtr lcl_unoToCmisProperty(const document::CmisProperty& prop )
167  {
168  libcmis::PropertyTypePtr propertyType( new libcmis::PropertyType( ) );
169 
170  OUString id = prop.Id;
171  OUString name = prop.Name;
172  bool bUpdatable = prop.Updatable;
173  bool bRequired = prop.Required;
174  bool bMultiValued = prop.MultiValued;
175  bool bOpenChoice = prop.OpenChoice;
176  uno::Any value = prop.Value;
177  std::vector< std::string > values;
178 
179  libcmis::PropertyType::Type type = libcmis::PropertyType::String;
180  if ( prop.Type == CMIS_TYPE_STRING )
181  {
182  uno::Sequence< OUString > seqValue;
183  value >>= seqValue;
184  std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(values),
185  [](const OUString& rValue) -> std::string { return OUSTR_TO_STDSTR( rValue ); });
186  type = libcmis::PropertyType::String;
187  }
188  else if ( prop.Type == CMIS_TYPE_BOOL )
189  {
190  uno::Sequence< sal_Bool > seqValue;
191  value >>= seqValue;
192  std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(values),
193  [](const bool nValue) -> std::string { return OUSTR_TO_STDSTR( OUString::boolean( nValue ) ); });
194  type = libcmis::PropertyType::Bool;
195  }
196  else if ( prop.Type == CMIS_TYPE_INTEGER )
197  {
198  uno::Sequence< sal_Int64 > seqValue;
199  value >>= seqValue;
200  std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(values),
201  [](const sal_Int64 nValue) -> std::string { return OUSTR_TO_STDSTR( OUString::number( nValue ) ); });
202  type = libcmis::PropertyType::Integer;
203  }
204  else if ( prop.Type == CMIS_TYPE_DECIMAL )
205  {
206  uno::Sequence< double > seqValue;
207  value >>= seqValue;
208  std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(values),
209  [](const double fValue) -> std::string { return OUSTR_TO_STDSTR( OUString::number( fValue ) ); });
210  type = libcmis::PropertyType::Decimal;
211  }
212  else if ( prop.Type == CMIS_TYPE_DATETIME )
213  {
214  uno::Sequence< util::DateTime > seqValue;
215  value >>= seqValue;
216  std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(values),
217  [](const util::DateTime& rValue) -> std::string {
218  OUStringBuffer aBuffer;
219  ::sax::Converter::convertDateTime( aBuffer, rValue, nullptr );
220  return OUSTR_TO_STDSTR( aBuffer.makeStringAndClear( ) );
221  });
222  type = libcmis::PropertyType::DateTime;
223  }
224 
225  propertyType->setId( OUSTR_TO_STDSTR( id ));
226  propertyType->setDisplayName( OUSTR_TO_STDSTR( name ) );
227  propertyType->setUpdatable( bUpdatable );
228  propertyType->setRequired( bRequired );
229  propertyType->setMultiValued( bMultiValued );
230  propertyType->setOpenChoice( bOpenChoice );
231  propertyType->setType( type );
232 
233  libcmis::PropertyPtr property( new libcmis::Property( propertyType, values ) );
234 
235  return property;
236  }
237 
238  uno::Sequence< uno::Any > generateErrorArguments( const cmis::URL & rURL )
239  {
240  uno::Sequence< uno::Any > aArguments(3);
241 
242  size_t i = 0;
243  aArguments[i++] <<= beans::PropertyValue(
244  "Binding URL",
245  - 1,
246  uno::makeAny( rURL.getBindingUrl() ),
247  beans::PropertyState_DIRECT_VALUE );
248 
249  aArguments[i++] <<= beans::PropertyValue(
250  "Username",
251  -1,
252  uno::makeAny( rURL.getUsername() ),
253  beans::PropertyState_DIRECT_VALUE );
254 
255  aArguments[i++] <<= beans::PropertyValue(
256  "Repository Id",
257  -1,
258  uno::makeAny( rURL.getRepositoryId() ),
259  beans::PropertyState_DIRECT_VALUE );
260 
261  return aArguments;
262  }
263 }
264 
265 namespace cmis
266 {
267  Content::Content( const uno::Reference< uno::XComponentContext >& rxContext,
268  ContentProvider *pProvider, const uno::Reference< ucb::XContentIdentifier >& Identifier,
269  libcmis::ObjectPtr const & pObject )
270  : ContentImplHelper( rxContext, pProvider, Identifier ),
271  m_pProvider( pProvider ),
272  m_pSession( nullptr ),
273  m_pObject( pObject ),
274  m_sURL( Identifier->getContentIdentifier( ) ),
275  m_aURL( Identifier->getContentIdentifier( ) ),
276  m_bTransient( false ),
277  m_bIsFolder( false )
278  {
279  SAL_INFO( "ucb.ucp.cmis", "Content::Content() " << m_sURL );
280 
281  m_sObjectPath = m_aURL.getObjectPath( );
282  m_sObjectId = m_aURL.getObjectId( );
283  }
284 
285  Content::Content( const uno::Reference< uno::XComponentContext >& rxContext, ContentProvider *pProvider,
286  const uno::Reference< ucb::XContentIdentifier >& Identifier,
287  bool bIsFolder )
288  : ContentImplHelper( rxContext, pProvider, Identifier ),
289  m_pProvider( pProvider ),
290  m_pSession( nullptr ),
291  m_sURL( Identifier->getContentIdentifier( ) ),
292  m_aURL( Identifier->getContentIdentifier( ) ),
293  m_bTransient( true ),
294  m_bIsFolder( bIsFolder )
295  {
296  SAL_INFO( "ucb.ucp.cmis", "Content::Content() " << m_sURL );
297 
298  m_sObjectPath = m_aURL.getObjectPath( );
299  m_sObjectId = m_aURL.getObjectId( );
300  }
301 
302  Content::~Content()
303  {
304  }
305 
306  libcmis::Session* Content::getSession( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
307  {
308  // Set the proxy if needed. We are doing that all times as the proxy data shouldn't be cached.
310  INetURLObject aBindingUrl( m_aURL.getBindingUrl( ) );
311  const ucbhelper::InternetProxyServer& rProxy = aProxyDecider.getProxy(
312  INetURLObject::GetScheme( aBindingUrl.GetProtocol( ) ), aBindingUrl.GetHost(), aBindingUrl.GetPort() );
313  OUString sProxy = rProxy.aName;
314  if ( rProxy.nPort > 0 )
315  sProxy += ":" + OUString::number( rProxy.nPort );
316  libcmis::SessionFactory::setProxySettings( OUSTR_TO_STDSTR( sProxy ), string(), string(), string() );
317 
318  // Look for a cached session, key is binding url + repo id
319  OUString sSessionId = m_aURL.getBindingUrl( ) + m_aURL.getRepositoryId( );
320  if ( nullptr == m_pSession )
321  m_pSession = m_pProvider->getSession( sSessionId, m_aURL.getUsername( ) );
322 
323  if ( nullptr == m_pSession )
324  {
325 #ifndef SYSTEM_CURL
326  // Initialize NSS library to make sure libcmis (and curl) can access CACERTs using NSS
327  // when using internal libcurl.
328  uno::Reference< css::xml::crypto::XNSSInitializer >
329  xNSSInitializer = css::xml::crypto::NSSInitializer::create( m_xContext );
330 
331  uno::Reference< css::xml::crypto::XDigestContext > xDigestContext(
332  xNSSInitializer->getDigestContext( css::xml::crypto::DigestID::SHA256,
333  uno::Sequence< beans::NamedValue >() ),
334  uno::UNO_SET_THROW );
335 #endif
336 
337  // Set the SSL Validation handler
338  libcmis::CertValidationHandlerPtr certHandler(
339  new CertValidationHandler( xEnv, m_xContext, aBindingUrl.GetHost( ) ) );
340  libcmis::SessionFactory::setCertificateValidationHandler( certHandler );
341 
342  // Get the auth credentials
343  AuthProvider aAuthProvider(xEnv, m_xIdentifier->getContentIdentifier(), m_aURL.getBindingUrl());
344  AuthProvider::setXEnv( xEnv );
345 
346  string rUsername = OUSTR_TO_STDSTR( m_aURL.getUsername( ) );
347  string rPassword = OUSTR_TO_STDSTR( m_aURL.getPassword( ) );
348 
349  bool bSkipInitialPWAuth = false;
350  if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL ) {
351  // skip the initial username and pw-auth prompt, the only supported method is the
352  // auth-code-fallback one (login with your browser, copy code into the dialog)
353  // TODO: if LO were to listen on localhost for the request, it would be much nicer
354  // user experience
355  bSkipInitialPWAuth = true;
356  }
357 
358  bool bIsDone = false;
359 
360  while ( !bIsDone )
361  {
362  if (bSkipInitialPWAuth || aAuthProvider.authenticationQuery(rUsername, rPassword))
363  {
364  // Initiate a CMIS session and register it as we found nothing
365  libcmis::OAuth2DataPtr oauth2Data;
366  if ( m_aURL.getBindingUrl( ) == GDRIVE_BASE_URL )
367  {
368  libcmis::SessionFactory::setOAuth2AuthCodeProvider(AuthProvider::gdriveAuthCodeFallback);
369  oauth2Data.reset( new libcmis::OAuth2Data(
370  GDRIVE_AUTH_URL, GDRIVE_TOKEN_URL,
371  GDRIVE_SCOPE, GDRIVE_REDIRECT_URI,
372  GDRIVE_CLIENT_ID, GDRIVE_CLIENT_SECRET ) );
373  }
374  if ( m_aURL.getBindingUrl().startsWith( ALFRESCO_CLOUD_BASE_URL ) )
375  oauth2Data.reset( new libcmis::OAuth2Data(
376  ALFRESCO_CLOUD_AUTH_URL, ALFRESCO_CLOUD_TOKEN_URL,
377  ALFRESCO_CLOUD_SCOPE, ALFRESCO_CLOUD_REDIRECT_URI,
378  ALFRESCO_CLOUD_CLIENT_ID, ALFRESCO_CLOUD_CLIENT_SECRET ) );
379  if ( m_aURL.getBindingUrl( ) == ONEDRIVE_BASE_URL )
380  {
381  // reset the skip, so user gets a chance to cancel
382  bSkipInitialPWAuth = false;
383  libcmis::SessionFactory::setOAuth2AuthCodeProvider(AuthProvider::onedriveAuthCodeFallback);
384  oauth2Data.reset( new libcmis::OAuth2Data(
385  ONEDRIVE_AUTH_URL, ONEDRIVE_TOKEN_URL,
386  ONEDRIVE_SCOPE, ONEDRIVE_REDIRECT_URI,
387  ONEDRIVE_CLIENT_ID, ONEDRIVE_CLIENT_SECRET ) );
388  }
389  try
390  {
391  m_pSession = libcmis::SessionFactory::createSession(
392  OUSTR_TO_STDSTR( m_aURL.getBindingUrl( ) ),
393  rUsername, rPassword, OUSTR_TO_STDSTR( m_aURL.getRepositoryId( ) ), false, oauth2Data );
394 
395  if ( m_pSession == nullptr )
396  {
397  // Fail: session was not created
399  ucb::IOErrorCode_INVALID_DEVICE,
400  generateErrorArguments(m_aURL),
401  xEnv);
402  }
403  else if ( m_pSession->getRepository() == nullptr )
404  {
405  // Fail: no repository or repository is invalid
407  ucb::IOErrorCode_INVALID_DEVICE,
408  generateErrorArguments(m_aURL),
409  xEnv,
410  "error accessing a repository");
411  }
412  else
413  {
414  m_pProvider->registerSession(sSessionId, m_aURL.getUsername( ), m_pSession);
415  }
416 
417  bIsDone = true;
418  }
419  catch( const libcmis::Exception & e )
420  {
421  if ( e.getType() != "permissionDenied" )
422  throw;
423  }
424  }
425  else
426  {
427  // Silently fail as the user cancelled the authentication
429  ucb::IOErrorCode_ABORT,
430  uno::Sequence< uno::Any >( 0 ),
431  xEnv );
432  throw uno::RuntimeException( );
433  }
434  }
435  }
436  return m_pSession;
437  }
438 
439  libcmis::ObjectTypePtr const & Content::getObjectType( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
440  {
441  if ( nullptr == m_pObjectType.get( ) && m_bTransient )
442  {
443  string typeId = m_bIsFolder ? "cmis:folder" : "cmis:document";
444  // The type to create needs to be fetched from the possible children types
445  // defined in the parent folder. Then, we'll pick up the first one we find matching
446  // cmis:folder or cmis:document (depending what we need to create).
447  // The easy case will work in most cases, but not on some servers (like Lotus Live)
448  libcmis::Folder* pParent = nullptr;
449  bool bTypeRestricted = false;
450  try
451  {
452  pParent = dynamic_cast< libcmis::Folder* >( getObject( xEnv ).get( ) );
453  }
454  catch ( const libcmis::Exception& )
455  {
456  }
457 
458  if ( pParent )
459  {
460  map< string, libcmis::PropertyPtr >& aProperties = pParent->getProperties( );
461  map< string, libcmis::PropertyPtr >::iterator it = aProperties.find( "cmis:allowedChildObjectTypeIds" );
462  if ( it != aProperties.end( ) )
463  {
464  libcmis::PropertyPtr pProperty = it->second;
465  if ( pProperty )
466  {
467  vector< string > typesIds = pProperty->getStrings( );
468  for ( const auto& rType : typesIds )
469  {
470  bTypeRestricted = true;
471  libcmis::ObjectTypePtr type = getSession( xEnv )->getType( rType );
472 
473  // FIXME Improve performances by adding getBaseTypeId( ) method to libcmis
474  if ( type->getBaseType( )->getId( ) == typeId )
475  {
476  m_pObjectType = type;
477  break;
478  }
479  }
480  }
481  }
482  }
483 
484  if ( !bTypeRestricted )
485  m_pObjectType = getSession( xEnv )->getType( typeId );
486  }
487  return m_pObjectType;
488  }
489 
490 
491  libcmis::ObjectPtr const & Content::getObject( const uno::Reference< ucb::XCommandEnvironment >& xEnv )
492  {
493  // can't get the session for some reason
494  // the recent file opening at start up is an example.
495  try
496  {
497  if ( !getSession( xEnv ) )
498  return m_pObject;
499  }
500  catch ( uno::RuntimeException& )
501  {
502  return m_pObject;
503  }
504  if ( !m_pObject.get() )
505  {
506  if ( !m_sObjectId.isEmpty( ) )
507  {
508  try
509  {
510  m_pObject = getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId ) );
511  }
512  catch ( const libcmis::Exception& )
513  {
514  throw libcmis::Exception( "Object not found" );
515  }
516  }
517  else if (!(m_sObjectPath.isEmpty() || m_sObjectPath == "/"))
518  {
519  try
520  {
521  m_pObject = getSession( xEnv )->getObjectByPath( OUSTR_TO_STDSTR( m_sObjectPath ) );
522  }
523  catch ( const libcmis::Exception& )
524  {
525  // In some cases, getting the object from the path doesn't work,
526  // but getting the parent from its path and the get the child in the list is OK.
527  // It's weird, but needed to handle case where the path isn't the folders/files
528  // names separated by '/' (as in Lotus Live)
529  INetURLObject aParentUrl( m_sURL );
531  aParentUrl.removeSegment( );
532  OUString sParentUrl = aParentUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE );
533  // Avoid infinite recursion if sParentUrl == m_sURL
534  if (sParentUrl != m_sURL)
535  {
536  rtl::Reference<Content> xParent(new Content(m_xContext, m_pProvider, new ucbhelper::ContentIdentifier(sParentUrl)));
537  libcmis::FolderPtr pParentFolder = boost::dynamic_pointer_cast< libcmis::Folder >(xParent->getObject(xEnv));
538  if (pParentFolder)
539  {
540  vector< libcmis::ObjectPtr > children = pParentFolder->getChildren();
541  auto it = std::find_if(children.begin(), children.end(),
542  [&sName](const libcmis::ObjectPtr& rChild) { return rChild->getName() == sName; });
543  if (it != children.end())
544  m_pObject = *it;
545  }
546  }
547 
548  if ( !m_pObject )
549  throw libcmis::Exception( "Object not found" );
550  }
551  }
552  else
553  {
554  m_pObject = getSession( xEnv )->getRootFolder( );
555  m_sObjectPath = "/";
556  m_sObjectId = OUString( );
557  }
558  }
559 
560  return m_pObject;
561  }
562 
563  bool Content::isFolder(const uno::Reference< ucb::XCommandEnvironment >& xEnv )
564  {
565  bool bIsFolder = false;
566  try
567  {
568  libcmis::ObjectPtr obj = getObject( xEnv );
569  if ( obj )
570  bIsFolder = obj->getBaseType( ) == "cmis:folder";
571  }
572  catch ( const libcmis::Exception& e )
573  {
574  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
575 
577  ucb::IOErrorCode_GENERAL,
578  uno::Sequence< uno::Any >( 0 ),
579  xEnv,
580  OUString::createFromAscii( e.what( ) ) );
581 
582  }
583  return bIsFolder;
584  }
585 
586  uno::Any Content::getBadArgExcept()
587  {
588  return uno::makeAny( lang::IllegalArgumentException(
589  "Wrong argument type!",
590  static_cast< cppu::OWeakObject * >( this ), -1) );
591  }
592 
593  libcmis::ObjectPtr Content::updateProperties(
594  const uno::Any& iCmisProps,
595  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
596  {
597  // Convert iCmisProps to Cmis Properties;
598  uno::Sequence< document::CmisProperty > aPropsSeq;
599  iCmisProps >>= aPropsSeq;
600  map< string, libcmis::PropertyPtr > aProperties;
601 
602  for ( const auto& rProp : std::as_const(aPropsSeq) )
603  {
604  std::string id = OUSTR_TO_STDSTR( rProp.Id );
605  libcmis::PropertyPtr prop = lcl_unoToCmisProperty( rProp );
606  aProperties.insert( std::pair<string, libcmis::PropertyPtr>( id, prop ) );
607  }
608  libcmis::ObjectPtr updateObj;
609  try
610  {
611  updateObj = getObject( xEnv )->updateProperties( aProperties );
612  }
613  catch ( const libcmis::Exception& e )
614  {
615  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: "<< e.what( ) );
616  }
617 
618  return updateObj;
619  }
620 
621  uno::Reference< sdbc::XRow > Content::getPropertyValues(
622  const uno::Sequence< beans::Property >& rProperties,
623  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
624  {
625  rtl::Reference< ::ucbhelper::PropertyValueSet > xRow = new ::ucbhelper::PropertyValueSet( m_xContext );
626 
627  for( const beans::Property& rProp : rProperties )
628  {
629  try
630  {
631  if ( rProp.Name == "IsDocument" )
632  {
633  try
634  {
635  libcmis::ObjectPtr obj = getObject( xEnv );
636  if ( obj )
637  xRow->appendBoolean( rProp, obj->getBaseType( ) == "cmis:document" );
638  }
639  catch ( const libcmis::Exception& )
640  {
641  if ( m_pObjectType.get( ) )
642  xRow->appendBoolean( rProp, getObjectType( xEnv )->getBaseType()->getId( ) == "cmis:document" );
643  else
644  xRow->appendVoid( rProp );
645  }
646  }
647  else if ( rProp.Name == "IsFolder" )
648  {
649  try
650  {
651  libcmis::ObjectPtr obj = getObject( xEnv );
652  if ( obj )
653  xRow->appendBoolean( rProp, obj->getBaseType( ) == "cmis:folder" );
654  else
655  xRow->appendBoolean( rProp, false );
656  }
657  catch ( const libcmis::Exception& )
658  {
659  if ( m_pObjectType.get( ) )
660  xRow->appendBoolean( rProp, getObjectType( xEnv )->getBaseType()->getId( ) == "cmis:folder" );
661  else
662  xRow->appendVoid( rProp );
663  }
664  }
665  else if ( rProp.Name == "Title" )
666  {
667  OUString sTitle;
668  try
669  {
670  sTitle = STD_TO_OUSTR( getObject( xEnv )->getName() );
671  }
672  catch ( const libcmis::Exception& )
673  {
674  if ( !m_pObjectProps.empty() )
675  {
676  map< string, libcmis::PropertyPtr >::iterator it = m_pObjectProps.find( "cmis:name" );
677  if ( it != m_pObjectProps.end( ) )
678  {
679  vector< string > values = it->second->getStrings( );
680  if ( !values.empty() )
681  sTitle = STD_TO_OUSTR( values.front( ) );
682  }
683  }
684  }
685 
686  // Nothing worked... get it from the path
687  if ( sTitle.isEmpty( ) )
688  {
689  OUString sPath = m_sObjectPath;
690 
691  // Get rid of the trailing slash problem
692  if ( sPath.endsWith("/") )
693  sPath = sPath.copy( 0, sPath.getLength() - 1 );
694 
695  // Get the last segment
696  sal_Int32 nPos = sPath.lastIndexOf( '/' );
697  if ( nPos >= 0 )
698  sTitle = sPath.copy( nPos + 1 );
699  }
700 
701  if ( !sTitle.isEmpty( ) )
702  xRow->appendString( rProp, sTitle );
703  else
704  xRow->appendVoid( rProp );
705  }
706  else if ( rProp.Name == "ObjectId" )
707  {
708  OUString sId;
709  try
710  {
711  sId = STD_TO_OUSTR( getObject( xEnv )->getId() );
712  }
713  catch ( const libcmis::Exception& )
714  {
715  if ( !m_pObjectProps.empty() )
716  {
717  map< string, libcmis::PropertyPtr >::iterator it = m_pObjectProps.find( "cmis:objectId" );
718  if ( it != m_pObjectProps.end( ) )
719  {
720  vector< string > values = it->second->getStrings( );
721  if ( !values.empty() )
722  sId = STD_TO_OUSTR( values.front( ) );
723  }
724  }
725  }
726 
727  if ( !sId.isEmpty( ) )
728  xRow->appendString( rProp, sId );
729  else
730  xRow->appendVoid( rProp );
731  }
732  else if ( rProp.Name == "TitleOnServer" )
733  {
734  xRow->appendString( rProp, m_sObjectPath);
735  }
736  else if ( rProp.Name == "IsReadOnly" )
737  {
738  boost::shared_ptr< libcmis::AllowableActions > allowableActions = getObject( xEnv )->getAllowableActions( );
739  bool bReadOnly = false;
740  if ( !allowableActions->isAllowed( libcmis::ObjectAction::SetContentStream ) &&
741  !allowableActions->isAllowed( libcmis::ObjectAction::CheckIn ) )
742  bReadOnly = true;
743 
744  xRow->appendBoolean( rProp, bReadOnly );
745  }
746  else if ( rProp.Name == "DateCreated" )
747  {
748  util::DateTime aTime = lcl_boostToUnoTime( getObject( xEnv )->getCreationDate( ) );
749  xRow->appendTimestamp( rProp, aTime );
750  }
751  else if ( rProp.Name == "DateModified" )
752  {
753  util::DateTime aTime = lcl_boostToUnoTime( getObject( xEnv )->getLastModificationDate( ) );
754  xRow->appendTimestamp( rProp, aTime );
755  }
756  else if ( rProp.Name == "Size" )
757  {
758  try
759  {
760  libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject( xEnv ).get( ) );
761  if ( nullptr != document )
762  xRow->appendLong( rProp, document->getContentLength() );
763  else
764  xRow->appendVoid( rProp );
765  }
766  catch ( const libcmis::Exception& )
767  {
768  xRow->appendVoid( rProp );
769  }
770  }
771  else if ( rProp.Name == "CreatableContentsInfo" )
772  {
773  xRow->appendObject( rProp, uno::makeAny( queryCreatableContentsInfo( xEnv ) ) );
774  }
775  else if ( rProp.Name == "MediaType" )
776  {
777  try
778  {
779  libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject( xEnv ).get( ) );
780  if ( nullptr != document )
781  xRow->appendString( rProp, STD_TO_OUSTR( document->getContentType() ) );
782  else
783  xRow->appendVoid( rProp );
784  }
785  catch ( const libcmis::Exception& )
786  {
787  xRow->appendVoid( rProp );
788  }
789  }
790  else if ( rProp.Name == "IsVolume" )
791  {
792  xRow->appendBoolean( rProp, false );
793  }
794  else if ( rProp.Name == "IsRemote" )
795  {
796  xRow->appendBoolean( rProp, false );
797  }
798  else if ( rProp.Name == "IsRemoveable" )
799  {
800  xRow->appendBoolean( rProp, false );
801  }
802  else if ( rProp.Name == "IsFloppy" )
803  {
804  xRow->appendBoolean( rProp, false );
805  }
806  else if ( rProp.Name == "IsCompactDisc" )
807  {
808  xRow->appendBoolean( rProp, false );
809  }
810  else if ( rProp.Name == "IsHidden" )
811  {
812  xRow->appendBoolean( rProp, false );
813  }
814  else if ( rProp.Name == "TargetURL" )
815  {
816  xRow->appendString( rProp, "" );
817  }
818  else if ( rProp.Name == "BaseURI" )
819  {
820  xRow->appendString( rProp, m_aURL.getBindingUrl( ) );
821  }
822  else if ( rProp.Name == "CmisProperties" )
823  {
824  try
825  {
826  libcmis::ObjectPtr object = getObject( xEnv );
827  map< string, libcmis::PropertyPtr >& aProperties = object->getProperties( );
828  uno::Sequence< document::CmisProperty > aCmisProperties( aProperties.size( ) );
829  document::CmisProperty* pCmisProps = aCmisProperties.getArray( );
830  sal_Int32 i = 0;
831  for ( const auto& [sId, rProperty] : aProperties )
832  {
833  string sDisplayName = rProperty->getPropertyType()->getDisplayName( );
834  bool bUpdatable = rProperty->getPropertyType()->isUpdatable( );
835  bool bRequired = rProperty->getPropertyType()->isRequired( );
836  bool bMultiValued = rProperty->getPropertyType()->isMultiValued();
837  bool bOpenChoice = rProperty->getPropertyType()->isOpenChoice();
838 
839  pCmisProps[i].Id = STD_TO_OUSTR( sId );
840  pCmisProps[i].Name = STD_TO_OUSTR( sDisplayName );
841  pCmisProps[i].Updatable = bUpdatable;
842  pCmisProps[i].Required = bRequired;
843  pCmisProps[i].MultiValued = bMultiValued;
844  pCmisProps[i].OpenChoice = bOpenChoice;
845  pCmisProps[i].Value = lcl_cmisPropertyToUno( rProperty );
846  switch ( rProperty->getPropertyType( )->getType( ) )
847  {
848  default:
849  case libcmis::PropertyType::String:
850  pCmisProps[i].Type = CMIS_TYPE_STRING;
851  break;
852  case libcmis::PropertyType::Integer:
853  pCmisProps[i].Type = CMIS_TYPE_INTEGER;
854  break;
855  case libcmis::PropertyType::Decimal:
856  pCmisProps[i].Type = CMIS_TYPE_DECIMAL;
857  break;
858  case libcmis::PropertyType::Bool:
859  pCmisProps[i].Type = CMIS_TYPE_BOOL;
860  break;
861  case libcmis::PropertyType::DateTime:
862  pCmisProps[i].Type = CMIS_TYPE_DATETIME;
863  break;
864  }
865  ++i;
866  }
867  xRow->appendObject( rProp.Name, uno::makeAny( aCmisProperties ) );
868  }
869  catch ( const libcmis::Exception& )
870  {
871  xRow->appendVoid( rProp );
872  }
873  }
874  else if ( rProp.Name == "IsVersionable" )
875  {
876  try
877  {
878  libcmis::ObjectPtr object = getObject( xEnv );
879  bool bIsVersionable = object->getTypeDescription( )->isVersionable( );
880  xRow->appendBoolean( rProp, bIsVersionable );
881  }
882  catch ( const libcmis::Exception& )
883  {
884  xRow->appendVoid( rProp );
885  }
886  }
887  else if ( rProp.Name == "CanCheckOut" )
888  {
889  try
890  {
891  libcmis::ObjectPtr pObject = getObject( xEnv );
892  libcmis::AllowableActionsPtr aAllowables = pObject->getAllowableActions( );
893  bool bAllowed = false;
894  if ( aAllowables )
895  {
896  bAllowed = aAllowables->isAllowed( libcmis::ObjectAction::CheckOut );
897  }
898  xRow->appendBoolean( rProp, bAllowed );
899  }
900  catch ( const libcmis::Exception& )
901  {
902  xRow->appendVoid( rProp );
903  }
904  }
905  else if ( rProp.Name == "CanCancelCheckOut" )
906  {
907  try
908  {
909  libcmis::ObjectPtr pObject = getObject( xEnv );
910  libcmis::AllowableActionsPtr aAllowables = pObject->getAllowableActions( );
911  bool bAllowed = false;
912  if ( aAllowables )
913  {
914  bAllowed = aAllowables->isAllowed( libcmis::ObjectAction::CancelCheckOut );
915  }
916  xRow->appendBoolean( rProp, bAllowed );
917  }
918  catch ( const libcmis::Exception& )
919  {
920  xRow->appendVoid( rProp );
921  }
922  }
923  else if ( rProp.Name == "CanCheckIn" )
924  {
925  try
926  {
927  libcmis::ObjectPtr pObject = getObject( xEnv );
928  libcmis::AllowableActionsPtr aAllowables = pObject->getAllowableActions( );
929  bool bAllowed = false;
930  if ( aAllowables )
931  {
932  bAllowed = aAllowables->isAllowed( libcmis::ObjectAction::CheckIn );
933  }
934  xRow->appendBoolean( rProp, bAllowed );
935  }
936  catch ( const libcmis::Exception& )
937  {
938  xRow->appendVoid( rProp );
939  }
940  }
941  else
942  SAL_INFO( "ucb.ucp.cmis", "Looking for unsupported property " << rProp.Name );
943  }
944  catch (const libcmis::Exception&)
945  {
946  xRow->appendVoid( rProp );
947  }
948  }
949 
950  return xRow;
951  }
952 
953  uno::Any Content::open(const ucb::OpenCommandArgument2 & rOpenCommand,
954  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
955  {
956  bool bIsFolder = isFolder( xEnv );
957 
958  // Handle the case of the non-existing file
959  if ( !getObject( xEnv ) )
960  {
961  uno::Sequence< uno::Any > aArgs( 1 );
962  aArgs[ 0 ] <<= m_xIdentifier->getContentIdentifier();
963  uno::Any aErr = uno::makeAny(
964  ucb::InteractiveAugmentedIOException(OUString(), static_cast< cppu::OWeakObject * >( this ),
965  task::InteractionClassification_ERROR,
966  bIsFolder ? ucb::IOErrorCode_NOT_EXISTING_PATH : ucb::IOErrorCode_NOT_EXISTING, aArgs)
967  );
968 
970  }
971 
972  uno::Any aRet;
973 
974  bool bOpenFolder = (
975  ( rOpenCommand.Mode == ucb::OpenMode::ALL ) ||
976  ( rOpenCommand.Mode == ucb::OpenMode::FOLDERS ) ||
977  ( rOpenCommand.Mode == ucb::OpenMode::DOCUMENTS )
978  );
979 
980  if ( bOpenFolder && bIsFolder )
981  {
982  uno::Reference< ucb::XDynamicResultSet > xSet
983  = new DynamicResultSet(m_xContext, this, rOpenCommand, xEnv );
984  aRet <<= xSet;
985  }
986  else if ( rOpenCommand.Sink.is() )
987  {
988  if (
989  ( rOpenCommand.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
990  ( rOpenCommand.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE )
991  )
992  {
994  uno::makeAny ( ucb::UnsupportedOpenModeException
995  ( OUString(), static_cast< cppu::OWeakObject * >( this ),
996  sal_Int16( rOpenCommand.Mode ) ) ),
997  xEnv );
998  }
999 
1000  if ( !feedSink( rOpenCommand.Sink, xEnv ) )
1001  {
1002  // Note: rOpenCommand.Sink may contain an XStream
1003  // implementation. Support for this type of
1004  // sink is optional...
1005  SAL_INFO( "ucb.ucp.cmis", "Failed to copy data to sink" );
1006 
1008  uno::makeAny (ucb::UnsupportedDataSinkException
1009  ( OUString(), static_cast< cppu::OWeakObject * >( this ),
1010  rOpenCommand.Sink ) ),
1011  xEnv );
1012  }
1013  }
1014  else
1015  SAL_INFO( "ucb.ucp.cmis", "Open falling through ..." );
1016 
1017  return aRet;
1018  }
1019 
1020  OUString Content::checkIn( const ucb::CheckinArgument& rArg,
1021  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1022  {
1023  ucbhelper::Content aSourceContent( rArg.SourceURL, xEnv, comphelper::getProcessComponentContext( ) );
1024  uno::Reference< io::XInputStream > xIn = aSourceContent.openStream( );
1025 
1026  libcmis::ObjectPtr object;
1027  try
1028  {
1029  object = getObject( xEnv );
1030  }
1031  catch ( const libcmis::Exception& e )
1032  {
1033  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1035  ucb::IOErrorCode_GENERAL,
1036  uno::Sequence< uno::Any >( 0 ),
1037  xEnv,
1038  OUString::createFromAscii( e.what() ) );
1039  }
1040 
1041  libcmis::Document* pPwc = dynamic_cast< libcmis::Document* >( object.get( ) );
1042  if ( !pPwc )
1043  {
1045  ucb::IOErrorCode_GENERAL,
1046  uno::Sequence< uno::Any >( 0 ),
1047  xEnv,
1048  "Checkin only supported by documents" );
1049  }
1050 
1051  boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) );
1052  uno::Reference < io::XOutputStream > xOutput = new StdOutputStream( pOut );
1053  copyData( xIn, xOutput );
1054 
1055  map< string, libcmis::PropertyPtr > newProperties;
1056  libcmis::DocumentPtr pDoc;
1057 
1058  try
1059  {
1060  pDoc = pPwc->checkIn( rArg.MajorVersion, OUSTR_TO_STDSTR( rArg.VersionComment ), newProperties,
1061  pOut, OUSTR_TO_STDSTR( rArg.MimeType ), OUSTR_TO_STDSTR( rArg.NewTitle ) );
1062  }
1063  catch ( const libcmis::Exception& e )
1064  {
1065  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1067  ucb::IOErrorCode_GENERAL,
1068  uno::Sequence< uno::Any >( 0 ),
1069  xEnv,
1070  OUString::createFromAscii( e.what() ) );
1071  }
1072 
1073  // Get the URL and send it back as a result
1074  URL aCmisUrl( m_sURL );
1075  vector< string > aPaths = pDoc->getPaths( );
1076  if ( !aPaths.empty() )
1077  {
1078  string sPath = aPaths.front( );
1079  aCmisUrl.setObjectPath( STD_TO_OUSTR( sPath ) );
1080  }
1081  else
1082  {
1083  // We may have unfiled document depending on the server, those
1084  // won't have any path, use their ID instead
1085  string sId = pDoc->getId( );
1086  aCmisUrl.setObjectId( STD_TO_OUSTR( sId ) );
1087  }
1088  return aCmisUrl.asString( );
1089  }
1090 
1091  OUString Content::checkOut( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1092  {
1093  OUString aRet;
1094  try
1095  {
1096  // Checkout the document if possible
1097  libcmis::DocumentPtr pDoc = boost::dynamic_pointer_cast< libcmis::Document >( getObject( xEnv ) );
1098  if ( pDoc.get( ) == nullptr )
1099  {
1101  ucb::IOErrorCode_GENERAL,
1102  uno::Sequence< uno::Any >( 0 ),
1103  xEnv,
1104  "Checkout only supported by documents" );
1105  }
1106  libcmis::DocumentPtr pPwc = pDoc->checkOut( );
1107 
1108  // Compute the URL of the Private Working Copy (PWC)
1109  URL aCmisUrl( m_sURL );
1110  vector< string > aPaths = pPwc->getPaths( );
1111  if ( !aPaths.empty() )
1112  {
1113  string sPath = aPaths.front( );
1114  aCmisUrl.setObjectPath( STD_TO_OUSTR( sPath ) );
1115  }
1116  else
1117  {
1118  // We may have unfiled PWC depending on the server, those
1119  // won't have any path, use their ID instead
1120  string sId = pPwc->getId( );
1121  aCmisUrl.setObjectId( STD_TO_OUSTR( sId ) );
1122  }
1123  aRet = aCmisUrl.asString( );
1124  }
1125  catch ( const libcmis::Exception& e )
1126  {
1127  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1129  ucb::IOErrorCode_GENERAL,
1130  uno::Sequence< uno::Any >( 0 ),
1131  xEnv,
1132  o3tl::runtimeToOUString(e.what()));
1133  }
1134  return aRet;
1135  }
1136 
1137  OUString Content::cancelCheckOut( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1138  {
1139  OUString aRet;
1140  try
1141  {
1142  libcmis::DocumentPtr pPwc = boost::dynamic_pointer_cast< libcmis::Document >( getObject( xEnv ) );
1143  if ( pPwc.get( ) == nullptr )
1144  {
1146  ucb::IOErrorCode_GENERAL,
1147  uno::Sequence< uno::Any >( 0 ),
1148  xEnv,
1149  "CancelCheckout only supported by documents" );
1150  }
1151  pPwc->cancelCheckout( );
1152 
1153  // Get the Original document (latest version)
1154  vector< libcmis::DocumentPtr > aVersions = pPwc->getAllVersions( );
1155  for ( const auto& rVersion : aVersions )
1156  {
1157  libcmis::DocumentPtr pVersion = rVersion;
1158  map< string, libcmis::PropertyPtr > aProps = pVersion->getProperties( );
1159  bool bIsLatestVersion = false;
1160  map< string, libcmis::PropertyPtr >::iterator propIt = aProps.find( string( "cmis:isLatestVersion" ) );
1161  if ( propIt != aProps.end( ) && !propIt->second->getBools( ).empty( ) )
1162  {
1163  bIsLatestVersion = propIt->second->getBools( ).front( );
1164  }
1165 
1166  if ( bIsLatestVersion )
1167  {
1168  // Compute the URL of the Document
1169  URL aCmisUrl( m_sURL );
1170  vector< string > aPaths = pVersion->getPaths( );
1171  if ( !aPaths.empty() )
1172  {
1173  string sPath = aPaths.front( );
1174  aCmisUrl.setObjectPath( STD_TO_OUSTR( sPath ) );
1175  }
1176  else
1177  {
1178  // We may have unfiled doc depending on the server, those
1179  // won't have any path, use their ID instead
1180  string sId = pVersion->getId( );
1181  aCmisUrl.setObjectId( STD_TO_OUSTR( sId ) );
1182  }
1183  aRet = aCmisUrl.asString( );
1184  break;
1185  }
1186  }
1187  }
1188  catch ( const libcmis::Exception& e )
1189  {
1190  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1192  ucb::IOErrorCode_GENERAL,
1193  uno::Sequence< uno::Any >( 0 ),
1194  xEnv,
1195  o3tl::runtimeToOUString(e.what()));
1196  }
1197  return aRet;
1198  }
1199 
1200  uno::Sequence< document::CmisVersion> Content::getAllVersions( const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1201  {
1202  try
1203  {
1204  // get the document
1205  libcmis::DocumentPtr pDoc = boost::dynamic_pointer_cast< libcmis::Document >( getObject( xEnv ) );
1206  if ( pDoc.get( ) == nullptr )
1207  {
1209  ucb::IOErrorCode_GENERAL,
1210  uno::Sequence< uno::Any >( 0 ),
1211  xEnv,
1212  "Can not get the document" );
1213  }
1214  vector< libcmis::DocumentPtr > aCmisVersions = pDoc->getAllVersions( );
1215  uno::Sequence< document::CmisVersion > aVersions( aCmisVersions.size( ) );
1216  int i = 0;
1217  for ( const auto& rVersion : aCmisVersions )
1218  {
1219  libcmis::DocumentPtr pVersion = rVersion;
1220  aVersions[i].Id = STD_TO_OUSTR( pVersion->getId( ) );
1221  aVersions[i].Author = STD_TO_OUSTR( pVersion->getCreatedBy( ) );
1222  aVersions[i].TimeStamp = lcl_boostToUnoTime( pVersion->getLastModificationDate( ) );
1223  aVersions[i].Comment = STD_TO_OUSTR( pVersion->getStringProperty("cmis:checkinComment") );
1224  ++i;
1225  }
1226  return aVersions;
1227  }
1228  catch ( const libcmis::Exception& e )
1229  {
1230  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1232  ucb::IOErrorCode_GENERAL,
1233  uno::Sequence< uno::Any >( 0 ),
1234  xEnv,
1235  o3tl::runtimeToOUString(e.what()));
1236  }
1237  return uno::Sequence< document::CmisVersion > ( );
1238  }
1239 
1240  void Content::transfer( const ucb::TransferInfo& rTransferInfo,
1241  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1242  {
1243  // If the source isn't on the same CMIS repository, then simply copy
1244  INetURLObject aSourceUrl( rTransferInfo.SourceURL );
1245  if ( aSourceUrl.GetProtocol() != INetProtocol::Cmis )
1246  {
1247  OUString sSrcBindingUrl = URL( rTransferInfo.SourceURL ).getBindingUrl( );
1248  if ( sSrcBindingUrl != m_aURL.getBindingUrl( ) )
1249  {
1251  uno::makeAny(
1252  ucb::InteractiveBadTransferURLException(
1253  "Unsupported URL scheme!",
1254  static_cast< cppu::OWeakObject * >( this ) ) ),
1255  xEnv );
1256  }
1257  }
1258 
1259  SAL_INFO( "ucb.ucp.cmis", "TODO - Content::transfer()" );
1260  }
1261 
1262  void Content::insert( const uno::Reference< io::XInputStream > & xInputStream,
1263  bool bReplaceExisting, std::u16string_view rMimeType,
1264  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1265  {
1266  if ( !xInputStream.is() )
1267  {
1268  ucbhelper::cancelCommandExecution( uno::makeAny
1269  ( ucb::MissingInputStreamException
1270  ( OUString(), static_cast< cppu::OWeakObject * >( this ) ) ),
1271  xEnv );
1272  }
1273 
1274  // For transient content, the URL is the one of the parent
1275  if ( !m_bTransient )
1276  return;
1277 
1278  OUString sNewPath;
1279 
1280  // Try to get the object from the server if there is any
1281  libcmis::FolderPtr pFolder;
1282  try
1283  {
1284  pFolder = boost::dynamic_pointer_cast< libcmis::Folder >( getObject( xEnv ) );
1285  }
1286  catch ( const libcmis::Exception& )
1287  {
1288  }
1289 
1290  if ( pFolder == nullptr )
1291  return;
1292 
1293  libcmis::ObjectPtr object;
1294  map< string, libcmis::PropertyPtr >::iterator it = m_pObjectProps.find( "cmis:name" );
1295  if ( it == m_pObjectProps.end( ) )
1296  {
1297  ucbhelper::cancelCommandExecution( uno::makeAny
1298  ( uno::RuntimeException( "Missing name property",
1299  static_cast< cppu::OWeakObject * >( this ) ) ),
1300  xEnv );
1301  }
1302  string newName = it->second->getStrings( ).front( );
1303  string newPath = OUSTR_TO_STDSTR( m_sObjectPath );
1304  if ( !newPath.empty( ) && newPath[ newPath.size( ) - 1 ] != '/' )
1305  newPath += "/";
1306  newPath += newName;
1307  try
1308  {
1309  if ( !m_sObjectId.isEmpty( ) )
1310  object = getSession( xEnv )->getObject( OUSTR_TO_STDSTR( m_sObjectId) );
1311  else
1312  object = getSession( xEnv )->getObjectByPath( newPath );
1313  sNewPath = STD_TO_OUSTR( newPath );
1314  }
1315  catch ( const libcmis::Exception& )
1316  {
1317  // Nothing matched the path
1318  }
1319 
1320  if ( nullptr != object.get( ) )
1321  {
1322  // Are the base type matching?
1323  if ( object->getBaseType( ) != m_pObjectType->getBaseType( )->getId() )
1324  {
1325  ucbhelper::cancelCommandExecution( uno::makeAny
1326  ( uno::RuntimeException( "Can't change a folder into a document and vice-versa.",
1327  static_cast< cppu::OWeakObject * >( this ) ) ),
1328  xEnv );
1329  }
1330 
1331  // Update the existing object if it's a document
1332  libcmis::Document* document = dynamic_cast< libcmis::Document* >( object.get( ) );
1333  if ( nullptr != document )
1334  {
1335  boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) );
1336  uno::Reference < io::XOutputStream > xOutput = new StdOutputStream( pOut );
1337  copyData( xInputStream, xOutput );
1338  try
1339  {
1340  document->setContentStream( pOut, OUSTR_TO_STDSTR( rMimeType ), string( ), bReplaceExisting );
1341  }
1342  catch ( const libcmis::Exception& )
1343  {
1344  ucbhelper::cancelCommandExecution( uno::makeAny
1345  ( uno::RuntimeException( "Error when setting document content",
1346  static_cast< cppu::OWeakObject * >( this ) ) ),
1347  xEnv );
1348  }
1349  }
1350  }
1351  else
1352  {
1353  // We need to create a brand new object... either folder or document
1354  bool bIsFolder = getObjectType( xEnv )->getBaseType( )->getId( ) == "cmis:folder";
1355  setCmisProperty( "cmis:objectTypeId", getObjectType( xEnv )->getId( ), xEnv );
1356 
1357  if ( bIsFolder )
1358  {
1359  try
1360  {
1361  pFolder->createFolder( m_pObjectProps );
1362  sNewPath = STD_TO_OUSTR( newPath );
1363  }
1364  catch ( const libcmis::Exception& )
1365  {
1366  ucbhelper::cancelCommandExecution( uno::makeAny
1367  ( uno::RuntimeException( "Error when creating folder",
1368  static_cast< cppu::OWeakObject * >( this ) ) ),
1369  xEnv );
1370  }
1371  }
1372  else
1373  {
1374  boost::shared_ptr< ostream > pOut( new ostringstream ( ios_base::binary | ios_base::in | ios_base::out ) );
1375  uno::Reference < io::XOutputStream > xOutput = new StdOutputStream( pOut );
1376  copyData( xInputStream, xOutput );
1377  try
1378  {
1379  pFolder->createDocument( m_pObjectProps, pOut, OUSTR_TO_STDSTR( rMimeType ), string() );
1380  sNewPath = STD_TO_OUSTR( newPath );
1381  }
1382  catch ( const libcmis::Exception& )
1383  {
1384  ucbhelper::cancelCommandExecution( uno::makeAny
1385  ( uno::RuntimeException( "Error when creating document",
1386  static_cast< cppu::OWeakObject * >( this ) ) ),
1387  xEnv );
1388  }
1389  }
1390  }
1391 
1392  if ( sNewPath.isEmpty( ) && m_sObjectId.isEmpty( ) )
1393  return;
1394 
1395  // Update the current content: it's no longer transient
1396  m_sObjectPath = sNewPath;
1397  URL aUrl( m_sURL );
1398  aUrl.setObjectPath( m_sObjectPath );
1399  aUrl.setObjectId( m_sObjectId );
1400  m_sURL = aUrl.asString( );
1401  m_pObject.reset( );
1402  m_pObjectType.reset( );
1403  m_pObjectProps.clear( );
1404  m_bTransient = false;
1405  inserted();
1406  }
1407 
1408  const int TRANSFER_BUFFER_SIZE = 65536;
1409 
1410  void Content::copyData(
1411  const uno::Reference< io::XInputStream >& xIn,
1412  const uno::Reference< io::XOutputStream >& xOut )
1413  {
1414  uno::Sequence< sal_Int8 > theData( TRANSFER_BUFFER_SIZE );
1415 
1416  while ( xIn->readBytes( theData, TRANSFER_BUFFER_SIZE ) > 0 )
1417  xOut->writeBytes( theData );
1418 
1419  xOut->closeOutput();
1420  }
1421 
1422  uno::Sequence< uno::Any > Content::setPropertyValues(
1423  const uno::Sequence< beans::PropertyValue >& rValues,
1424  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1425  {
1426  try
1427  {
1428  // Get the already set properties if possible
1429  if ( !m_bTransient && getObject( xEnv ).get( ) )
1430  {
1431  m_pObjectProps.clear( );
1432  m_pObjectType = getObject( xEnv )->getTypeDescription();
1433  }
1434  }
1435  catch ( const libcmis::Exception& e )
1436  {
1437  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1439  ucb::IOErrorCode_GENERAL,
1440  uno::Sequence< uno::Any >( 0 ),
1441  xEnv,
1442  o3tl::runtimeToOUString(e.what()));
1443  }
1444 
1445  sal_Int32 nCount = rValues.getLength();
1446  uno::Sequence< uno::Any > aRet( nCount );
1447 
1448  bool bChanged = false;
1449  const beans::PropertyValue* pValues = rValues.getConstArray();
1450  for ( sal_Int32 n = 0; n < nCount; ++n )
1451  {
1452  const beans::PropertyValue& rValue = pValues[ n ];
1453  if ( rValue.Name == "ContentType" ||
1454  rValue.Name == "MediaType" ||
1455  rValue.Name == "IsDocument" ||
1456  rValue.Name == "IsFolder" ||
1457  rValue.Name == "Size" ||
1458  rValue.Name == "CreatableContentsInfo" )
1459  {
1460  lang::IllegalAccessException e ( "Property is read-only!",
1461  static_cast< cppu::OWeakObject* >( this ) );
1462  aRet[ n ] <<= e;
1463  }
1464  else if ( rValue.Name == "Title" )
1465  {
1466  OUString aNewTitle;
1467  if (!( rValue.Value >>= aNewTitle ))
1468  {
1469  aRet[ n ] <<= beans::IllegalTypeException
1470  ( "Property value has wrong type!",
1471  static_cast< cppu::OWeakObject * >( this ) );
1472  continue;
1473  }
1474 
1475  if ( aNewTitle.isEmpty() )
1476  {
1477  aRet[ n ] <<= lang::IllegalArgumentException
1478  ( "Empty title not allowed!",
1479  static_cast< cppu::OWeakObject * >( this ), -1 );
1480  continue;
1481 
1482  }
1483 
1484  setCmisProperty( "cmis:name", OUSTR_TO_STDSTR( aNewTitle ), xEnv );
1485  bChanged = true;
1486  }
1487  else
1488  {
1489  SAL_INFO( "ucb.ucp.cmis", "Couldn't set property: " << rValue.Name );
1490  lang::IllegalAccessException e ( "Property is read-only!",
1491  static_cast< cppu::OWeakObject* >( this ) );
1492  aRet[ n ] <<= e;
1493  }
1494  }
1495 
1496  try
1497  {
1498  if ( !m_bTransient && bChanged )
1499  {
1500  getObject( xEnv )->updateProperties( m_pObjectProps );
1501  }
1502  }
1503  catch ( const libcmis::Exception& e )
1504  {
1505  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1507  ucb::IOErrorCode_GENERAL,
1508  uno::Sequence< uno::Any >( 0 ),
1509  xEnv,
1510  o3tl::runtimeToOUString(e.what()));
1511  }
1512 
1513  return aRet;
1514  }
1515 
1516  bool Content::feedSink( const uno::Reference< uno::XInterface>& xSink,
1517  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1518  {
1519  if ( !xSink.is() )
1520  return false;
1521 
1522  uno::Reference< io::XOutputStream > xOut(xSink, uno::UNO_QUERY );
1523  uno::Reference< io::XActiveDataSink > xDataSink(xSink, uno::UNO_QUERY );
1524  uno::Reference< io::XActiveDataStreamer > xDataStreamer( xSink, uno::UNO_QUERY );
1525 
1526  if ( !xOut.is() && !xDataSink.is() && ( !xDataStreamer.is() || !xDataStreamer->getStream().is() ) )
1527  return false;
1528 
1529  if ( xDataStreamer.is() && !xOut.is() )
1530  xOut = xDataStreamer->getStream()->getOutputStream();
1531 
1532  try
1533  {
1534  libcmis::Document* document = dynamic_cast< libcmis::Document* >( getObject( xEnv ).get() );
1535 
1536  if (!document)
1537  return false;
1538 
1539  boost::shared_ptr< istream > aIn = document->getContentStream( );
1540 
1541  uno::Reference< io::XInputStream > xIn = new StdInputStream( aIn );
1542  if( !xIn.is( ) )
1543  return false;
1544 
1545  if ( xDataSink.is() )
1546  xDataSink->setInputStream( xIn );
1547  else if ( xOut.is() )
1548  copyData( xIn, xOut );
1549  }
1550  catch ( const libcmis::Exception& e )
1551  {
1552  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1554  ucb::IOErrorCode_GENERAL,
1555  uno::Sequence< uno::Any >( 0 ),
1556  xEnv,
1557  o3tl::runtimeToOUString(e.what()));
1558  }
1559 
1560  return true;
1561  }
1562 
1563  uno::Sequence< beans::Property > Content::getProperties(
1564  const uno::Reference< ucb::XCommandEnvironment > & )
1565  {
1566  static const beans::Property aGenericProperties[] =
1567  {
1568  beans::Property( "IsDocument",
1570  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1571  beans::Property( "IsFolder",
1573  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1574  beans::Property( "Title",
1576  beans::PropertyAttribute::BOUND ),
1577  beans::Property( "ObjectId",
1579  beans::PropertyAttribute::BOUND ),
1580  beans::Property( "TitleOnServer",
1582  beans::PropertyAttribute::BOUND ),
1583  beans::Property( "IsReadOnly",
1585  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1586  beans::Property( "DateCreated",
1588  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1589  beans::Property( "DateModified",
1591  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1592  beans::Property( "Size",
1594  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1595  beans::Property( "CreatableContentsInfo",
1596  -1, cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
1597  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1598  beans::Property( "MediaType",
1600  beans::PropertyAttribute::BOUND ),
1601  beans::Property( "CmisProperties",
1602  -1, cppu::UnoType<uno::Sequence< document::CmisProperty>>::get(),
1603  beans::PropertyAttribute::BOUND ),
1604  beans::Property( "IsVersionable",
1606  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1607  beans::Property( "CanCheckOut",
1609  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1610  beans::Property( "CanCancelCheckOut",
1612  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1613  beans::Property( "CanCheckIn",
1615  beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY ),
1616  };
1617 
1618  const int nProps = SAL_N_ELEMENTS(aGenericProperties);
1619  return uno::Sequence< beans::Property > ( aGenericProperties, nProps );
1620  }
1621 
1622  uno::Sequence< ucb::CommandInfo > Content::getCommands(
1623  const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1624  {
1625  static const ucb::CommandInfo aCommandInfoTable[] =
1626  {
1627  // Required commands
1628  ucb::CommandInfo
1629  ( "getCommandInfo",
1630  -1, cppu::UnoType<void>::get() ),
1631  ucb::CommandInfo
1632  ( "getPropertySetInfo",
1633  -1, cppu::UnoType<void>::get() ),
1634  ucb::CommandInfo
1635  ( "getPropertyValues",
1636  -1, cppu::UnoType<uno::Sequence< beans::Property >>::get() ),
1637  ucb::CommandInfo
1638  ( "setPropertyValues",
1639  -1, cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get() ),
1640 
1641  // Optional standard commands
1642  ucb::CommandInfo
1643  ( "delete",
1644  -1, cppu::UnoType<bool>::get() ),
1645  ucb::CommandInfo
1646  ( "insert",
1648  ucb::CommandInfo
1649  ( "open",
1651 
1652  // Mandatory CMIS-only commands
1653  ucb::CommandInfo ( "checkout", -1, cppu::UnoType<void>::get() ),
1654  ucb::CommandInfo ( "cancelCheckout", -1, cppu::UnoType<void>::get() ),
1655  ucb::CommandInfo ( "checkIn", -1,
1657  ucb::CommandInfo ( "updateProperties", -1, cppu::UnoType<void>::get() ),
1658  ucb::CommandInfo
1659  ( "getAllVersions",
1660  -1, cppu::UnoType<uno::Sequence< document::CmisVersion >>::get() ),
1661 
1662 
1663  // Folder Only, omitted if not a folder
1664  ucb::CommandInfo
1665  ( "transfer",
1667  ucb::CommandInfo
1668  ( "createNewContent",
1670  };
1671 
1672  const int nProps = SAL_N_ELEMENTS( aCommandInfoTable );
1673  return uno::Sequence< ucb::CommandInfo >(aCommandInfoTable, isFolder( xEnv ) ? nProps : nProps - 2);
1674  }
1675 
1676  OUString Content::getParentURL( )
1677  {
1678  SAL_INFO( "ucb.ucp.cmis", "Content::getParentURL()" );
1679  OUString parentUrl = "/";
1680  if ( m_sObjectPath == "/" )
1681  return parentUrl;
1682  else
1683  {
1684  INetURLObject aUrl( m_sURL );
1685  if ( aUrl.getSegmentCount( ) > 0 )
1686  {
1687  URL aCmisUrl( m_sURL );
1688  aUrl.removeSegment( );
1690  parentUrl = aCmisUrl.asString( );
1691  }
1692  }
1693  return parentUrl;
1694  }
1695 
1697 
1698  void SAL_CALL Content::acquire() noexcept
1699  {
1700  ContentImplHelper::acquire();
1701  }
1702 
1703  void SAL_CALL Content::release() noexcept
1704  {
1705  ContentImplHelper::release();
1706  }
1707 
1708  uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
1709  {
1710  uno::Any aRet = cppu::queryInterface( rType, static_cast< ucb::XContentCreator * >( this ) );
1711  return aRet.hasValue() ? aRet : ContentImplHelper::queryInterface(rType);
1712  }
1713 
1714  OUString SAL_CALL Content::getImplementationName()
1715  {
1716  return "com.sun.star.comp.CmisContent";
1717  }
1718 
1719  uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
1720  {
1721  uno::Sequence<OUString> aSNS { "com.sun.star.ucb.CmisContent" };
1722  return aSNS;
1723  }
1724 
1725  OUString SAL_CALL Content::getContentType()
1726  {
1727  OUString sRet;
1728  try
1729  {
1730  sRet = isFolder( uno::Reference< ucb::XCommandEnvironment >() )
1731  ? std::u16string_view(u"" CMIS_FOLDER_TYPE)
1732  : std::u16string_view(u"" CMIS_FILE_TYPE);
1733  }
1734  catch (const uno::RuntimeException&)
1735  {
1736  throw;
1737  }
1738  catch (const uno::Exception& e)
1739  {
1741  throw lang::WrappedTargetRuntimeException(
1742  "wrapped Exception " + e.Message,
1743  uno::Reference<uno::XInterface>(), a);
1744  }
1745  return sRet;
1746  }
1747 
1748  uno::Any SAL_CALL Content::execute(
1749  const ucb::Command& aCommand,
1750  sal_Int32 /*CommandId*/,
1751  const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1752  {
1753  SAL_INFO( "ucb.ucp.cmis", "Content::execute( ) - " << aCommand.Name );
1754  uno::Any aRet;
1755 
1756  if ( aCommand.Name == "getPropertyValues" )
1757  {
1758  uno::Sequence< beans::Property > Properties;
1759  if ( !( aCommand.Argument >>= Properties ) )
1760  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1761  aRet <<= getPropertyValues( Properties, xEnv );
1762  }
1763  else if ( aCommand.Name == "getPropertySetInfo" )
1764  aRet <<= getPropertySetInfo( xEnv, false );
1765  else if ( aCommand.Name == "getCommandInfo" )
1766  aRet <<= getCommandInfo( xEnv, false );
1767  else if ( aCommand.Name == "open" )
1768  {
1769  ucb::OpenCommandArgument2 aOpenCommand;
1770  if ( !( aCommand.Argument >>= aOpenCommand ) )
1771  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1772  aRet = open( aOpenCommand, xEnv );
1773  }
1774  else if ( aCommand.Name == "transfer" )
1775  {
1776  ucb::TransferInfo transferArgs;
1777  if ( !( aCommand.Argument >>= transferArgs ) )
1778  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1779  transfer( transferArgs, xEnv );
1780  }
1781  else if ( aCommand.Name == "setPropertyValues" )
1782  {
1783  uno::Sequence< beans::PropertyValue > aProperties;
1784  if ( !( aCommand.Argument >>= aProperties ) || !aProperties.hasElements() )
1785  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1786  aRet <<= setPropertyValues( aProperties, xEnv );
1787  }
1788  else if (aCommand.Name == "createNewContent"
1789  && isFolder( xEnv ) )
1790  {
1791  ucb::ContentInfo arg;
1792  if ( !( aCommand.Argument >>= arg ) )
1793  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1794  aRet <<= createNewContent( arg );
1795  }
1796  else if ( aCommand.Name == "insert" )
1797  {
1798  ucb::InsertCommandArgument2 arg;
1799  if ( !( aCommand.Argument >>= arg ) )
1800  {
1801  ucb::InsertCommandArgument insertArg;
1802  if ( !( aCommand.Argument >>= insertArg ) )
1803  ucbhelper::cancelCommandExecution ( getBadArgExcept (), xEnv );
1804 
1805  arg.Data = insertArg.Data;
1806  arg.ReplaceExisting = insertArg.ReplaceExisting;
1807  }
1808  // store the document id
1809  m_sObjectId = arg.DocumentId;
1810  insert( arg.Data, arg.ReplaceExisting, arg.MimeType, xEnv );
1811  }
1812  else if ( aCommand.Name == "delete" )
1813  {
1814  try
1815  {
1816  if ( !isFolder( xEnv ) )
1817  {
1818  getObject( xEnv )->remove( );
1819  }
1820  else
1821  {
1822  libcmis::Folder* folder = dynamic_cast< libcmis::Folder* >( getObject( xEnv ).get() );
1823  if (folder)
1824  folder->removeTree( );
1825  }
1826  }
1827  catch ( const libcmis::Exception& e )
1828  {
1829  SAL_INFO( "ucb.ucp.cmis", "Unexpected libcmis exception: " << e.what( ) );
1831  ucb::IOErrorCode_GENERAL,
1832  uno::Sequence< uno::Any >( 0 ),
1833  xEnv,
1834  o3tl::runtimeToOUString(e.what()));
1835  }
1836  }
1837  else if ( aCommand.Name == "checkout" )
1838  {
1839  aRet <<= checkOut( xEnv );
1840  }
1841  else if ( aCommand.Name == "cancelCheckout" )
1842  {
1843  aRet <<= cancelCheckOut( xEnv );
1844  }
1845  else if ( aCommand.Name == "checkin" )
1846  {
1847  ucb::CheckinArgument aArg;
1848  if ( !( aCommand.Argument >>= aArg ) )
1849  {
1850  ucbhelper::cancelCommandExecution ( getBadArgExcept(), xEnv );
1851  }
1852  aRet <<= checkIn( aArg, xEnv );
1853  }
1854  else if ( aCommand.Name == "getAllVersions" )
1855  {
1856  aRet <<= getAllVersions( xEnv );
1857  }
1858  else if ( aCommand.Name == "updateProperties" )
1859  {
1860  updateProperties( aCommand.Argument, xEnv );
1861  }
1862  else
1863  {
1864  SAL_INFO( "ucb.ucp.cmis", "Unknown command to execute" );
1865 
1867  ( uno::makeAny( ucb::UnsupportedCommandException
1868  ( OUString(),
1869  static_cast< cppu::OWeakObject * >( this ) ) ),
1870  xEnv );
1871  }
1872 
1873  return aRet;
1874  }
1875 
1876  void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
1877  {
1878  SAL_INFO( "ucb.ucp.cmis", "TODO - Content::abort()" );
1879  // TODO Implement me
1880  }
1881 
1882  uno::Sequence< ucb::ContentInfo > SAL_CALL Content::queryCreatableContentsInfo()
1883  {
1884  return queryCreatableContentsInfo( uno::Reference< ucb::XCommandEnvironment >() );
1885  }
1886 
1887  uno::Reference< ucb::XContent > SAL_CALL Content::createNewContent(
1888  const ucb::ContentInfo& Info )
1889  {
1890  bool create_document;
1891 
1892  if ( Info.Type == CMIS_FILE_TYPE )
1893  create_document = true;
1894  else if ( Info.Type == CMIS_FOLDER_TYPE )
1895  create_document = false;
1896  else
1897  {
1898  SAL_INFO( "ucb.ucp.cmis", "Unknown type of content to create" );
1899  return uno::Reference< ucb::XContent >();
1900  }
1901 
1902  OUString sParentURL = m_xIdentifier->getContentIdentifier();
1903 
1904  // Set the parent URL for the transient objects
1905  uno::Reference< ucb::XContentIdentifier > xId(new ::ucbhelper::ContentIdentifier(sParentURL));
1906 
1907  try
1908  {
1909  return new ::cmis::Content( m_xContext, m_pProvider, xId, !create_document );
1910  }
1911  catch ( ucb::ContentCreationException & )
1912  {
1913  return uno::Reference< ucb::XContent >();
1914  }
1915  }
1916 
1917  uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
1918  {
1919  try
1920  {
1921  if ( isFolder( uno::Reference< ucb::XCommandEnvironment >() ) )
1922  {
1923  static cppu::OTypeCollection s_aFolderCollection
1924  (CPPU_TYPE_REF( lang::XTypeProvider ),
1925  CPPU_TYPE_REF( lang::XServiceInfo ),
1926  CPPU_TYPE_REF( lang::XComponent ),
1927  CPPU_TYPE_REF( ucb::XContent ),
1928  CPPU_TYPE_REF( ucb::XCommandProcessor ),
1929  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
1930  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
1931  CPPU_TYPE_REF( beans::XPropertyContainer ),
1932  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
1933  CPPU_TYPE_REF( container::XChild ),
1934  CPPU_TYPE_REF( ucb::XContentCreator ) );
1935  return s_aFolderCollection.getTypes();
1936  }
1937  }
1938  catch (const uno::RuntimeException&)
1939  {
1940  throw;
1941  }
1942  catch (const uno::Exception& e)
1943  {
1945  throw lang::WrappedTargetRuntimeException(
1946  "wrapped Exception " + e.Message,
1947  uno::Reference<uno::XInterface>(), a);
1948  }
1949 
1950  static cppu::OTypeCollection s_aFileCollection
1951  (CPPU_TYPE_REF( lang::XTypeProvider ),
1952  CPPU_TYPE_REF( lang::XServiceInfo ),
1953  CPPU_TYPE_REF( lang::XComponent ),
1954  CPPU_TYPE_REF( ucb::XContent ),
1955  CPPU_TYPE_REF( ucb::XCommandProcessor ),
1956  CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
1957  CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
1958  CPPU_TYPE_REF( beans::XPropertyContainer ),
1959  CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
1960  CPPU_TYPE_REF( container::XChild ) );
1961 
1962  return s_aFileCollection.getTypes();
1963  }
1964 
1965  uno::Sequence< ucb::ContentInfo > Content::queryCreatableContentsInfo(
1966  const uno::Reference< ucb::XCommandEnvironment >& xEnv)
1967  {
1968  try
1969  {
1970  if ( isFolder( xEnv ) )
1971  {
1972 
1973  // Minimum set of props we really need
1974  uno::Sequence< beans::Property > props
1975  {
1976  {
1977  "Title",
1978  -1,
1980  beans::PropertyAttribute::MAYBEVOID | beans::PropertyAttribute::BOUND
1981  }
1982  };
1983 
1984  return
1985  {
1986  {
1988  ( ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM |
1989  ucb::ContentInfoAttribute::KIND_DOCUMENT ),
1990  props
1991  },
1992  {
1994  ucb::ContentInfoAttribute::KIND_FOLDER,
1995  props
1996  }
1997  };
1998  }
1999  }
2000  catch (const uno::RuntimeException&)
2001  {
2002  throw;
2003  }
2004  catch (const uno::Exception& e)
2005  {
2007  throw lang::WrappedTargetRuntimeException(
2008  "wrapped Exception " + e.Message,
2009  uno::Reference<uno::XInterface>(), a);
2010  }
2011  return {};
2012  }
2013 
2014  std::vector< uno::Reference< ucb::XContent > > Content::getChildren( )
2015  {
2016  std::vector< uno::Reference< ucb::XContent > > results;
2017  SAL_INFO( "ucb.ucp.cmis", "Content::getChildren() " << m_sURL );
2018 
2019  libcmis::FolderPtr pFolder = boost::dynamic_pointer_cast< libcmis::Folder >( getObject( uno::Reference< ucb::XCommandEnvironment >() ) );
2020  if ( nullptr != pFolder )
2021  {
2022  // Get the children from pObject
2023  try
2024  {
2025  vector< libcmis::ObjectPtr > children = pFolder->getChildren( );
2026 
2027  // Loop over the results
2028  for ( const auto& rChild : children )
2029  {
2030  // TODO Cache the objects
2031 
2033  OUString sUser = aURL.GetUser( INetURLObject::DecodeMechanism::WithCharset );
2034 
2035  URL aUrl( m_sURL );
2036  OUString sPath( m_sObjectPath );
2037  if ( !sPath.endsWith("/") )
2038  sPath += "/";
2039  sPath += STD_TO_OUSTR( rChild->getName( ) );
2040  OUString sId = STD_TO_OUSTR( rChild->getId( ) );
2041 
2042  aUrl.setObjectId( sId );
2043  aUrl.setObjectPath( sPath );
2044  aUrl.setUsername( sUser );
2045 
2046  uno::Reference< ucb::XContentIdentifier > xId = new ucbhelper::ContentIdentifier( aUrl.asString( ) );
2047  uno::Reference< ucb::XContent > xContent = new Content( m_xContext, m_pProvider, xId, rChild );
2048 
2049  results.push_back( xContent );
2050  }
2051  }
2052  catch ( const libcmis::Exception& e )
2053  {
2054  SAL_INFO( "ucb.ucp.cmis", "Exception thrown: " << e.what() );
2055  }
2056  }
2057 
2058  return results;
2059  }
2060 
2061  void Content::setCmisProperty(const std::string& rName, const std::string& rValue, const uno::Reference< ucb::XCommandEnvironment >& xEnv )
2062  {
2063  if ( !getObjectType( xEnv ).get( ) )
2064  return;
2065 
2066  map< string, libcmis::PropertyPtr >::iterator propIt = m_pObjectProps.find(rName);
2067  vector< string > values;
2068  values.push_back(rValue);
2069 
2070  if ( propIt == m_pObjectProps.end( ) && getObjectType( xEnv ).get( ) )
2071  {
2072  map< string, libcmis::PropertyTypePtr > propsTypes = getObjectType( xEnv )->getPropertiesTypes( );
2073  map< string, libcmis::PropertyTypePtr >::iterator typeIt = propsTypes.find(rName);
2074 
2075  if ( typeIt != propsTypes.end( ) )
2076  {
2077  libcmis::PropertyTypePtr propType = typeIt->second;
2078  libcmis::PropertyPtr property( new libcmis::Property( propType, values ) );
2079  m_pObjectProps.insert(pair< string, libcmis::PropertyPtr >(rName, property));
2080  }
2081  }
2082  else if ( propIt != m_pObjectProps.end( ) )
2083  {
2084  propIt->second->setValues( values );
2085  }
2086  }
2087 }
2088 
2089 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool hasValue()
const OUString & getRepositoryId() const
Definition: cmis_url.hxx:31
std::vector< char * > values
#define CMIS_FOLDER_TYPE
OUString GetURLPath(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
long Long
sal_Int64 n
XTYPEPROVIDER_COMMON_IMPL(Content)
const OUString & getBindingUrl() const
Definition: cmis_url.hxx:30
OUString newName(std::u16string_view aNewPrefix, const OUString &aOldPrefix, std::u16string_view old_Name)
Definition: filglob.cxx:174
bool bReadOnly
sal_Int32 getSegmentCount(bool bIgnoreFinalSlash=true) const
css::uno::Any const & rValue
Reference< XRow > xRow
Sequence< PropertyValue > aArguments
PropertiesInfo aProperties
Any SAL_CALL getCaughtException()
int nCount
oslFileHandle & pOut
OUString GetUser(DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
OUString m_sURL
const char * sName
#define SAL_N_ELEMENTS(arr)
#define STD_TO_OUSTR(str)
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
void setUsername(const OUString &sUser)
Definition: cmis_url.cxx:53
int i
uno_Any a
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
DECL_LISTENERMULTIPLEXER_END void SAL_CALL inserted(::sal_Int32 ID) override
css::util::URL m_aURL
float u
unsigned char sal_Bool
static void convertDateTime(OUStringBuffer &rBuffer, const css::util::DateTime &rDateTime, sal_Int16 const *pTimeZoneOffset, bool bAddTimeIf0AM=false)
dictionary props
static OUString GetScheme(INetProtocol eTheScheme)
OUString asString()
Definition: cmis_url.cxx:58
OUString aURL
css::uno::Type const & get()
Implements a OutputStream working on an std::ostream.
#define CMIS_TYPE_BOOL
#define OUSTR_TO_STDSTR(s)
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
const PropertyValue * pValues
css::uno::Reference< css::io::XInputStream > openStream()
OUString runtimeToOUString(char const *runtimeString)
std::unique_ptr< char[]> aBuffer
void setObjectId(const OUString &sId)
Definition: cmis_url.cxx:48
const OUString & getUsername() const
Definition: cmis_url.hxx:32
exports com.sun.star. document
#define CMIS_TYPE_DATETIME
Any makeAny(Color const &value)
#define CPPU_TYPE_REF(T)
const int TRANSFER_BUFFER_SIZE
#define CMIS_FILE_TYPE
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
#define SAL_INFO(area, stream)
Reference< XComponentContext > getProcessComponentContext()
const char * name
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
#define CMIS_TYPE_STRING
Any value
void setObjectPath(const OUString &sPath)
Definition: cmis_url.cxx:43
friend ContentProvider
Definition: pkgprovider.cxx:53
#define CMIS_TYPE_DECIMAL
Reference< XContentIdentifier > xId
OUString sId
uno::Reference< ucb::XContent > xContent
ResultType type
Reference< XComponentContext > m_xContext
#define CMIS_TYPE_INTEGER
InternetProxyServer getProxy(const OUString &rProtocol, const OUString &rHost, sal_Int32 nPort) const
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)
sal_uInt16 nPos
sal_Int16 nValue
bool removeSegment(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true)
OUString sDisplayName