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