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