LibreOffice Module ucb (master) 1
tdoc_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 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20
21/**************************************************************************
22 TODO
23 **************************************************************************
24
25 *************************************************************************/
26
27#include <sal/config.h>
28
29#include <string_view>
30
31#include <o3tl/string_view.hxx>
33#include <rtl/ustrbuf.hxx>
34#include <com/sun/star/beans/IllegalTypeException.hpp>
35#include <com/sun/star/beans/PropertyAttribute.hpp>
36#include <com/sun/star/beans/XPropertySet.hpp>
37#include <com/sun/star/embed/InvalidStorageException.hpp>
38#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
39#include <com/sun/star/embed/XTransactedObject.hpp>
40#include <com/sun/star/io/BufferSizeExceededException.hpp>
41#include <com/sun/star/io/IOException.hpp>
42#include <com/sun/star/io/NotConnectedException.hpp>
43#include <com/sun/star/io/XActiveDataSink.hpp>
44#include <com/sun/star/io/XActiveDataStreamer.hpp>
45#include <com/sun/star/lang/IllegalAccessException.hpp>
46#include <com/sun/star/packages/WrongPasswordException.hpp>
47#include <com/sun/star/task/DocumentPasswordRequest.hpp>
48#include <com/sun/star/task/XInteractionPassword.hpp>
49#include <com/sun/star/ucb/CommandFailedException.hpp>
50#include <com/sun/star/ucb/ContentAction.hpp>
51#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
52#include <com/sun/star/ucb/IllegalIdentifierException.hpp>
53#include <com/sun/star/ucb/InsertCommandArgument.hpp>
54#include <com/sun/star/ucb/InteractiveBadTransferURLException.hpp>
55#include <com/sun/star/ucb/MissingInputStreamException.hpp>
56#include <com/sun/star/ucb/NameClash.hpp>
57#include <com/sun/star/ucb/NameClashException.hpp>
58#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
59#include <com/sun/star/ucb/OpenMode.hpp>
60#include <com/sun/star/ucb/TransferInfo.hpp>
61#include <com/sun/star/ucb/UnsupportedCommandException.hpp>
62#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
63#include <com/sun/star/ucb/UnsupportedNameClashException.hpp>
64#include <com/sun/star/ucb/UnsupportedOpenModeException.hpp>
65#include <com/sun/star/ucb/XCommandInfo.hpp>
66#include <com/sun/star/ucb/XPersistentPropertySet.hpp>
67
73#include <ucbhelper/macros.hxx>
74#include <utility>
75
76#include "tdoc_content.hxx"
77#include "tdoc_resultset.hxx"
79
80#include "../inc/urihelper.hxx"
81
82using namespace com::sun::star;
83using namespace tdoc_ucp;
84
85
86static ContentType lcl_getContentType( std::u16string_view rType )
87{
88 if ( rType == TDOC_ROOT_CONTENT_TYPE )
89 return ROOT;
90 else if ( rType == TDOC_DOCUMENT_CONTENT_TYPE )
91 return DOCUMENT;
92 else if ( rType == TDOC_FOLDER_CONTENT_TYPE )
93 return FOLDER;
94 else if ( rType == TDOC_STREAM_CONTENT_TYPE )
95 return STREAM;
96 else
97 {
98 OSL_FAIL( "Content::Content - unsupported content type string" );
99 return STREAM;
100 }
101}
102
103
104// Content Implementation.
105
106
107// static ( "virtual" ctor )
108rtl::Reference<Content> Content::create(
109 const uno::Reference< uno::XComponentContext >& rxContext,
110 ContentProvider* pProvider,
111 const uno::Reference< ucb::XContentIdentifier >& Identifier )
112{
113 // Fail, if resource does not exist.
114 ContentProperties aProps;
115 if ( !Content::loadData( pProvider,
116 Uri( Identifier->getContentIdentifier() ),
117 aProps ) )
118 return nullptr;
119
120 return new Content( rxContext, pProvider, Identifier, std::move(aProps) );
121}
122
123
124// static ( "virtual" ctor )
126 const uno::Reference< uno::XComponentContext >& rxContext,
127 ContentProvider* pProvider,
128 const uno::Reference< ucb::XContentIdentifier >& Identifier,
129 const ucb::ContentInfo& Info )
130{
131 if ( Info.Type.isEmpty() )
132 return nullptr;
133
135 {
136 OSL_FAIL( "Content::create - unsupported content type!" );
137 return nullptr;
138 }
139
140 return new Content( rxContext, pProvider, Identifier, Info );
141}
142
143
145 const uno::Reference< uno::XComponentContext > & rxContext,
146 ContentProvider * pProvider,
147 const uno::Reference< ucb::XContentIdentifier > & Identifier,
148 ContentProperties aProps )
149: ContentImplHelper( rxContext, pProvider, Identifier ),
150 m_aProps(std::move( aProps )),
151 m_eState( PERSISTENT ),
152 m_pProvider( pProvider )
153{
154}
155
156
157// ctor for a content just created via XContentCreator::createNewContent()
158Content::Content(
159 const uno::Reference< uno::XComponentContext >& rxContext,
160 ContentProvider* pProvider,
161 const uno::Reference< ucb::XContentIdentifier >& Identifier,
162 const ucb::ContentInfo& Info )
163 : ContentImplHelper( rxContext, pProvider, Identifier ),
164 m_aProps( lcl_getContentType( Info.Type ), OUString() ), // no Title (yet)
165 m_eState( TRANSIENT ),
166 m_pProvider( pProvider )
167{
168}
169
170
171// virtual
172Content::~Content()
173{
174}
175
176
177// XInterface methods.
178
179
180// virtual
181void SAL_CALL Content::acquire()
182 noexcept
183{
184 ContentImplHelper::acquire();
185}
186
187
188// virtual
189void SAL_CALL Content::release()
190 noexcept
191{
192 ContentImplHelper::release();
193}
194
195
196// virtual
197uno::Any SAL_CALL Content::queryInterface( const uno::Type & rType )
198{
199 uno::Any aRet = ContentImplHelper::queryInterface( rType );
200
201 if ( !aRet.hasValue() )
202 {
204 rType, static_cast< ucb::XContentCreator * >( this ) );
205 if ( aRet.hasValue() )
206 {
207 if ( !m_aProps.isContentCreator() )
208 return uno::Any();
209 }
210 }
211
212 return aRet;
213}
214
215
216// XTypeProvider methods.
217
218
220
221
222// virtual
223uno::Sequence< uno::Type > SAL_CALL Content::getTypes()
224{
226 {
227 static cppu::OTypeCollection s_aFolderTypes(
228 CPPU_TYPE_REF( lang::XTypeProvider ),
229 CPPU_TYPE_REF( lang::XServiceInfo ),
230 CPPU_TYPE_REF( lang::XComponent ),
231 CPPU_TYPE_REF( ucb::XContent ),
232 CPPU_TYPE_REF( ucb::XCommandProcessor ),
233 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
234 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
235 CPPU_TYPE_REF( beans::XPropertyContainer ),
236 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
237 CPPU_TYPE_REF( container::XChild ),
238 CPPU_TYPE_REF( ucb::XContentCreator ) );
239
240 return s_aFolderTypes.getTypes();
241 }
242 else
243 {
244 static cppu::OTypeCollection s_aDocumentTypes(
245 CPPU_TYPE_REF( lang::XTypeProvider ),
246 CPPU_TYPE_REF( lang::XServiceInfo ),
247 CPPU_TYPE_REF( lang::XComponent ),
248 CPPU_TYPE_REF( ucb::XContent ),
249 CPPU_TYPE_REF( ucb::XCommandProcessor ),
250 CPPU_TYPE_REF( beans::XPropertiesChangeNotifier ),
251 CPPU_TYPE_REF( ucb::XCommandInfoChangeNotifier ),
252 CPPU_TYPE_REF( beans::XPropertyContainer ),
253 CPPU_TYPE_REF( beans::XPropertySetInfoChangeNotifier ),
254 CPPU_TYPE_REF( container::XChild ) );
255
256 return s_aDocumentTypes.getTypes();
257 }
258}
259
260
261// XServiceInfo methods.
262
263
264// virtual
266{
267 return "com.sun.star.comp.ucb.TransientDocumentsContent";
268}
269
270
271// virtual
272uno::Sequence< OUString > SAL_CALL Content::getSupportedServiceNames()
273{
274 osl::Guard< osl::Mutex > aGuard( m_aMutex );
275
276 uno::Sequence< OUString > aSNS( 1 );
277
278 if ( m_aProps.getType() == STREAM )
279 aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsStreamContent";
280 else if ( m_aProps.getType() == FOLDER )
281 aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsFolderContent";
282 else if ( m_aProps.getType() == DOCUMENT )
283 aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsDocumentContent";
284 else
285 aSNS.getArray()[ 0 ] = "com.sun.star.ucb.TransientDocumentsRootContent";
286
287 return aSNS;
288}
289
290
291// XContent methods.
292
293
294// virtual
295OUString SAL_CALL Content::getContentType()
296{
297 osl::Guard< osl::Mutex > aGuard( m_aMutex );
298 return m_aProps.getContentType();
299}
300
301
302// virtual
303uno::Reference< ucb::XContentIdentifier > SAL_CALL
305{
306 {
307 osl::Guard< osl::Mutex > aGuard( m_aMutex );
308
309 // Transient?
310 if ( m_eState == TRANSIENT )
311 {
312 // Transient contents have no identifier.
313 return uno::Reference< ucb::XContentIdentifier >();
314 }
315 }
316 return ContentImplHelper::getIdentifier();
317}
318
319
320// XCommandProcessor methods.
321
322
323// virtual
325 const ucb::Command& aCommand,
326 sal_Int32 /*CommandId*/,
327 const uno::Reference< ucb::XCommandEnvironment >& Environment )
328{
329 uno::Any aRet;
330
331 if ( aCommand.Name == "getPropertyValues" )
332 {
333
334 // getPropertyValues
335
336
337 uno::Sequence< beans::Property > Properties;
338 if ( !( aCommand.Argument >>= Properties ) )
339 {
341 uno::Any( lang::IllegalArgumentException(
342 "Wrong argument type!",
343 getXWeak(),
344 -1 ) ),
345 Environment );
346 // Unreachable
347 }
348
349 aRet <<= getPropertyValues( Properties );
350 }
351 else if ( aCommand.Name == "setPropertyValues" )
352 {
353
354 // setPropertyValues
355
356
357 uno::Sequence< beans::PropertyValue > aProperties;
358 if ( !( aCommand.Argument >>= aProperties ) )
359 {
361 uno::Any( lang::IllegalArgumentException(
362 "Wrong argument type!",
363 getXWeak(),
364 -1 ) ),
365 Environment );
366 // Unreachable
367 }
368
369 if ( !aProperties.hasElements() )
370 {
372 uno::Any( lang::IllegalArgumentException(
373 "No properties!",
374 getXWeak(),
375 -1 ) ),
376 Environment );
377 // Unreachable
378 }
379
380 aRet <<= setPropertyValues( aProperties, Environment );
381 }
382 else if ( aCommand.Name == "getPropertySetInfo" )
383 {
384
385 // getPropertySetInfo
386
387
388 aRet <<= getPropertySetInfo( Environment );
389 }
390 else if ( aCommand.Name == "getCommandInfo" )
391 {
392
393 // getCommandInfo
394
395
396 aRet <<= getCommandInfo( Environment );
397 }
398 else if ( aCommand.Name == "open" )
399 {
400
401 // open
402
403
404 ucb::OpenCommandArgument2 aOpenCommand;
405 if ( !( aCommand.Argument >>= aOpenCommand ) )
406 {
408 uno::Any( lang::IllegalArgumentException(
409 "Wrong argument type!",
410 getXWeak(),
411 -1 ) ),
412 Environment );
413 // Unreachable
414 }
415
416 aRet = open( aOpenCommand, Environment );
417 }
418 else if ( aCommand.Name == "insert" )
419 {
420
421 // insert ( Supported by folders and streams only )
422
423
425 if ( ( eType != FOLDER ) && ( eType != STREAM ) )
426 {
428 uno::Any( ucb::UnsupportedCommandException(
429 "insert command only supported by "
430 "folders and streams!",
431 getXWeak() ) ),
432 Environment );
433 // Unreachable
434 }
435
436 if ( eType == STREAM )
437 {
438 Uri aUri( m_xIdentifier->getContentIdentifier() );
439 Uri aParentUri( aUri.getParentUri() );
440 if ( aParentUri.isDocument() )
441 {
443 uno::Any( ucb::UnsupportedCommandException(
444 "insert command not supported by "
445 "streams that are direct children "
446 "of document root!",
447 getXWeak() ) ),
448 Environment );
449 // Unreachable
450 }
451 }
452
453 ucb::InsertCommandArgument aArg;
454 if ( !( aCommand.Argument >>= aArg ) )
455 {
457 uno::Any( lang::IllegalArgumentException(
458 "Wrong argument type!",
459 getXWeak(),
460 -1 ) ),
461 Environment );
462 // Unreachable
463 }
464
465 sal_Int32 nNameClash = aArg.ReplaceExisting
466 ? ucb::NameClash::OVERWRITE
467 : ucb::NameClash::ERROR;
468 insert( aArg.Data, nNameClash, Environment );
469 }
470 else if ( aCommand.Name == "delete" )
471 {
472
473 // delete ( Supported by folders and streams only )
474
475
476 {
477 osl::MutexGuard aGuard( m_aMutex );
478
480 if ( ( eType != FOLDER ) && ( eType != STREAM ) )
481 {
483 uno::Any( ucb::UnsupportedCommandException(
484 "delete command only supported by "
485 "folders and streams!",
486 getXWeak() ) ),
487 Environment );
488 // Unreachable
489 }
490 }
491
492 bool bDeletePhysical = false;
493 aCommand.Argument >>= bDeletePhysical;
494 destroy( bDeletePhysical, Environment );
495
496 // Remove own and all children's persistent data.
497 if ( !removeData() )
498 {
499 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
500 {
501 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
502 }));
504 ucb::IOErrorCode_CANT_WRITE,
505 aArgs,
506 Environment,
507 "Cannot remove persistent data!",
508 this );
509 // Unreachable
510 }
511
512 // Remove own and all children's Additional Core Properties.
514 }
515 else if ( aCommand.Name == "transfer" )
516 {
517
518 // transfer ( Supported by document and folders only )
519
520
521 {
522 osl::MutexGuard aGuard( m_aMutex );
523
525 if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
526 {
528 uno::Any( ucb::UnsupportedCommandException(
529 "transfer command only supported "
530 "by folders and documents!",
531 getXWeak() ) ),
532 Environment );
533 // Unreachable
534 }
535 }
536
537 ucb::TransferInfo aInfo;
538 if ( !( aCommand.Argument >>= aInfo ) )
539 {
540 OSL_FAIL( "Wrong argument type!" );
542 uno::Any( lang::IllegalArgumentException(
543 "Wrong argument type!",
544 getXWeak(),
545 -1 ) ),
546 Environment );
547 // Unreachable
548 }
549
550 transfer( aInfo, Environment );
551 }
552 else if ( aCommand.Name == "createNewContent" )
553 {
554
555 // createNewContent ( Supported by document and folders only )
556
557
558 {
559 osl::MutexGuard aGuard( m_aMutex );
560
562 if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
563 {
565 uno::Any( ucb::UnsupportedCommandException(
566 "createNewContent command only "
567 "supported by folders and "
568 "documents!",
569 getXWeak() ) ),
570 Environment );
571 // Unreachable
572 }
573 }
574
575 ucb::ContentInfo aInfo;
576 if ( !( aCommand.Argument >>= aInfo ) )
577 {
578 OSL_FAIL( "Wrong argument type!" );
580 uno::Any( lang::IllegalArgumentException(
581 "Wrong argument type!",
582 getXWeak(),
583 -1 ) ),
584 Environment );
585 // Unreachable
586 }
587
588 aRet <<= createNewContent( aInfo );
589 }
590 else
591 {
592
593 // Unsupported command
594
595
597 uno::Any( ucb::UnsupportedCommandException(
598 OUString(),
599 getXWeak() ) ),
600 Environment );
601 // Unreachable
602 }
603
604 return aRet;
605}
606
607
608// virtual
609void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
610{
611}
612
613
614// XContentCreator methods.
615
616
617// virtual
618uno::Sequence< ucb::ContentInfo > SAL_CALL
620{
622}
623
624
625// virtual
626uno::Reference< ucb::XContent > SAL_CALL
627Content::createNewContent( const ucb::ContentInfo& Info )
628{
630 {
631 osl::Guard< osl::Mutex > aGuard( m_aMutex );
632
633 if ( Info.Type.isEmpty() )
634 return uno::Reference< ucb::XContent >();
635
636 bool bCreateFolder = Info.Type == TDOC_FOLDER_CONTENT_TYPE;
637
638 // streams cannot be created as direct children of document root
639 if ( !bCreateFolder && ( m_aProps.getType() == DOCUMENT ) )
640 {
641 OSL_FAIL( "Content::createNewContent - streams cannot be "
642 "created as direct children of document root!" );
643 return uno::Reference< ucb::XContent >();
644 }
645 if ( !bCreateFolder && Info.Type != TDOC_STREAM_CONTENT_TYPE )
646 {
647 OSL_FAIL( "Content::createNewContent - unsupported type!" );
648 return uno::Reference< ucb::XContent >();
649 }
650
651 OUString aURL = m_xIdentifier->getContentIdentifier();
652
653 OSL_ENSURE( !aURL.isEmpty(),
654 "Content::createNewContent - empty identifier!" );
655
656 if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
657 aURL += "/";
658
659 if ( bCreateFolder )
660 aURL += "New_Folder";
661 else
662 aURL += "New_Stream";
663
664 uno::Reference< ucb::XContentIdentifier > xId
665 = new ::ucbhelper::ContentIdentifier( aURL );
666
667 return create( m_xContext, m_pProvider, xId, Info );
668 }
669 else
670 {
671 OSL_FAIL( "createNewContent called on non-contentcreator object!" );
672 return uno::Reference< ucb::XContent >();
673 }
674}
675
676
677// virtual
679{
680 osl::Guard< osl::Mutex > aGuard( m_aMutex );
681 Uri aUri( m_xIdentifier->getContentIdentifier() );
682 return aUri.getParentUri();
683}
684
685
686uno::Reference< ucb::XContentIdentifier >
687Content::makeNewIdentifier( const OUString& rTitle )
688{
689 osl::Guard< osl::Mutex > aGuard( m_aMutex );
690
691 // Assemble new content identifier...
692 Uri aUri( m_xIdentifier->getContentIdentifier() );
693 OUString aNewURL = aUri.getParentUri() + ::ucb_impl::urihelper::encodeSegment( rTitle );
694
695 return
696 uno::Reference< ucb::XContentIdentifier >(
697 new ::ucbhelper::ContentIdentifier( aNewURL ) );
698}
699
700
702{
703 osl::Guard< osl::Mutex > aGuard( m_aMutex );
704
705 // Only folders (root, documents, folders) have children.
706 if ( !m_aProps.getIsFolder() )
707 return;
708
709 // Obtain a list with a snapshot of all currently instantiated contents
710 // from provider and extract the contents which are direct children
711 // of this content.
712
713 ::ucbhelper::ContentRefList aAllContents;
714 m_xProvider->queryExistingContents( aAllContents );
715
716 OUString aURL = m_xIdentifier->getContentIdentifier();
717 sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
718
719 if ( nURLPos != ( aURL.getLength() - 1 ) )
720 {
721 // No trailing slash found. Append.
722 aURL += "/";
723 }
724
725 sal_Int32 nLen = aURL.getLength();
726
727 for ( const auto& rContent : aAllContents )
728 {
729 ::ucbhelper::ContentImplHelperRef xChild = rContent;
730 OUString aChildURL
731 = xChild->getIdentifier()->getContentIdentifier();
732
733 // Is aURL a prefix of aChildURL?
734 if ( ( aChildURL.getLength() > nLen ) &&
735 ( aChildURL.startsWith( aURL ) ) )
736 {
737 sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
738
739 if ( ( nPos == -1 ) ||
740 ( nPos == ( aChildURL.getLength() - 1 ) ) )
741 {
742 // No further slashes / only a final slash. It's a child!
743 rChildren.emplace_back(
744 static_cast< Content * >( xChild.get() ) );
745 }
746 }
747 }
748}
749
750
752 const uno::Reference< ucb::XContentIdentifier >& xNewId )
753{
754 if ( !xNewId.is() )
755 return false;
756
757 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
758
759 uno::Reference< ucb::XContent > xThis = this;
760
761 // Already persistent?
762 if ( m_eState != PERSISTENT )
763 {
764 OSL_FAIL( "Content::exchangeIdentity - Not persistent!" );
765 return false;
766 }
767
768 // Only folders and streams can be renamed -> exchange identity.
770 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
771 {
772 OSL_FAIL( "Content::exchangeIdentity - "
773 "Not supported by root or document!" );
774 return false;
775 }
776
777 // Exchange own identity.
778
779 // Fail, if a content with given id already exists.
780 if ( !hasData( Uri( xNewId->getContentIdentifier() ) ) )
781 {
782 OUString aOldURL = m_xIdentifier->getContentIdentifier();
783
784 aGuard.clear();
785 if ( exchange( xNewId ) )
786 {
787 if ( eType == FOLDER )
788 {
789 // Process instantiated children...
790
791 ContentRefList aChildren;
792 queryChildren( aChildren );
793
794 for ( const auto& rChild : aChildren )
795 {
796 ContentRef xChild = rChild;
797
798 // Create new content identifier for the child...
799 uno::Reference< ucb::XContentIdentifier > xOldChildId
800 = xChild->getIdentifier();
801 OUString aOldChildURL
802 = xOldChildId->getContentIdentifier();
803 OUString aNewChildURL
804 = aOldChildURL.replaceAt(
805 0,
806 aOldURL.getLength(),
807 xNewId->getContentIdentifier() );
808 uno::Reference< ucb::XContentIdentifier > xNewChildId
809 = new ::ucbhelper::ContentIdentifier( aNewChildURL );
810
811 if ( !xChild->exchangeIdentity( xNewChildId ) )
812 return false;
813 }
814 }
815 return true;
816 }
817 }
818
819 OSL_FAIL( "Content::exchangeIdentity - "
820 "Panic! Cannot exchange identity!" );
821 return false;
822}
823
824
825// static
826uno::Reference< sdbc::XRow > Content::getPropertyValues(
827 const uno::Reference< uno::XComponentContext >& rxContext,
828 const uno::Sequence< beans::Property >& rProperties,
829 ContentProvider* pProvider,
830 const OUString& rContentId )
831{
833 if ( loadData( pProvider, Uri(rContentId), aData ) )
834 {
835 return getPropertyValues(
836 rxContext, rProperties, aData, pProvider, rContentId );
837 }
838 else
839 {
841 = new ::ucbhelper::PropertyValueSet( rxContext );
842
843 for ( const beans::Property& rProp : rProperties )
844 xRow->appendVoid( rProp );
845
846 return xRow;
847 }
848}
849
850
851// static
852uno::Reference< sdbc::XRow > Content::getPropertyValues(
853 const uno::Reference< uno::XComponentContext >& rxContext,
854 const uno::Sequence< beans::Property >& rProperties,
855 const ContentProperties& rData,
856 ContentProvider* pProvider,
857 const OUString& rContentId )
858{
859 // Note: Empty sequence means "get values of all supported properties".
860
862 = new ::ucbhelper::PropertyValueSet( rxContext );
863
864 if ( rProperties.hasElements() )
865 {
866 uno::Reference< beans::XPropertySet > xAdditionalPropSet;
867 bool bTriedToGetAdditionalPropSet = false;
868
869 for ( const beans::Property& rProp : rProperties )
870 {
871 // Process Core properties.
872
873 if ( rProp.Name == "ContentType" )
874 {
875 xRow->appendString ( rProp, rData.getContentType() );
876 }
877 else if ( rProp.Name == "Title" )
878 {
879 xRow->appendString ( rProp, rData.getTitle() );
880 }
881 else if ( rProp.Name == "IsDocument" )
882 {
883 xRow->appendBoolean( rProp, rData.getIsDocument() );
884 }
885 else if ( rProp.Name == "IsFolder" )
886 {
887 xRow->appendBoolean( rProp, rData.getIsFolder() );
888 }
889 else if ( rProp.Name == "CreatableContentsInfo" )
890 {
891 xRow->appendObject(
892 rProp, uno::Any( rData.getCreatableContentsInfo() ) );
893 }
894 else if ( rProp.Name == "DateModified" )
895 {
896 // DateModified is only supported by streams.
897 ContentType eType = rData.getType();
898 if ( eType == STREAM )
899 {
900 xRow->appendObject(
901 rProp,
902 uno::Any(
903 pProvider->queryStreamDateModified( rContentId ) ) );
904 }
905 else
906 xRow->appendVoid( rProp );
907 }
908 else if ( rProp.Name == "Storage" )
909 {
910 // Storage is only supported by folders.
911 ContentType eType = rData.getType();
912 if ( eType == FOLDER )
913 xRow->appendObject(
914 rProp,
915 uno::Any(
916 pProvider->queryStorageClone( rContentId ) ) );
917 else
918 xRow->appendVoid( rProp );
919 }
920 else if ( rProp.Name == "DocumentModel" )
921 {
922 // DocumentModel is only supported by documents.
923 ContentType eType = rData.getType();
924 if ( eType == DOCUMENT )
925 xRow->appendObject(
926 rProp,
927 uno::Any(
928 pProvider->queryDocumentModel( rContentId ) ) );
929 else
930 xRow->appendVoid( rProp );
931 }
932 else
933 {
934 // Not a Core Property! Maybe it's an Additional Core Property?!
935
936 if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
937 {
938 xAdditionalPropSet =
939 pProvider->getAdditionalPropertySet( rContentId,
940 false );
941 bTriedToGetAdditionalPropSet = true;
942 }
943
944 if ( xAdditionalPropSet.is() )
945 {
946 if ( !xRow->appendPropertySetValue(
947 xAdditionalPropSet,
948 rProp ) )
949 {
950 // Append empty entry.
951 xRow->appendVoid( rProp );
952 }
953 }
954 else
955 {
956 // Append empty entry.
957 xRow->appendVoid( rProp );
958 }
959 }
960 }
961 }
962 else
963 {
964 // Append all Core Properties.
965 xRow->appendString (
966 beans::Property( "ContentType",
967 -1,
969 beans::PropertyAttribute::BOUND
970 | beans::PropertyAttribute::READONLY ),
971 rData.getContentType() );
972
973 ContentType eType = rData.getType();
974
975 xRow->appendString (
976 beans::Property( "Title",
977 -1,
979 // Title is read-only for root and documents.
980 beans::PropertyAttribute::BOUND |
981 ( ( eType == ROOT ) || ( eType == DOCUMENT )
982 ? beans::PropertyAttribute::READONLY
983 : 0 ) ),
984 rData.getTitle() );
985 xRow->appendBoolean(
986 beans::Property( "IsDocument",
987 -1,
989 beans::PropertyAttribute::BOUND
990 | beans::PropertyAttribute::READONLY ),
991 rData.getIsDocument() );
992 xRow->appendBoolean(
993 beans::Property( "IsFolder",
994 -1,
996 beans::PropertyAttribute::BOUND
997 | beans::PropertyAttribute::READONLY ),
998 rData.getIsFolder() );
999 xRow->appendObject(
1000 beans::Property(
1001 "CreatableContentsInfo",
1002 -1,
1003 cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
1004 beans::PropertyAttribute::BOUND
1005 | beans::PropertyAttribute::READONLY ),
1007
1008 // DateModified is only supported by streams.
1009 if ( eType == STREAM )
1010 {
1011 xRow->appendObject(
1012 beans::Property( "DateModified",
1013 -1,
1015 beans::PropertyAttribute::BOUND
1016 | beans::PropertyAttribute::READONLY ),
1017 uno::Any( pProvider->queryStreamDateModified( rContentId ) ) );
1018 }
1019
1020 // Storage is only supported by folders.
1021 if ( eType == FOLDER )
1022 xRow->appendObject(
1023 beans::Property( "Storage",
1024 -1,
1026 beans::PropertyAttribute::BOUND
1027 | beans::PropertyAttribute::READONLY ),
1028 uno::Any( pProvider->queryStorageClone( rContentId ) ) );
1029
1030 // DocumentModel is only supported by documents.
1031 if ( eType == DOCUMENT )
1032 xRow->appendObject(
1033 beans::Property( "DocumentModel",
1034 -1,
1036 beans::PropertyAttribute::BOUND
1037 | beans::PropertyAttribute::READONLY ),
1038 uno::Any(
1039 pProvider->queryDocumentModel( rContentId ) ) );
1040
1041 // Append all Additional Core Properties.
1042
1043 uno::Reference< beans::XPropertySet > xSet =
1044 pProvider->getAdditionalPropertySet( rContentId, false );
1045 xRow->appendPropertySet( xSet );
1046 }
1047
1048 return xRow;
1049}
1050
1051
1052uno::Reference< sdbc::XRow > Content::getPropertyValues(
1053 const uno::Sequence< beans::Property >& rProperties )
1054{
1055 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1057 rProperties,
1058 m_aProps,
1060 m_xIdentifier->getContentIdentifier() );
1061}
1062
1063
1064uno::Sequence< uno::Any > Content::setPropertyValues(
1065 const uno::Sequence< beans::PropertyValue >& rValues,
1066 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1067{
1068 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1069
1070 uno::Sequence< uno::Any > aRet( rValues.getLength() );
1071 auto aRetRange = asNonConstRange(aRet);
1072 uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
1073 sal_Int32 nChanged = 0;
1074
1075 beans::PropertyChangeEvent aEvent;
1076 aEvent.Source = getXWeak();
1077 aEvent.Further = false;
1078 // aEvent.PropertyName =
1079 aEvent.PropertyHandle = -1;
1080 // aEvent.OldValue =
1081 // aEvent.NewValue =
1082
1083 const beans::PropertyValue* pValues = rValues.getConstArray();
1084 sal_Int32 nCount = rValues.getLength();
1085
1086 uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
1087 bool bTriedToGetAdditionalPropSet = false;
1088
1089 bool bExchange = false;
1090 OUString aOldTitle;
1091 sal_Int32 nTitlePos = -1;
1092
1093 for ( sal_Int32 n = 0; n < nCount; ++n )
1094 {
1095 const beans::PropertyValue& rValue = pValues[ n ];
1096
1097 if ( rValue.Name == "ContentType" )
1098 {
1099 // Read-only property!
1100 aRetRange[ n ] <<= lang::IllegalAccessException(
1101 "Property is read-only!",
1102 getXWeak() );
1103 }
1104 else if ( rValue.Name == "IsDocument" )
1105 {
1106 // Read-only property!
1107 aRetRange[ n ] <<= lang::IllegalAccessException(
1108 "Property is read-only!",
1109 getXWeak() );
1110 }
1111 else if ( rValue.Name == "IsFolder" )
1112 {
1113 // Read-only property!
1114 aRetRange[ n ] <<= lang::IllegalAccessException(
1115 "Property is read-only!",
1116 getXWeak() );
1117 }
1118 else if ( rValue.Name == "CreatableContentsInfo" )
1119 {
1120 // Read-only property!
1121 aRetRange[ n ] <<= lang::IllegalAccessException(
1122 "Property is read-only!",
1123 getXWeak() );
1124 }
1125 else if ( rValue.Name == "Title" )
1126 {
1127 // Title is read-only for root and documents.
1129 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
1130 {
1131 aRetRange[ n ] <<= lang::IllegalAccessException(
1132 "Property is read-only!",
1133 getXWeak() );
1134 }
1135 else
1136 {
1137 OUString aNewValue;
1138 if ( rValue.Value >>= aNewValue )
1139 {
1140 // No empty titles!
1141 if ( !aNewValue.isEmpty() )
1142 {
1143 if ( aNewValue != m_aProps.getTitle() )
1144 {
1145 // modified title -> modified URL -> exchange !
1146 if ( m_eState == PERSISTENT )
1147 bExchange = true;
1148
1149 aOldTitle = m_aProps.getTitle();
1150 m_aProps.setTitle( aNewValue );
1151
1152 // property change event will be sent later...
1153
1154 // remember position within sequence of values
1155 // (for error handling).
1156 nTitlePos = n;
1157 }
1158 }
1159 else
1160 {
1161 aRetRange[ n ] <<= lang::IllegalArgumentException(
1162 "Empty Title not allowed!",
1163 getXWeak(),
1164 -1 );
1165 }
1166 }
1167 else
1168 {
1169 aRetRange[ n ] <<= beans::IllegalTypeException(
1170 "Title Property value has wrong type!",
1171 getXWeak() );
1172 }
1173 }
1174 }
1175 else if ( rValue.Name == "Storage" )
1176 {
1178 if ( eType == FOLDER )
1179 {
1180 aRetRange[ n ] <<= lang::IllegalAccessException(
1181 "Property is read-only!",
1182 getXWeak() );
1183 }
1184 else
1185 {
1186 // Storage is only supported by folders.
1187 aRetRange[ n ] <<= beans::UnknownPropertyException(
1188 "Storage property only supported by folders",
1189 getXWeak() );
1190 }
1191 }
1192 else if ( rValue.Name == "DocumentModel" )
1193 {
1195 if ( eType == DOCUMENT )
1196 {
1197 aRetRange[ n ] <<= lang::IllegalAccessException(
1198 "Property is read-only!",
1199 getXWeak() );
1200 }
1201 else
1202 {
1203 // Storage is only supported by folders.
1204 aRetRange[ n ] <<= beans::UnknownPropertyException(
1205 "DocumentModel property only supported by documents",
1206 getXWeak() );
1207 }
1208 }
1209 else
1210 {
1211 // Not a Core Property! Maybe it's an Additional Core Property?!
1212
1213 if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
1214 {
1215 xAdditionalPropSet = getAdditionalPropertySet( false );
1216 bTriedToGetAdditionalPropSet = true;
1217 }
1218
1219 if ( xAdditionalPropSet.is() )
1220 {
1221 try
1222 {
1223 uno::Any aOldValue = xAdditionalPropSet->getPropertyValue(
1224 rValue.Name );
1225 if ( aOldValue != rValue.Value )
1226 {
1227 xAdditionalPropSet->setPropertyValue(
1228 rValue.Name, rValue.Value );
1229
1230 aEvent.PropertyName = rValue.Name;
1231 aEvent.OldValue = aOldValue;
1232 aEvent.NewValue = rValue.Value;
1233
1234 aChanges.getArray()[ nChanged ] = aEvent;
1235 nChanged++;
1236 }
1237 }
1238 catch ( beans::UnknownPropertyException const & e )
1239 {
1240 aRetRange[ n ] <<= e;
1241 }
1242 catch ( lang::WrappedTargetException const & e )
1243 {
1244 aRetRange[ n ] <<= e;
1245 }
1246 catch ( beans::PropertyVetoException const & e )
1247 {
1248 aRetRange[ n ] <<= e;
1249 }
1250 catch ( lang::IllegalArgumentException const & e )
1251 {
1252 aRetRange[ n ] <<= e;
1253 }
1254 }
1255 else
1256 {
1257 aRetRange[ n ] <<= uno::Exception(
1258 "No property set for storing the value!",
1259 getXWeak() );
1260 }
1261 }
1262 }
1263
1264 if ( bExchange )
1265 {
1266 uno::Reference< ucb::XContentIdentifier > xOldId
1267 = m_xIdentifier;
1268 uno::Reference< ucb::XContentIdentifier > xNewId
1270
1271 aGuard.clear();
1272 if ( exchangeIdentity( xNewId ) )
1273 {
1274 // Adapt persistent data.
1275 renameData( xOldId, xNewId );
1276
1277 // Adapt Additional Core Properties.
1278 renameAdditionalPropertySet( xOldId->getContentIdentifier(),
1279 xNewId->getContentIdentifier() );
1280 }
1281 else
1282 {
1283 // Roll-back.
1284 m_aProps.setTitle( aOldTitle );
1285 aOldTitle.clear();
1286
1287 // Set error .
1288 aRetRange[ nTitlePos ] <<= uno::Exception(
1289 "Exchange failed!",
1290 getXWeak() );
1291 }
1292 }
1293
1294 if ( !aOldTitle.isEmpty() )
1295 {
1296 aEvent.PropertyName = "Title";
1297 aEvent.OldValue <<= aOldTitle;
1298 aEvent.NewValue <<= m_aProps.getTitle();
1299
1300 aChanges.getArray()[ nChanged ] = aEvent;
1301 nChanged++;
1302 }
1303
1304 if ( nChanged > 0 )
1305 {
1306 // Save changes, if content was already made persistent.
1307 if ( !bExchange && ( m_eState == PERSISTENT ) )
1308 {
1309 if ( !storeData( uno::Reference< io::XInputStream >(), xEnv ) )
1310 {
1311 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1312 {
1313 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1314 }));
1316 ucb::IOErrorCode_CANT_WRITE,
1317 aArgs,
1318 xEnv,
1319 "Cannot store persistent data!",
1320 this );
1321 // Unreachable
1322 }
1323 }
1324
1325 aChanges.realloc( nChanged );
1326
1327 aGuard.clear();
1328 notifyPropertiesChange( aChanges );
1329 }
1330
1331 return aRet;
1332}
1333
1334
1336 const ucb::OpenCommandArgument2& rArg,
1337 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1338{
1339 if ( rArg.Mode == ucb::OpenMode::ALL ||
1340 rArg.Mode == ucb::OpenMode::FOLDERS ||
1341 rArg.Mode == ucb::OpenMode::DOCUMENTS )
1342 {
1343
1344 // open command for a folder content
1345
1346
1347 uno::Reference< ucb::XDynamicResultSet > xSet
1348 = new DynamicResultSet( m_xContext, this, rArg );
1349 return uno::Any( xSet );
1350 }
1351 else
1352 {
1353
1354 // open command for a document content
1355
1356
1357 if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
1358 ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
1359 {
1360 // Currently(?) unsupported.
1362 uno::Any( ucb::UnsupportedOpenModeException(
1363 OUString(),
1364 getXWeak(),
1365 sal_Int16( rArg.Mode ) ) ),
1366 xEnv );
1367 // Unreachable
1368 }
1369
1370 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1371
1372 uno::Reference< io::XActiveDataStreamer > xDataStreamer(
1373 rArg.Sink, uno::UNO_QUERY );
1374 if ( xDataStreamer.is() )
1375 {
1376 // May throw CommandFailedException, DocumentPasswordRequest!
1377 uno::Reference< io::XStream > xStream = getStream( xEnv );
1378 if ( !xStream.is() )
1379 {
1380 // No interaction if we are not persistent!
1381 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1382 {
1383 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1384 }));
1386 ucb::IOErrorCode_CANT_READ,
1387 aArgs,
1389 ? xEnv
1390 : uno::Reference< ucb::XCommandEnvironment >(),
1391 "Got no data stream!",
1392 this );
1393 // Unreachable
1394 }
1395
1396 // Done.
1397 xDataStreamer->setStream( xStream );
1398 }
1399 else
1400 {
1401 uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
1402 if ( xOut.is() )
1403 {
1404 // PUSH: write data into xOut
1405
1406 // May throw CommandFailedException, DocumentPasswordRequest!
1407 uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
1408 if ( !xIn.is() )
1409 {
1410 // No interaction if we are not persistent!
1411 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1412 {
1413 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1414 }));
1416 ucb::IOErrorCode_CANT_READ,
1417 aArgs,
1419 ? xEnv
1420 : uno::Reference< ucb::XCommandEnvironment >(),
1421 "Got no data stream!",
1422 this );
1423 // Unreachable
1424 }
1425
1426 try
1427 {
1428 uno::Sequence< sal_Int8 > aBuffer;
1429
1430 while (true)
1431 {
1432 sal_Int32 nRead = xIn->readSomeBytes( aBuffer, 65536 );
1433 if (!nRead)
1434 break;
1435 aBuffer.realloc( nRead );
1436 xOut->writeBytes( aBuffer );
1437 }
1438
1439 xOut->closeOutput();
1440 }
1441 catch ( io::NotConnectedException const & )
1442 {
1443 // closeOutput, readSomeBytes, writeBytes
1444 }
1445 catch ( io::BufferSizeExceededException const & )
1446 {
1447 // closeOutput, readSomeBytes, writeBytes
1448 }
1449 catch ( io::IOException const & )
1450 {
1451 // closeOutput, readSomeBytes, writeBytes
1452 }
1453 }
1454 else
1455 {
1456 uno::Reference< io::XActiveDataSink > xDataSink(
1457 rArg.Sink, uno::UNO_QUERY );
1458 if ( xDataSink.is() )
1459 {
1460 // PULL: wait for client read
1461
1462 // May throw CommandFailedException, DocumentPasswordRequest!
1463 uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
1464 if ( !xIn.is() )
1465 {
1466 // No interaction if we are not persistent!
1467 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1468 {
1469 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1470 }));
1472 ucb::IOErrorCode_CANT_READ,
1473 aArgs,
1475 ? xEnv
1476 : uno::Reference<
1477 ucb::XCommandEnvironment >(),
1478 "Got no data stream!",
1479 this );
1480 // Unreachable
1481 }
1482
1483 // Done.
1484 xDataSink->setInputStream( xIn );
1485 }
1486 else
1487 {
1489 uno::Any(
1490 ucb::UnsupportedDataSinkException(
1491 OUString(),
1492 getXWeak(),
1493 rArg.Sink ) ),
1494 xEnv );
1495 // Unreachable
1496 }
1497 }
1498 }
1499 }
1500
1501 return uno::Any();
1502}
1503
1504
1505void Content::insert( const uno::Reference< io::XInputStream >& xData,
1506 sal_Int32 nNameClashResolve,
1507 const uno::Reference<
1508 ucb::XCommandEnvironment > & xEnv )
1509{
1510 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1511
1513
1514 OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
1515 "insert command only supported by streams and folders!" );
1516
1517 Uri aUri( m_xIdentifier->getContentIdentifier() );
1518
1519#if OSL_DEBUG_LEVEL > 0
1520 if ( eType == STREAM )
1521 {
1522 Uri aParentUri( aUri.getParentUri() );
1523 OSL_ENSURE( !aParentUri.isDocument(),
1524 "insert command not supported by streams that are direct "
1525 "children of document root!" );
1526 }
1527#endif
1528
1529 // Check, if all required properties were set.
1530 if ( eType == FOLDER )
1531 {
1532 // Required: Title
1533
1534 if ( m_aProps.getTitle().isEmpty() )
1535 m_aProps.setTitle( aUri.getDecodedName() );
1536 }
1537 else // stream
1538 {
1539 // Required: data
1540
1541 if ( !xData.is() )
1542 {
1544 uno::Any( ucb::MissingInputStreamException(
1545 OUString(),
1546 getXWeak() ) ),
1547 xEnv );
1548 // Unreachable
1549 }
1550
1551 // Required: Title
1552
1553 if ( m_aProps.getTitle().isEmpty() )
1554 m_aProps.setTitle( aUri.getDecodedName() );
1555 }
1556
1557 Uri aNewUri( aUri.getParentUri() + m_aProps.getTitle() );
1558
1559 // Handle possible name clash...
1560 switch ( nNameClashResolve )
1561 {
1562 // fail.
1563 case ucb::NameClash::ERROR:
1564 if ( hasData( aNewUri ) )
1565 {
1567 uno::Any( ucb::NameClashException(
1568 OUString(),
1569 getXWeak(),
1570 task::InteractionClassification_ERROR,
1571 m_aProps.getTitle() ) ),
1572 xEnv );
1573 // Unreachable
1574 }
1575 break;
1576
1577 // replace (possibly) existing object.
1578 case ucb::NameClash::OVERWRITE:
1579 break;
1580
1581 // "invent" a new valid title.
1582 case ucb::NameClash::RENAME:
1583 if ( hasData( aNewUri ) )
1584 {
1585 sal_Int32 nTry = 0;
1586
1587 do
1588 {
1589 aNewUri.setUri( aNewUri.getUri() + "_" + OUString::number(++nTry) );
1590 }
1591 while ( hasData( aNewUri ) && ( nTry < 1000 ) );
1592
1593 if ( nTry == 1000 )
1594 {
1596 uno::Any(
1597 ucb::UnsupportedNameClashException(
1598 "Unable to resolve name clash!",
1599 getXWeak(),
1600 nNameClashResolve ) ),
1601 xEnv );
1602 // Unreachable
1603 }
1604 else
1605 {
1606 m_aProps.setTitle( m_aProps.getTitle() + "_" + OUString::number( ++nTry ) );
1607 }
1608 }
1609 break;
1610
1611 case ucb::NameClash::KEEP: // deprecated
1612 case ucb::NameClash::ASK:
1613 default:
1614 if ( hasData( aNewUri ) )
1615 {
1617 uno::Any(
1618 ucb::UnsupportedNameClashException(
1619 OUString(),
1620 getXWeak(),
1621 nNameClashResolve ) ),
1622 xEnv );
1623 // Unreachable
1624 }
1625 break;
1626 }
1627
1628 // Identifier changed?
1629 bool bNewId = ( aUri != aNewUri );
1630
1631 if ( bNewId )
1632 {
1634 = new ::ucbhelper::ContentIdentifier( aNewUri.getUri() );
1635 }
1636
1637 if ( !storeData( xData, xEnv ) )
1638 {
1639 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1640 {
1641 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1642 }));
1644 ucb::IOErrorCode_CANT_WRITE,
1645 aArgs,
1646 xEnv,
1647 "Cannot store persistent data!",
1648 this );
1649 // Unreachable
1650 }
1651
1653
1654 if ( bNewId )
1655 {
1656 //loadData( m_pProvider, m_aUri, m_aProps );
1657
1658 aGuard.clear();
1659 inserted();
1660 }
1661}
1662
1663
1664void Content::destroy( bool bDeletePhysical,
1665 const uno::Reference<
1666 ucb::XCommandEnvironment > & xEnv )
1667{
1668 // @@@ take care about bDeletePhysical -> trashcan support
1669
1670 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1671
1673
1674 OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
1675 "delete command only supported by streams and folders!" );
1676
1677 uno::Reference< ucb::XContent > xThis = this;
1678
1679 // Persistent?
1680 if ( m_eState != PERSISTENT )
1681 {
1683 uno::Any( ucb::UnsupportedCommandException(
1684 "Not persistent!",
1685 getXWeak() ) ),
1686 xEnv );
1687 // Unreachable
1688 }
1689
1690 m_eState = DEAD;
1691
1692 aGuard.clear();
1693 deleted();
1694
1695 if ( eType == FOLDER )
1696 {
1697 // Process instantiated children...
1698
1699 ContentRefList aChildren;
1700 queryChildren( aChildren );
1701
1702 for ( auto& rChild : aChildren )
1703 {
1704 rChild->destroy( bDeletePhysical, xEnv );
1705 }
1706 }
1707}
1708
1709
1711{
1712 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1713
1714 m_eState = DEAD;
1715
1716 // @@@ anything else to reset or such?
1717
1718 // callback follows!
1719 aGuard.clear();
1720
1721 // Propagate destruction to content event listeners
1722 // Remove this from provider's content list.
1723 deleted();
1724}
1725
1726
1727uno::Reference< ucb::XContent >
1728Content::queryChildContent( std::u16string_view rRelativeChildUri )
1729{
1730 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1731
1732 const OUString aMyId = getIdentifier()->getContentIdentifier();
1733 OUStringBuffer aBuf( aMyId );
1734 if ( !aMyId.endsWith("/") )
1735 aBuf.append( "/" );
1736 if ( !o3tl::starts_with(rRelativeChildUri, u"/") )
1737 aBuf.append( rRelativeChildUri );
1738 else
1739 aBuf.append( rRelativeChildUri.substr(1) );
1740
1741 uno::Reference< ucb::XContentIdentifier > xChildId
1742 = new ::ucbhelper::ContentIdentifier( aBuf.makeStringAndClear() );
1743
1744 uno::Reference< ucb::XContent > xChild;
1745 try
1746 {
1747 xChild = m_pProvider->queryContent( xChildId );
1748 }
1749 catch ( ucb::IllegalIdentifierException const & )
1750 {
1751 // handled below.
1752 }
1753
1754 OSL_ENSURE( xChild.is(),
1755 "Content::queryChildContent - unable to create child content!" );
1756 return xChild;
1757}
1758
1759
1760void Content::notifyChildRemoved( std::u16string_view rRelativeChildUri )
1761{
1762 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1763
1764 // Ugly! Need to create child content object, just to fill event properly.
1765 uno::Reference< ucb::XContent > xChild
1766 = queryChildContent( rRelativeChildUri );
1767
1768 if ( !xChild.is() )
1769 return;
1770
1771 // callback follows!
1772 aGuard.clear();
1773
1774 // Notify "REMOVED" event.
1775 ucb::ContentEvent aEvt(
1776 getXWeak(),
1777 ucb::ContentAction::REMOVED,
1778 xChild,
1779 getIdentifier() );
1780 notifyContentEvent( aEvt );
1781}
1782
1783
1784void Content::notifyChildInserted( std::u16string_view rRelativeChildUri )
1785{
1786 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1787
1788 // Ugly! Need to create child content object, just to fill event properly.
1789 uno::Reference< ucb::XContent > xChild
1790 = queryChildContent( rRelativeChildUri );
1791
1792 if ( !xChild.is() )
1793 return;
1794
1795 // callback follows!
1796 aGuard.clear();
1797
1798 // Notify "INSERTED" event.
1799 ucb::ContentEvent aEvt(
1800 getXWeak(),
1801 ucb::ContentAction::INSERTED,
1802 xChild,
1803 getIdentifier() );
1804 notifyContentEvent( aEvt );
1805}
1806
1807
1809 const ucb::TransferInfo& rInfo,
1810 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1811{
1812 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1813
1814 // Persistent?
1815 if ( m_eState != PERSISTENT )
1816 {
1818 uno::Any( ucb::UnsupportedCommandException(
1819 "Not persistent!",
1820 getXWeak() ) ),
1821 xEnv );
1822 // Unreachable
1823 }
1824
1825 // Does source URI scheme match? Only vnd.sun.star.tdoc is supported.
1826
1827 if ( rInfo.SourceURL.getLength() < TDOC_URL_SCHEME_LENGTH + 2 )
1828 {
1829 // Invalid length (to short).
1831 uno::Any( ucb::InteractiveBadTransferURLException(
1832 OUString(),
1833 getXWeak() ) ),
1834 xEnv );
1835 // Unreachable
1836 }
1837
1838 OUString aScheme
1839 = rInfo.SourceURL.copy( 0, TDOC_URL_SCHEME_LENGTH + 2 )
1840 .toAsciiLowerCase();
1841 if ( aScheme != TDOC_URL_SCHEME ":/" )
1842 {
1843 // Invalid scheme.
1845 uno::Any( ucb::InteractiveBadTransferURLException(
1846 OUString(),
1847 getXWeak() ) ),
1848 xEnv );
1849 // Unreachable
1850 }
1851
1852 // Does source URI describe a tdoc folder or stream?
1853 Uri aSourceUri( rInfo.SourceURL );
1854 if ( !aSourceUri.isValid() )
1855 {
1857 uno::Any( lang::IllegalArgumentException(
1858 "Invalid source URI! Syntax!",
1859 getXWeak(),
1860 -1 ) ),
1861 xEnv );
1862 // Unreachable
1863 }
1864
1865 if ( aSourceUri.isRoot() || aSourceUri.isDocument() )
1866 {
1868 uno::Any( lang::IllegalArgumentException(
1869 "Invalid source URI! Must describe a folder or stream!",
1870 getXWeak(),
1871 -1 ) ),
1872 xEnv );
1873 // Unreachable
1874 }
1875
1876 // Is source not a parent of me / not me?
1877 OUString aId = m_xIdentifier->getContentIdentifier();
1878 sal_Int32 nPos = aId.lastIndexOf( '/' );
1879 if ( nPos != ( aId.getLength() - 1 ) )
1880 {
1881 // No trailing slash found. Append.
1882 aId += "/";
1883 }
1884
1885 if ( rInfo.SourceURL.getLength() <= aId.getLength() )
1886 {
1887 if ( aId.startsWith( rInfo.SourceURL ) )
1888 {
1889 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1890 {
1891 {"Uri", uno::Any(rInfo.SourceURL)}
1892 }));
1894 ucb::IOErrorCode_RECURSIVE,
1895 aArgs,
1896 xEnv,
1897 "Target is equal to or is a child of source!",
1898 this );
1899 // Unreachable
1900 }
1901 }
1902
1903 if ( m_aProps.getType() == DOCUMENT )
1904 {
1905 bool bOK = false;
1906
1907 uno::Reference< embed::XStorage > xStorage
1909 aSourceUri.getParentUri(), READ_WRITE_NOCREATE );
1910 if ( xStorage.is() )
1911 {
1912 try
1913 {
1914 if ( xStorage->isStreamElement( aSourceUri.getDecodedName() ) )
1915 {
1917 uno::Any( lang::IllegalArgumentException(
1918 "Invalid source URI! "
1919 "Streams cannot be created as "
1920 "children of document root!",
1921 getXWeak(),
1922 -1 ) ),
1923 xEnv );
1924 // Unreachable
1925 }
1926 bOK = true;
1927 }
1928 catch ( container::NoSuchElementException const & )
1929 {
1930 // handled below.
1931 }
1932 catch ( lang::IllegalArgumentException const & )
1933 {
1934 // handled below.
1935 }
1936 catch ( embed::InvalidStorageException const & )
1937 {
1938 // handled below.
1939 }
1940 }
1941
1942 if ( !bOK )
1943 {
1945 uno::Any( lang::IllegalArgumentException(
1946 "Invalid source URI! Unable to determine source type!",
1947 getXWeak(),
1948 -1 ) ),
1949 xEnv );
1950 // Unreachable
1951 }
1952 }
1953
1954
1955 // Copy data.
1956
1957
1958 OUString aNewName( !rInfo.NewTitle.isEmpty()
1959 ? rInfo.NewTitle
1960 : aSourceUri.getDecodedName() );
1961
1962 if ( !copyData( aSourceUri, aNewName ) )
1963 {
1964 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1965 {
1966 {"Uri", uno::Any(rInfo.SourceURL)}
1967 }));
1969 ucb::IOErrorCode_CANT_WRITE,
1970 aArgs,
1971 xEnv,
1972 "Cannot copy data!",
1973 this );
1974 // Unreachable
1975 }
1976
1977
1978 // Copy own and all children's Additional Core Properties.
1979
1980
1981 OUString aTargetUri = m_xIdentifier->getContentIdentifier();
1982 if ( ( aTargetUri.lastIndexOf( '/' ) + 1 ) != aTargetUri.getLength() )
1983 aTargetUri += "/";
1984
1985 if ( !rInfo.NewTitle.isEmpty() )
1986 aTargetUri += ::ucb_impl::urihelper::encodeSegment( rInfo.NewTitle );
1987 else
1988 aTargetUri += aSourceUri.getName();
1989
1990 if ( !copyAdditionalPropertySet( aSourceUri.getUri(), aTargetUri ) )
1991 {
1992 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1993 {
1994 {"Uri", uno::Any(rInfo.SourceURL)}
1995 }));
1997 ucb::IOErrorCode_CANT_WRITE,
1998 aArgs,
1999 xEnv,
2000 "Cannot copy additional properties!",
2001 this );
2002 // Unreachable
2003 }
2004
2005
2006 // Propagate new content.
2007
2008
2010 try
2011 {
2012 uno::Reference< ucb::XContentIdentifier > xTargetId
2013 = new ::ucbhelper::ContentIdentifier( aTargetUri );
2014
2015 // Note: The static cast is okay here, because its sure that
2016 // m_xProvider is always the WebDAVContentProvider.
2017 xTarget = static_cast< Content * >(
2018 m_pProvider->queryContent( xTargetId ).get() );
2019
2020 }
2021 catch ( ucb::IllegalIdentifierException const & )
2022 {
2023 // queryContent
2024 }
2025
2026 if ( !xTarget.is() )
2027 {
2028 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2029 {
2030 {"Uri", uno::Any(aTargetUri)}
2031 }));
2033 ucb::IOErrorCode_CANT_READ,
2034 aArgs,
2035 xEnv,
2036 "Cannot instantiate target object!",
2037 this );
2038 // Unreachable
2039 }
2040
2041 // Announce transferred content in its new folder.
2042 xTarget->inserted();
2043
2044
2045 // Remove source, if requested
2046
2047
2048 if ( !rInfo.MoveData )
2049 return;
2050
2052 try
2053 {
2054 uno::Reference< ucb::XContentIdentifier >
2055 xSourceId = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );
2056
2057 // Note: The static cast is okay here, because its sure
2058 // that m_xProvider is always the ContentProvider.
2059 xSource = static_cast< Content * >(
2060 m_xProvider->queryContent( xSourceId ).get() );
2061 }
2062 catch ( ucb::IllegalIdentifierException const & )
2063 {
2064 // queryContent
2065 }
2066
2067 if ( !xSource.is() )
2068 {
2069 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2070 {
2071 {"Uri", uno::Any(rInfo.SourceURL)}
2072 }));
2074 ucb::IOErrorCode_CANT_READ,
2075 aArgs,
2076 xEnv,
2077 "Cannot instantiate target object!",
2078 this );
2079 // Unreachable
2080 }
2081
2082 // Propagate destruction (recursively).
2083 xSource->destroy( true, xEnv );
2084
2085 // Remove all persistent data of source and its children.
2086 if ( !xSource->removeData() )
2087 {
2088 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2089 {
2090 {"Uri", uno::Any(rInfo.SourceURL)}
2091 }));
2093 ucb::IOErrorCode_CANT_WRITE,
2094 aArgs,
2095 xEnv,
2096 "Cannot remove persistent data of source object!",
2097 this );
2098 // Unreachable
2099 }
2100
2101 // Remove own and all children's Additional Core Properties.
2102 if ( xSource->removeAdditionalPropertySet() )
2103 return;
2104
2105 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2106 {
2107 {"Uri", uno::Any(rInfo.SourceURL)}
2108 }));
2110 ucb::IOErrorCode_CANT_WRITE,
2111 aArgs,
2112 xEnv,
2113 "Cannot remove additional properties of source object!",
2114 this );
2115 // Unreachable
2116}
2117
2118
2119//static
2120bool Content::hasData( ContentProvider const * pProvider, const Uri & rUri )
2121{
2122 if ( rUri.isRoot() )
2123 {
2124 return true; // root has no storage
2125 }
2126 else if ( rUri.isDocument() )
2127 {
2128 uno::Reference< embed::XStorage > xStorage
2129 = pProvider->queryStorage( rUri.getUri(), READ );
2130 return xStorage.is();
2131 }
2132 else
2133 {
2134 // folder or stream
2135
2136 // Ask parent storage. In case that rUri describes a stream,
2137 // ContentProvider::queryStorage( rUri ) would return null.
2138
2139 uno::Reference< embed::XStorage > xStorage
2140 = pProvider->queryStorage( rUri.getParentUri(), READ );
2141
2142 if ( !xStorage.is() )
2143 return false;
2144
2145 return xStorage->hasByName( rUri.getDecodedName() );
2146 }
2147}
2148
2149
2150//static
2151bool Content::loadData( ContentProvider const * pProvider,
2152 const Uri & rUri,
2153 ContentProperties& rProps )
2154{
2155 if ( rUri.isRoot() ) // root has no storage, but can always be created
2156 {
2157 rProps
2159 ROOT, pProvider->queryStorageTitle( rUri.getUri() ) );
2160 }
2161 else if ( rUri.isDocument() ) // document must have storage
2162 {
2163 uno::Reference< embed::XStorage > xStorage
2164 = pProvider->queryStorage( rUri.getUri(), READ );
2165
2166 if ( !xStorage.is() )
2167 return false;
2168
2169 rProps
2171 DOCUMENT, pProvider->queryStorageTitle( rUri.getUri() ) );
2172 }
2173 else // stream or folder; stream has no storage; folder has storage
2174 {
2175 uno::Reference< embed::XStorage > xStorage
2176 = pProvider->queryStorage( rUri.getParentUri(), READ );
2177
2178 if ( !xStorage.is() )
2179 return false;
2180
2181 // Check whether exists at all, is stream or folder
2182 try
2183 {
2184 // return: true -> folder
2185 // return: false -> stream
2186 // NoSuchElementException -> neither folder nor stream
2187 bool bIsFolder
2188 = xStorage->isStorageElement( rUri.getDecodedName() );
2189
2190 rProps
2192 bIsFolder ? FOLDER : STREAM,
2193 pProvider->queryStorageTitle( rUri.getUri() ) );
2194 }
2195 catch ( container::NoSuchElementException const & )
2196 {
2197 // there is no element with such name
2198 //OSL_ENSURE( false, "Caught NoSuchElementException!" );
2199 return false;
2200 }
2201 catch ( lang::IllegalArgumentException const & )
2202 {
2203 // an illegal argument is provided
2204 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2205 return false;
2206 }
2207 catch ( embed::InvalidStorageException const & )
2208 {
2209 // this storage is in invalid state for any reason
2210 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2211 return false;
2212 }
2213 }
2214 return true;
2215}
2216
2217
2218bool Content::storeData( const uno::Reference< io::XInputStream >& xData,
2219 const uno::Reference<
2220 ucb::XCommandEnvironment >& xEnv )
2221{
2222 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2223
2225 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2226 {
2227 OSL_FAIL( "storeData not supported by root and documents!" );
2228 return false;
2229 }
2230
2231 Uri aUri( m_xIdentifier->getContentIdentifier() );
2232
2233 if ( eType == FOLDER )
2234 {
2235 uno::Reference< embed::XStorage > xStorage
2236 = m_pProvider->queryStorage( aUri.getUri(), READ_WRITE_CREATE );
2237
2238 if ( !xStorage.is() )
2239 return false;
2240
2241 uno::Reference< beans::XPropertySet > xPropSet(
2242 xStorage, uno::UNO_QUERY );
2243 OSL_ENSURE( xPropSet.is(),
2244 "Content::storeData - Got no XPropertySet interface!" );
2245 if ( !xPropSet.is() )
2246 return false;
2247
2248 try
2249 {
2250 // According to MBA, if no mediatype is set, folder and all
2251 // its contents will be lost on save of the document!!!
2252 xPropSet->setPropertyValue(
2253 "MediaType",
2254 uno::Any(
2255 OUString( // @@@ better mediatype
2256 "application/binary" ) ) );
2257 }
2258 catch ( beans::UnknownPropertyException const & )
2259 {
2260 OSL_FAIL( "Property MediaType not supported!" );
2261 return false;
2262 }
2263 catch ( beans::PropertyVetoException const & )
2264 {
2265 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2266 return false;
2267 }
2268 catch ( lang::IllegalArgumentException const & )
2269 {
2270 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2271 return false;
2272 }
2273 catch ( lang::WrappedTargetException const & )
2274 {
2275 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2276 return false;
2277 }
2278
2279 if ( !commitStorage( xStorage ) )
2280 return false;
2281 }
2282 else if ( eType == STREAM )
2283 {
2284 // stream
2285
2286 // Important: Parent storage and output stream must be kept alive until
2287 // changes have been committed!
2288 uno::Reference< embed::XStorage > xStorage
2290 aUri.getParentUri(), READ_WRITE_CREATE );
2291 uno::Reference< io::XOutputStream > xOut;
2292
2293 if ( !xStorage.is() )
2294 return false;
2295
2296 if ( xData.is() )
2297 {
2298 // May throw CommandFailedException, DocumentPasswordRequest!
2299 xOut = getTruncatedOutputStream( xEnv );
2300
2301 OSL_ENSURE( xOut.is(), "No target data stream!" );
2302
2303 try
2304 {
2305 uno::Sequence< sal_Int8 > aBuffer;
2306 while (true)
2307 {
2308 sal_Int32 nRead = xData->readSomeBytes( aBuffer, 65536 );
2309 if (!nRead)
2310 break;
2311 aBuffer.realloc( nRead );
2312 xOut->writeBytes( aBuffer );
2313 }
2314
2315 closeOutputStream( xOut );
2316 }
2317 catch ( io::NotConnectedException const & )
2318 {
2319 // readSomeBytes, writeBytes
2320 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2321 closeOutputStream( xOut );
2322 return false;
2323 }
2324 catch ( io::BufferSizeExceededException const & )
2325 {
2326 // readSomeBytes, writeBytes
2327 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2328 closeOutputStream( xOut );
2329 return false;
2330 }
2331 catch ( io::IOException const & )
2332 {
2333 // readSomeBytes, writeBytes
2334 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2335 closeOutputStream( xOut );
2336 return false;
2337 }
2338 catch ( ... )
2339 {
2340 closeOutputStream( xOut );
2341 throw;
2342 }
2343 }
2344
2345 // Commit changes.
2346 if ( !commitStorage( xStorage ) )
2347 return false;
2348 }
2349 else
2350 {
2351 OSL_FAIL( "Unknown content type!" );
2352 return false;
2353 }
2354 return true;
2355}
2356
2357
2359 const uno::Reference< ucb::XContentIdentifier >& xOldId,
2360 const uno::Reference< ucb::XContentIdentifier >& xNewId )
2361{
2362 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2363
2365 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2366 {
2367 OSL_FAIL( "renameData not supported by root and documents!" );
2368 return;
2369 }
2370
2371 Uri aOldUri( xOldId->getContentIdentifier() );
2372 uno::Reference< embed::XStorage > xStorage
2374 aOldUri.getParentUri(), READ_WRITE_NOCREATE );
2375
2376 if ( !xStorage.is() )
2377 return;
2378
2379 try
2380 {
2381 Uri aNewUri( xNewId->getContentIdentifier() );
2382 xStorage->renameElement(
2383 aOldUri.getDecodedName(), aNewUri.getDecodedName() );
2384 }
2385 catch ( embed::InvalidStorageException const & )
2386 {
2387 // this storage is in invalid state for any reason
2388 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2389 return;
2390 }
2391 catch ( lang::IllegalArgumentException const & )
2392 {
2393 // an illegal argument is provided
2394 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2395 return;
2396 }
2397 catch ( container::NoSuchElementException const & )
2398 {
2399 // there is no element with old name in this storage
2400 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2401 return;
2402 }
2403 catch ( container::ElementExistException const & )
2404 {
2405 // an element with new name already exists in this storage
2406 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2407 return;
2408 }
2409 catch ( io::IOException const & )
2410 {
2411 // in case of io errors during renaming
2412 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2413 return;
2414 }
2415 catch ( embed::StorageWrappedTargetException const & )
2416 {
2417 // wraps other exceptions
2418 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2419 return;
2420 }
2421
2422 commitStorage( xStorage );
2423}
2424
2425
2427{
2428 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2429
2431 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2432 {
2433 OSL_FAIL( "removeData not supported by root and documents!" );
2434 return false;
2435 }
2436
2437 Uri aUri( m_xIdentifier->getContentIdentifier() );
2438 uno::Reference< embed::XStorage > xStorage
2441
2442 if ( !xStorage.is() )
2443 return false;
2444
2445 try
2446 {
2447 xStorage->removeElement( aUri.getDecodedName() );
2448 }
2449 catch ( embed::InvalidStorageException const & )
2450 {
2451 // this storage is in invalid state for any reason
2452 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2453 return false;
2454 }
2455 catch ( lang::IllegalArgumentException const & )
2456 {
2457 // an illegal argument is provided
2458 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2459 return false;
2460 }
2461 catch ( container::NoSuchElementException const & )
2462 {
2463 // there is no element with this name in this storage
2464 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2465 return false;
2466 }
2467 catch ( io::IOException const & )
2468 {
2469 // in case of io errors during renaming
2470 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2471 return false;
2472 }
2473 catch ( embed::StorageWrappedTargetException const & )
2474 {
2475 // wraps other exceptions
2476 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2477 return false;
2478 }
2479
2480 return commitStorage( xStorage );
2481}
2482
2483
2484bool Content::copyData( const Uri & rSourceUri, const OUString & rNewName )
2485{
2486 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2487
2489 if ( ( eType == ROOT ) || ( eType == STREAM ) )
2490 {
2491 OSL_FAIL( "copyData not supported by root and streams!" );
2492 return false;
2493 }
2494
2495 Uri aDestUri( m_xIdentifier->getContentIdentifier() );
2496 uno::Reference< embed::XStorage > xDestStorage
2498
2499 if ( !xDestStorage.is() )
2500 return false;
2501
2502 uno::Reference< embed::XStorage > xSourceStorage
2503 = m_pProvider->queryStorage( rSourceUri.getParentUri(), READ );
2504
2505 if ( !xSourceStorage.is() )
2506 return false;
2507
2508 try
2509 {
2510 xSourceStorage->copyElementTo( rSourceUri.getDecodedName(),
2511 xDestStorage,
2512 rNewName );
2513 }
2514 catch ( embed::InvalidStorageException const & )
2515 {
2516 // this storage is in invalid state for any reason
2517 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2518 return false;
2519 }
2520 catch ( lang::IllegalArgumentException const & )
2521 {
2522 // an illegal argument is provided
2523 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2524 return false;
2525 }
2526 catch ( container::NoSuchElementException const & )
2527 {
2528 // there is no element with this name in this storage
2529 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2530 return false;
2531 }
2532 catch ( container::ElementExistException const & )
2533 {
2534 // there is no element with this name in this storage
2535 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2536 return false;
2537 }
2538 catch ( io::IOException const & )
2539 {
2540 // in case of io errors during renaming
2541 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2542 return false;
2543 }
2544 catch ( embed::StorageWrappedTargetException const & )
2545 {
2546 // wraps other exceptions
2547 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2548 return false;
2549 }
2550
2551 return commitStorage( xDestStorage );
2552}
2553
2554
2555// static
2556bool Content::commitStorage( const uno::Reference< embed::XStorage > & xStorage )
2557{
2558 // Commit changes
2559 uno::Reference< embed::XTransactedObject > xTO( xStorage, uno::UNO_QUERY );
2560
2561 OSL_ENSURE( xTO.is(),
2562 "Required interface css.embed.XTransactedObject missing!" );
2563 try
2564 {
2565 xTO->commit();
2566 }
2567 catch ( io::IOException const & )
2568 {
2569 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2570 return false;
2571 }
2572 catch ( lang::WrappedTargetException const & )
2573 {
2574 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2575 return false;
2576 }
2577
2578 return true;
2579}
2580
2581
2582// static
2584 const uno::Reference< io::XOutputStream > & xOut )
2585{
2586 if ( xOut.is() )
2587 {
2588 try
2589 {
2590 xOut->closeOutput();
2591 return true;
2592 }
2593 catch ( io::NotConnectedException const & )
2594 {
2595 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2596 }
2597 catch ( io::BufferSizeExceededException const & )
2598 {
2599 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2600 }
2601 catch ( io::IOException const & )
2602 {
2603 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2604 }
2605 }
2606 return false;
2607}
2608
2611static OUString obtainPassword(
2612 const OUString & rName,
2613 task::PasswordRequestMode eMode,
2614 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2615{
2617 = new DocumentPasswordRequest( eMode, rName );
2618
2619 if ( xEnv.is() )
2620 {
2621 uno::Reference< task::XInteractionHandler > xIH
2622 = xEnv->getInteractionHandler();
2623 if ( xIH.is() )
2624 {
2625 xIH->handle( xRequest );
2626
2628 = xRequest->getSelection();
2629
2630 if ( xSelection.is() )
2631 {
2632 // Handler handled the request.
2633 uno::Reference< task::XInteractionAbort > xAbort(
2634 xSelection.get(), uno::UNO_QUERY );
2635 if ( xAbort.is() )
2636 {
2637 throw ucb::CommandFailedException(
2638 "Abort requested by Interaction Handler.",
2639 uno::Reference< uno::XInterface >(),
2640 xRequest->getRequest() );
2641 }
2642
2643 uno::Reference< task::XInteractionPassword > xPassword(
2644 xSelection.get(), uno::UNO_QUERY );
2645 if ( xPassword.is() )
2646 {
2647 return xPassword->getPassword();
2648 }
2649
2650 // Unknown selection. Should never happen.
2651 throw ucb::CommandFailedException(
2652 "Interaction Handler selected unknown continuation!",
2653 uno::Reference< uno::XInterface >(),
2654 xRequest->getRequest() );
2655 }
2656 }
2657 }
2658
2659 // No IH or IH did not handle exception.
2660 task::DocumentPasswordRequest aRequest;
2661 xRequest->getRequest() >>= aRequest;
2662 throw aRequest;
2663}
2664
2665
2666uno::Reference< io::XInputStream > Content::getInputStream(
2667 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2668{
2669 OUString aUri;
2670 OUString aPassword;
2671 bool bPasswordRequested = false;
2672
2673 {
2674 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2675
2676 OSL_ENSURE( m_aProps.getType() == STREAM,
2677 "Content::getInputStream - content is no stream!" );
2678
2679 aUri = Uri( m_xIdentifier->getContentIdentifier() ).getUri();
2680 }
2681
2682 for ( ;; )
2683 {
2684 try
2685 {
2686 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2687 return m_pProvider->queryInputStream( aUri, aPassword );
2688 }
2689 catch ( packages::WrongPasswordException const & )
2690 {
2691 // Obtain (new) password.
2692 aPassword
2693 = obtainPassword( aUri, /* @@@ find better title */
2694 bPasswordRequested
2695 ? task::PasswordRequestMode_PASSWORD_REENTER
2696 : task::PasswordRequestMode_PASSWORD_ENTER,
2697 xEnv );
2698 bPasswordRequested = true;
2699 }
2700 }
2701}
2702
2706static uno::Reference< io::XOutputStream > lcl_getTruncatedOutputStream(
2707 const OUString & rUri,
2708 ContentProvider const * pProvider,
2709 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2710{
2711 OUString aPassword;
2712 bool bPasswordRequested = false;
2713 for ( ;; )
2714 {
2715 try
2716 {
2717 return pProvider->queryOutputStream(
2718 rUri, aPassword, true /* truncate */ );
2719 }
2720 catch ( packages::WrongPasswordException const & )
2721 {
2722 // Obtain (new) password.
2723 aPassword
2724 = obtainPassword( rUri, /* @@@ find better title */
2725 bPasswordRequested
2726 ? task::PasswordRequestMode_PASSWORD_REENTER
2727 : task::PasswordRequestMode_PASSWORD_ENTER,
2728 xEnv );
2729 bPasswordRequested = true;
2730 }
2731 }
2732}
2733
2734
2735uno::Reference< io::XOutputStream > Content::getTruncatedOutputStream(
2736 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2737{
2738 OSL_ENSURE( m_aProps.getType() == STREAM,
2739 "Content::getTruncatedOutputStream - content is no stream!" );
2740
2742 Uri( m_xIdentifier->getContentIdentifier() ).getUri(),
2744 xEnv );
2745}
2746
2747
2748uno::Reference< io::XStream > Content::getStream(
2749 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2750{
2751 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2752
2753 OSL_ENSURE( m_aProps.getType() == STREAM,
2754 "Content::getStream - content is no stream!" );
2755
2756 OUString aUri( Uri( m_xIdentifier->getContentIdentifier() ).getUri() );
2757 OUString aPassword;
2758 bool bPasswordRequested = false;
2759 for ( ;; )
2760 {
2761 try
2762 {
2763 return m_pProvider->queryStream(
2764 aUri, aPassword, false /* no truncate */ );
2765 }
2766 catch ( packages::WrongPasswordException const & )
2767 {
2768 // Obtain (new) password.
2769 aPassword
2770 = obtainPassword( aUri, /* @@@ find better title */
2771 bPasswordRequested
2772 ? task::PasswordRequestMode_PASSWORD_REENTER
2773 : task::PasswordRequestMode_PASSWORD_ENTER,
2774 xEnv );
2775 bPasswordRequested = true;
2776 }
2777 }
2778}
2779
2780
2781// ContentProperties Implementation.
2782
2783
2784uno::Sequence< ucb::ContentInfo >
2786{
2787 if ( isContentCreator() )
2788 {
2789 uno::Sequence< beans::Property > aProps( 1 );
2790 aProps.getArray()[ 0 ] = beans::Property(
2791 "Title",
2792 -1,
2794 beans::PropertyAttribute::BOUND );
2795
2796 if ( getType() == DOCUMENT )
2797 {
2798 // streams cannot be created as direct children of document root
2799 uno::Sequence< ucb::ContentInfo > aSeq( 1 );
2800
2801 // Folder.
2802 aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
2803 aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
2804 aSeq.getArray()[ 0 ].Properties = aProps;
2805
2806 return aSeq;
2807 }
2808 else
2809 {
2810 uno::Sequence< ucb::ContentInfo > aSeq( 2 );
2811
2812 // Folder.
2813 aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
2814 aSeq.getArray()[ 0 ].Attributes
2815 = ucb::ContentInfoAttribute::KIND_FOLDER;
2816 aSeq.getArray()[ 0 ].Properties = aProps;
2817
2818 // Stream.
2819 aSeq.getArray()[ 1 ].Type = TDOC_STREAM_CONTENT_TYPE;
2820 aSeq.getArray()[ 1 ].Attributes
2821 = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
2822 | ucb::ContentInfoAttribute::KIND_DOCUMENT;
2823 aSeq.getArray()[ 1 ].Properties = aProps;
2824
2825 return aSeq;
2826 }
2827 }
2828 else
2829 {
2830 OSL_FAIL( "getCreatableContentsInfo called on non-contentcreator "
2831 "object!" );
2832
2833 return uno::Sequence< ucb::ContentInfo >( 0 );
2834 }
2835}
2836
2837
2839{
2840 return ( getType() == FOLDER ) || ( getType() == DOCUMENT );
2841}
2842
2843/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
const PropertyValue * pValues
PropertiesInfo aProperties
Reference< XInputStream > xStream
AnyEventRef aEvent
css::uno::Sequence< css::uno::Type > SAL_CALL getTypes()
ContentType getType() const
css::uno::Sequence< css::ucb::ContentInfo > getCreatableContentsInfo() const
const OUString & getContentType() const
void setTitle(const OUString &rTitle)
const OUString & getTitle() const
css::util::DateTime queryStreamDateModified(OUString const &uri) const
OUString queryStorageTitle(const OUString &rUri) const
css::uno::Reference< css::io::XOutputStream > queryOutputStream(const OUString &rUri, const OUString &rPassword, bool bTruncate) const
css::uno::Reference< css::io::XInputStream > queryInputStream(const OUString &rUri, const OUString &rPassword) const
css::uno::Reference< css::embed::XStorage > queryStorageClone(const OUString &rUri) const
css::uno::Reference< css::frame::XModel > queryDocumentModel(const OUString &rUri) const
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL queryContent(const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier) override
css::uno::Reference< css::io::XStream > queryStream(const OUString &rUri, const OUString &rPassword, bool bTruncate) const
css::uno::Reference< css::embed::XStorage > queryStorage(const OUString &rUri, StorageAccessMode eMode) const
static bool hasData(ContentProvider const *pProvider, const Uri &rUri)
static bool commitStorage(const css::uno::Reference< css::embed::XStorage > &xStorage)
css::uno::Reference< css::io::XOutputStream > getTruncatedOutputStream(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual css::uno::Reference< css::ucb::XContentIdentifier > SAL_CALL getIdentifier() override
bool exchangeIdentity(const css::uno::Reference< css::ucb::XContentIdentifier > &xNewId)
void renameData(const css::uno::Reference< css::ucb::XContentIdentifier > &xOldId, const css::uno::Reference< css::ucb::XContentIdentifier > &xNewId)
css::uno::Sequence< css::uno::Any > setPropertyValues(const css::uno::Sequence< css::beans::PropertyValue > &rValues, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
bool copyData(const Uri &rSourceUri, const OUString &rNewName)
virtual css::uno::Sequence< css::ucb::ContentInfo > SAL_CALL queryCreatableContentsInfo() override
ContentProvider * m_pProvider
virtual css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType) override
virtual css::uno::Any SAL_CALL execute(const css::ucb::Command &aCommand, sal_Int32 CommandId, const css::uno::Reference< css::ucb::XCommandEnvironment > &Environment) override
ContentState m_eState
void insert(const css::uno::Reference< css::io::XInputStream > &xData, sal_Int32 nNameClashResolve, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
std::vector< ContentRef > ContentRefList
css::uno::Any open(const css::ucb::OpenCommandArgument2 &rArg, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void destroy(bool bDeletePhysical, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual css::uno::Reference< css::ucb::XContent > SAL_CALL createNewContent(const css::ucb::ContentInfo &Info) override
rtl::Reference< Content > ContentRef
static rtl::Reference< Content > create(const css::uno::Reference< css::uno::XComponentContext > &rxContext, ContentProvider *pProvider, const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier)
css::uno::Reference< css::sdbc::XRow > getPropertyValues(const css::uno::Sequence< css::beans::Property > &rProperties)
void notifyChildInserted(std::u16string_view rRelativeChildUri)
css::uno::Reference< css::io::XStream > getStream(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
css::uno::Reference< css::io::XInputStream > getInputStream(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void notifyChildRemoved(std::u16string_view rRelativeChildUri)
virtual OUString SAL_CALL getImplementationName() override
void transfer(const css::ucb::TransferInfo &rInfo, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
virtual void SAL_CALL acquire() noexcept override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
css::uno::Reference< css::ucb::XContentIdentifier > makeNewIdentifier(const OUString &rTitle)
virtual void SAL_CALL release() noexcept override
static bool closeOutputStream(const css::uno::Reference< css::io::XOutputStream > &xOut)
virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override
bool storeData(const css::uno::Reference< css::io::XInputStream > &xData, const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv)
void queryChildren(ContentRefList &rChildren)
virtual OUString SAL_CALL getContentType() override
virtual void SAL_CALL abort(sal_Int32 CommandId) override
virtual OUString getParentURL() override
ContentProperties m_aProps
static bool loadData(ContentProvider const *pProvider, const Uri &rUri, ContentProperties &rProps)
Content(const css::uno::Reference< css::uno::XComponentContext > &rxContext, ContentProvider *pProvider, const css::uno::Reference< css::ucb::XContentIdentifier > &Identifier, ContentProperties aProps)
css::uno::Reference< css::ucb::XContent > queryChildContent(std::u16string_view rRelativeChildUri)
bool isRoot() const
Definition: tdoc_uri.hxx:93
const OUString & getDecodedName() const
Definition: tdoc_uri.hxx:74
const OUString & getParentUri() const
Definition: tdoc_uri.hxx:65
const OUString & getUri() const
Definition: tdoc_uri.hxx:60
bool isDocument() const
Definition: tdoc_uri.hxx:99
css::uno::Reference< css::ucb::XPersistentPropertySet > getAdditionalPropertySet(bool bCreate)
css::uno::Reference< css::ucb::XCommandInfo > getCommandInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
bool renameAdditionalPropertySet(const OUString &rOldKey, const OUString &rNewKey)
rtl::Reference< ContentProviderImplHelper > m_xProvider
void notifyContentEvent(const css::ucb::ContentEvent &evt) const
bool copyAdditionalPropertySet(const OUString &rSourceKey, const OUString &rTargetKey)
bool exchange(const css::uno::Reference< css::ucb::XContentIdentifier > &rNewId)
css::uno::Reference< css::beans::XPropertySetInfo > getPropertySetInfo(const css::uno::Reference< css::ucb::XCommandEnvironment > &xEnv, bool bCache=true)
css::uno::Reference< css::ucb::XContentIdentifier > m_xIdentifier
css::uno::Reference< css::uno::XComponentContext > m_xContext
void notifyPropertiesChange(const css::uno::Sequence< css::beans::PropertyChangeEvent > &evt) const
int nCount
#define TOOLS_WARN_EXCEPTION(area, stream)
URL aURL
float u
Reference< XInterface > xTarget
DocumentType eType
Mode eMode
sal_Int64 n
sal_uInt16 nPos
Sequence< sal_Int8 > aSeq
#define CPPU_TYPE_REF(T)
aBuf
constexpr OUStringLiteral aData
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
Type
css::uno::Any SAL_CALL queryInterface(const css::uno::Type &rType, Interface1 *p1)
Info
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
constexpr OUStringLiteral TDOC_ROOT_CONTENT_TYPE
constexpr OUStringLiteral TDOC_STREAM_CONTENT_TYPE
constexpr OUStringLiteral TDOC_DOCUMENT_CONTENT_TYPE
@ READ_WRITE_NOCREATE
@ READ_WRITE_CREATE
constexpr OUStringLiteral TDOC_FOLDER_CONTENT_TYPE
OUString encodeSegment(const OUString &rSegment)
Definition: urihelper.hxx:29
std::vector< ContentImplHelperRef > ContentRefList
void cancelCommandExecution(const uno::Any &rException, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
uno::Sequence< beans::Property > m_aProps
bool hasValue()
static OUString obtainPassword(const OUString &rName, task::PasswordRequestMode eMode, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
static ContentType lcl_getContentType(std::u16string_view rType)
XTYPEPROVIDER_COMMON_IMPL(Content)
static uno::Reference< io::XOutputStream > lcl_getTruncatedOutputStream(const OUString &rUri, ContentProvider const *pProvider, const uno::Reference< ucb::XCommandEnvironment > &xEnv)
#define TDOC_URL_SCHEME_LENGTH
Definition: tdoc_uri.hxx:29
#define TDOC_URL_SCHEME
Definition: tdoc_uri.hxx:28
OUString aCommand
std::unique_ptr< char[]> aBuffer