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