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