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