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