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 static_cast< cppu::OWeakObject * >( this ),
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 static_cast< cppu::OWeakObject * >( this ),
364 -1 ) ),
365 Environment );
366 // Unreachable
367 }
368
369 if ( !aProperties.hasElements() )
370 {
372 uno::Any( lang::IllegalArgumentException(
373 "No properties!",
374 static_cast< cppu::OWeakObject * >( this ),
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 static_cast< cppu::OWeakObject * >( this ),
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 static_cast< cppu::OWeakObject * >( this ) ) ),
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 static_cast< cppu::OWeakObject * >(
448 this ) ) ),
449 Environment );
450 // Unreachable
451 }
452 }
453
454 ucb::InsertCommandArgument aArg;
455 if ( !( aCommand.Argument >>= aArg ) )
456 {
458 uno::Any( lang::IllegalArgumentException(
459 "Wrong argument type!",
460 static_cast< cppu::OWeakObject * >( this ),
461 -1 ) ),
462 Environment );
463 // Unreachable
464 }
465
466 sal_Int32 nNameClash = aArg.ReplaceExisting
467 ? ucb::NameClash::OVERWRITE
468 : ucb::NameClash::ERROR;
469 insert( aArg.Data, nNameClash, Environment );
470 }
471 else if ( aCommand.Name == "delete" )
472 {
473
474 // delete ( Supported by folders and streams only )
475
476
477 {
478 osl::MutexGuard aGuard( m_aMutex );
479
481 if ( ( eType != FOLDER ) && ( eType != STREAM ) )
482 {
484 uno::Any( ucb::UnsupportedCommandException(
485 "delete command only supported by "
486 "folders and streams!",
487 static_cast< cppu::OWeakObject * >(
488 this ) ) ),
489 Environment );
490 // Unreachable
491 }
492 }
493
494 bool bDeletePhysical = false;
495 aCommand.Argument >>= bDeletePhysical;
496 destroy( bDeletePhysical, Environment );
497
498 // Remove own and all children's persistent data.
499 if ( !removeData() )
500 {
501 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
502 {
503 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
504 }));
506 ucb::IOErrorCode_CANT_WRITE,
507 aArgs,
508 Environment,
509 "Cannot remove persistent data!",
510 this );
511 // Unreachable
512 }
513
514 // Remove own and all children's Additional Core Properties.
516 }
517 else if ( aCommand.Name == "transfer" )
518 {
519
520 // transfer ( Supported by document and folders only )
521
522
523 {
524 osl::MutexGuard aGuard( m_aMutex );
525
527 if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
528 {
530 uno::Any( ucb::UnsupportedCommandException(
531 "transfer command only supported "
532 "by folders and documents!",
533 static_cast< cppu::OWeakObject * >(
534 this ) ) ),
535 Environment );
536 // Unreachable
537 }
538 }
539
540 ucb::TransferInfo aInfo;
541 if ( !( aCommand.Argument >>= aInfo ) )
542 {
543 OSL_FAIL( "Wrong argument type!" );
545 uno::Any( lang::IllegalArgumentException(
546 "Wrong argument type!",
547 static_cast< cppu::OWeakObject * >( this ),
548 -1 ) ),
549 Environment );
550 // Unreachable
551 }
552
553 transfer( aInfo, Environment );
554 }
555 else if ( aCommand.Name == "createNewContent" )
556 {
557
558 // createNewContent ( Supported by document and folders only )
559
560
561 {
562 osl::MutexGuard aGuard( m_aMutex );
563
565 if ( ( eType != FOLDER ) && ( eType != DOCUMENT ) )
566 {
568 uno::Any( ucb::UnsupportedCommandException(
569 "createNewContent command only "
570 "supported by folders and "
571 "documents!",
572 static_cast< cppu::OWeakObject * >(
573 this ) ) ),
574 Environment );
575 // Unreachable
576 }
577 }
578
579 ucb::ContentInfo aInfo;
580 if ( !( aCommand.Argument >>= aInfo ) )
581 {
582 OSL_FAIL( "Wrong argument type!" );
584 uno::Any( lang::IllegalArgumentException(
585 "Wrong argument type!",
586 static_cast< cppu::OWeakObject * >( this ),
587 -1 ) ),
588 Environment );
589 // Unreachable
590 }
591
592 aRet <<= createNewContent( aInfo );
593 }
594 else
595 {
596
597 // Unsupported command
598
599
601 uno::Any( ucb::UnsupportedCommandException(
602 OUString(),
603 static_cast< cppu::OWeakObject * >( this ) ) ),
604 Environment );
605 // Unreachable
606 }
607
608 return aRet;
609}
610
611
612// virtual
613void SAL_CALL Content::abort( sal_Int32 /*CommandId*/ )
614{
615}
616
617
618// XContentCreator methods.
619
620
621// virtual
622uno::Sequence< ucb::ContentInfo > SAL_CALL
624{
626}
627
628
629// virtual
630uno::Reference< ucb::XContent > SAL_CALL
631Content::createNewContent( const ucb::ContentInfo& Info )
632{
634 {
635 osl::Guard< osl::Mutex > aGuard( m_aMutex );
636
637 if ( Info.Type.isEmpty() )
638 return uno::Reference< ucb::XContent >();
639
640 bool bCreateFolder = Info.Type == TDOC_FOLDER_CONTENT_TYPE;
641
642 // streams cannot be created as direct children of document root
643 if ( !bCreateFolder && ( m_aProps.getType() == DOCUMENT ) )
644 {
645 OSL_FAIL( "Content::createNewContent - streams cannot be "
646 "created as direct children of document root!" );
647 return uno::Reference< ucb::XContent >();
648 }
649 if ( !bCreateFolder && Info.Type != TDOC_STREAM_CONTENT_TYPE )
650 {
651 OSL_FAIL( "Content::createNewContent - unsupported type!" );
652 return uno::Reference< ucb::XContent >();
653 }
654
655 OUString aURL = m_xIdentifier->getContentIdentifier();
656
657 OSL_ENSURE( !aURL.isEmpty(),
658 "Content::createNewContent - empty identifier!" );
659
660 if ( ( aURL.lastIndexOf( '/' ) + 1 ) != aURL.getLength() )
661 aURL += "/";
662
663 if ( bCreateFolder )
664 aURL += "New_Folder";
665 else
666 aURL += "New_Stream";
667
668 uno::Reference< ucb::XContentIdentifier > xId
669 = new ::ucbhelper::ContentIdentifier( aURL );
670
671 return create( m_xContext, m_pProvider, xId, Info );
672 }
673 else
674 {
675 OSL_FAIL( "createNewContent called on non-contentcreator object!" );
676 return uno::Reference< ucb::XContent >();
677 }
678}
679
680
681// virtual
683{
684 osl::Guard< osl::Mutex > aGuard( m_aMutex );
685 Uri aUri( m_xIdentifier->getContentIdentifier() );
686 return aUri.getParentUri();
687}
688
689
690uno::Reference< ucb::XContentIdentifier >
691Content::makeNewIdentifier( const OUString& rTitle )
692{
693 osl::Guard< osl::Mutex > aGuard( m_aMutex );
694
695 // Assemble new content identifier...
696 Uri aUri( m_xIdentifier->getContentIdentifier() );
697 OUString aNewURL = aUri.getParentUri() + ::ucb_impl::urihelper::encodeSegment( rTitle );
698
699 return
700 uno::Reference< ucb::XContentIdentifier >(
701 new ::ucbhelper::ContentIdentifier( aNewURL ) );
702}
703
704
706{
707 osl::Guard< osl::Mutex > aGuard( m_aMutex );
708
709 // Only folders (root, documents, folders) have children.
710 if ( !m_aProps.getIsFolder() )
711 return;
712
713 // Obtain a list with a snapshot of all currently instantiated contents
714 // from provider and extract the contents which are direct children
715 // of this content.
716
717 ::ucbhelper::ContentRefList aAllContents;
718 m_xProvider->queryExistingContents( aAllContents );
719
720 OUString aURL = m_xIdentifier->getContentIdentifier();
721 sal_Int32 nURLPos = aURL.lastIndexOf( '/' );
722
723 if ( nURLPos != ( aURL.getLength() - 1 ) )
724 {
725 // No trailing slash found. Append.
726 aURL += "/";
727 }
728
729 sal_Int32 nLen = aURL.getLength();
730
731 for ( const auto& rContent : aAllContents )
732 {
733 ::ucbhelper::ContentImplHelperRef xChild = rContent;
734 OUString aChildURL
735 = xChild->getIdentifier()->getContentIdentifier();
736
737 // Is aURL a prefix of aChildURL?
738 if ( ( aChildURL.getLength() > nLen ) &&
739 ( aChildURL.startsWith( aURL ) ) )
740 {
741 sal_Int32 nPos = aChildURL.indexOf( '/', nLen );
742
743 if ( ( nPos == -1 ) ||
744 ( nPos == ( aChildURL.getLength() - 1 ) ) )
745 {
746 // No further slashes / only a final slash. It's a child!
747 rChildren.emplace_back(
748 static_cast< Content * >( xChild.get() ) );
749 }
750 }
751 }
752}
753
754
756 const uno::Reference< ucb::XContentIdentifier >& xNewId )
757{
758 if ( !xNewId.is() )
759 return false;
760
761 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
762
763 uno::Reference< ucb::XContent > xThis = this;
764
765 // Already persistent?
766 if ( m_eState != PERSISTENT )
767 {
768 OSL_FAIL( "Content::exchangeIdentity - Not persistent!" );
769 return false;
770 }
771
772 // Only folders and streams can be renamed -> exchange identity.
774 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
775 {
776 OSL_FAIL( "Content::exchangeIdentity - "
777 "Not supported by root or document!" );
778 return false;
779 }
780
781 // Exchange own identity.
782
783 // Fail, if a content with given id already exists.
784 if ( !hasData( Uri( xNewId->getContentIdentifier() ) ) )
785 {
786 OUString aOldURL = m_xIdentifier->getContentIdentifier();
787
788 aGuard.clear();
789 if ( exchange( xNewId ) )
790 {
791 if ( eType == FOLDER )
792 {
793 // Process instantiated children...
794
795 ContentRefList aChildren;
796 queryChildren( aChildren );
797
798 for ( const auto& rChild : aChildren )
799 {
800 ContentRef xChild = rChild;
801
802 // Create new content identifier for the child...
803 uno::Reference< ucb::XContentIdentifier > xOldChildId
804 = xChild->getIdentifier();
805 OUString aOldChildURL
806 = xOldChildId->getContentIdentifier();
807 OUString aNewChildURL
808 = aOldChildURL.replaceAt(
809 0,
810 aOldURL.getLength(),
811 xNewId->getContentIdentifier() );
812 uno::Reference< ucb::XContentIdentifier > xNewChildId
813 = new ::ucbhelper::ContentIdentifier( aNewChildURL );
814
815 if ( !xChild->exchangeIdentity( xNewChildId ) )
816 return false;
817 }
818 }
819 return true;
820 }
821 }
822
823 OSL_FAIL( "Content::exchangeIdentity - "
824 "Panic! Cannot exchange identity!" );
825 return false;
826}
827
828
829// static
830uno::Reference< sdbc::XRow > Content::getPropertyValues(
831 const uno::Reference< uno::XComponentContext >& rxContext,
832 const uno::Sequence< beans::Property >& rProperties,
833 ContentProvider* pProvider,
834 const OUString& rContentId )
835{
837 if ( loadData( pProvider, Uri(rContentId), aData ) )
838 {
839 return getPropertyValues(
840 rxContext, rProperties, aData, pProvider, rContentId );
841 }
842 else
843 {
845 = new ::ucbhelper::PropertyValueSet( rxContext );
846
847 for ( const beans::Property& rProp : rProperties )
848 xRow->appendVoid( rProp );
849
850 return xRow;
851 }
852}
853
854
855// static
856uno::Reference< sdbc::XRow > Content::getPropertyValues(
857 const uno::Reference< uno::XComponentContext >& rxContext,
858 const uno::Sequence< beans::Property >& rProperties,
859 const ContentProperties& rData,
860 ContentProvider* pProvider,
861 const OUString& rContentId )
862{
863 // Note: Empty sequence means "get values of all supported properties".
864
866 = new ::ucbhelper::PropertyValueSet( rxContext );
867
868 if ( rProperties.hasElements() )
869 {
870 uno::Reference< beans::XPropertySet > xAdditionalPropSet;
871 bool bTriedToGetAdditionalPropSet = false;
872
873 for ( const beans::Property& rProp : rProperties )
874 {
875 // Process Core properties.
876
877 if ( rProp.Name == "ContentType" )
878 {
879 xRow->appendString ( rProp, rData.getContentType() );
880 }
881 else if ( rProp.Name == "Title" )
882 {
883 xRow->appendString ( rProp, rData.getTitle() );
884 }
885 else if ( rProp.Name == "IsDocument" )
886 {
887 xRow->appendBoolean( rProp, rData.getIsDocument() );
888 }
889 else if ( rProp.Name == "IsFolder" )
890 {
891 xRow->appendBoolean( rProp, rData.getIsFolder() );
892 }
893 else if ( rProp.Name == "CreatableContentsInfo" )
894 {
895 xRow->appendObject(
896 rProp, uno::Any( rData.getCreatableContentsInfo() ) );
897 }
898 else if ( rProp.Name == "Storage" )
899 {
900 // Storage is only supported by folders.
901 ContentType eType = rData.getType();
902 if ( eType == FOLDER )
903 xRow->appendObject(
904 rProp,
905 uno::Any(
906 pProvider->queryStorageClone( rContentId ) ) );
907 else
908 xRow->appendVoid( rProp );
909 }
910 else if ( rProp.Name == "DocumentModel" )
911 {
912 // DocumentModel is only supported by documents.
913 ContentType eType = rData.getType();
914 if ( eType == DOCUMENT )
915 xRow->appendObject(
916 rProp,
917 uno::Any(
918 pProvider->queryDocumentModel( rContentId ) ) );
919 else
920 xRow->appendVoid( rProp );
921 }
922 else
923 {
924 // Not a Core Property! Maybe it's an Additional Core Property?!
925
926 if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
927 {
928 xAdditionalPropSet =
929 pProvider->getAdditionalPropertySet( rContentId,
930 false );
931 bTriedToGetAdditionalPropSet = true;
932 }
933
934 if ( xAdditionalPropSet.is() )
935 {
936 if ( !xRow->appendPropertySetValue(
937 xAdditionalPropSet,
938 rProp ) )
939 {
940 // Append empty entry.
941 xRow->appendVoid( rProp );
942 }
943 }
944 else
945 {
946 // Append empty entry.
947 xRow->appendVoid( rProp );
948 }
949 }
950 }
951 }
952 else
953 {
954 // Append all Core Properties.
955 xRow->appendString (
956 beans::Property( "ContentType",
957 -1,
959 beans::PropertyAttribute::BOUND
960 | beans::PropertyAttribute::READONLY ),
961 rData.getContentType() );
962
963 ContentType eType = rData.getType();
964
965 xRow->appendString (
966 beans::Property( "Title",
967 -1,
969 // Title is read-only for root and documents.
970 beans::PropertyAttribute::BOUND |
971 ( ( eType == ROOT ) || ( eType == DOCUMENT )
972 ? beans::PropertyAttribute::READONLY
973 : 0 ) ),
974 rData.getTitle() );
975 xRow->appendBoolean(
976 beans::Property( "IsDocument",
977 -1,
979 beans::PropertyAttribute::BOUND
980 | beans::PropertyAttribute::READONLY ),
981 rData.getIsDocument() );
982 xRow->appendBoolean(
983 beans::Property( "IsFolder",
984 -1,
986 beans::PropertyAttribute::BOUND
987 | beans::PropertyAttribute::READONLY ),
988 rData.getIsFolder() );
989 xRow->appendObject(
990 beans::Property(
991 "CreatableContentsInfo",
992 -1,
993 cppu::UnoType<uno::Sequence< ucb::ContentInfo >>::get(),
994 beans::PropertyAttribute::BOUND
995 | beans::PropertyAttribute::READONLY ),
997
998 // Storage is only supported by folders.
999 if ( eType == FOLDER )
1000 xRow->appendObject(
1001 beans::Property( "Storage",
1002 -1,
1004 beans::PropertyAttribute::BOUND
1005 | beans::PropertyAttribute::READONLY ),
1006 uno::Any( pProvider->queryStorageClone( rContentId ) ) );
1007
1008 // DocumentModel is only supported by documents.
1009 if ( eType == DOCUMENT )
1010 xRow->appendObject(
1011 beans::Property( "DocumentModel",
1012 -1,
1014 beans::PropertyAttribute::BOUND
1015 | beans::PropertyAttribute::READONLY ),
1016 uno::Any(
1017 pProvider->queryDocumentModel( rContentId ) ) );
1018
1019 // Append all Additional Core Properties.
1020
1021 uno::Reference< beans::XPropertySet > xSet =
1022 pProvider->getAdditionalPropertySet( rContentId, false );
1023 xRow->appendPropertySet( xSet );
1024 }
1025
1026 return xRow;
1027}
1028
1029
1030uno::Reference< sdbc::XRow > Content::getPropertyValues(
1031 const uno::Sequence< beans::Property >& rProperties )
1032{
1033 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1035 rProperties,
1036 m_aProps,
1038 m_xIdentifier->getContentIdentifier() );
1039}
1040
1041
1042uno::Sequence< uno::Any > Content::setPropertyValues(
1043 const uno::Sequence< beans::PropertyValue >& rValues,
1044 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1045{
1046 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1047
1048 uno::Sequence< uno::Any > aRet( rValues.getLength() );
1049 auto aRetRange = asNonConstRange(aRet);
1050 uno::Sequence< beans::PropertyChangeEvent > aChanges( rValues.getLength() );
1051 sal_Int32 nChanged = 0;
1052
1053 beans::PropertyChangeEvent aEvent;
1054 aEvent.Source = static_cast< cppu::OWeakObject * >( this );
1055 aEvent.Further = false;
1056 // aEvent.PropertyName =
1057 aEvent.PropertyHandle = -1;
1058 // aEvent.OldValue =
1059 // aEvent.NewValue =
1060
1061 const beans::PropertyValue* pValues = rValues.getConstArray();
1062 sal_Int32 nCount = rValues.getLength();
1063
1064 uno::Reference< ucb::XPersistentPropertySet > xAdditionalPropSet;
1065 bool bTriedToGetAdditionalPropSet = false;
1066
1067 bool bExchange = false;
1068 OUString aOldTitle;
1069 sal_Int32 nTitlePos = -1;
1070
1071 for ( sal_Int32 n = 0; n < nCount; ++n )
1072 {
1073 const beans::PropertyValue& rValue = pValues[ n ];
1074
1075 if ( rValue.Name == "ContentType" )
1076 {
1077 // Read-only property!
1078 aRetRange[ n ] <<= lang::IllegalAccessException(
1079 "Property is read-only!",
1080 static_cast< cppu::OWeakObject * >( this ) );
1081 }
1082 else if ( rValue.Name == "IsDocument" )
1083 {
1084 // Read-only property!
1085 aRetRange[ n ] <<= lang::IllegalAccessException(
1086 "Property is read-only!",
1087 static_cast< cppu::OWeakObject * >( this ) );
1088 }
1089 else if ( rValue.Name == "IsFolder" )
1090 {
1091 // Read-only property!
1092 aRetRange[ n ] <<= lang::IllegalAccessException(
1093 "Property is read-only!",
1094 static_cast< cppu::OWeakObject * >( this ) );
1095 }
1096 else if ( rValue.Name == "CreatableContentsInfo" )
1097 {
1098 // Read-only property!
1099 aRetRange[ n ] <<= lang::IllegalAccessException(
1100 "Property is read-only!",
1101 static_cast< cppu::OWeakObject * >( this ) );
1102 }
1103 else if ( rValue.Name == "Title" )
1104 {
1105 // Title is read-only for root and documents.
1107 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
1108 {
1109 aRetRange[ n ] <<= lang::IllegalAccessException(
1110 "Property is read-only!",
1111 static_cast< cppu::OWeakObject * >( this ) );
1112 }
1113 else
1114 {
1115 OUString aNewValue;
1116 if ( rValue.Value >>= aNewValue )
1117 {
1118 // No empty titles!
1119 if ( !aNewValue.isEmpty() )
1120 {
1121 if ( aNewValue != m_aProps.getTitle() )
1122 {
1123 // modified title -> modified URL -> exchange !
1124 if ( m_eState == PERSISTENT )
1125 bExchange = true;
1126
1127 aOldTitle = m_aProps.getTitle();
1128 m_aProps.setTitle( aNewValue );
1129
1130 // property change event will be sent later...
1131
1132 // remember position within sequence of values
1133 // (for error handling).
1134 nTitlePos = n;
1135 }
1136 }
1137 else
1138 {
1139 aRetRange[ n ] <<= lang::IllegalArgumentException(
1140 "Empty Title not allowed!",
1141 static_cast< cppu::OWeakObject * >( this ),
1142 -1 );
1143 }
1144 }
1145 else
1146 {
1147 aRetRange[ n ] <<= beans::IllegalTypeException(
1148 "Title Property value has wrong type!",
1149 static_cast< cppu::OWeakObject * >( this ) );
1150 }
1151 }
1152 }
1153 else if ( rValue.Name == "Storage" )
1154 {
1156 if ( eType == FOLDER )
1157 {
1158 aRetRange[ n ] <<= lang::IllegalAccessException(
1159 "Property is read-only!",
1160 static_cast< cppu::OWeakObject * >( this ) );
1161 }
1162 else
1163 {
1164 // Storage is only supported by folders.
1165 aRetRange[ n ] <<= beans::UnknownPropertyException(
1166 "Storage property only supported by folders",
1167 static_cast< cppu::OWeakObject * >( this ) );
1168 }
1169 }
1170 else if ( rValue.Name == "DocumentModel" )
1171 {
1173 if ( eType == DOCUMENT )
1174 {
1175 aRetRange[ n ] <<= lang::IllegalAccessException(
1176 "Property is read-only!",
1177 static_cast< cppu::OWeakObject * >( this ) );
1178 }
1179 else
1180 {
1181 // Storage is only supported by folders.
1182 aRetRange[ n ] <<= beans::UnknownPropertyException(
1183 "DocumentModel property only supported by documents",
1184 static_cast< cppu::OWeakObject * >( this ) );
1185 }
1186 }
1187 else
1188 {
1189 // Not a Core Property! Maybe it's an Additional Core Property?!
1190
1191 if ( !bTriedToGetAdditionalPropSet && !xAdditionalPropSet.is() )
1192 {
1193 xAdditionalPropSet = getAdditionalPropertySet( false );
1194 bTriedToGetAdditionalPropSet = true;
1195 }
1196
1197 if ( xAdditionalPropSet.is() )
1198 {
1199 try
1200 {
1201 uno::Any aOldValue = xAdditionalPropSet->getPropertyValue(
1202 rValue.Name );
1203 if ( aOldValue != rValue.Value )
1204 {
1205 xAdditionalPropSet->setPropertyValue(
1206 rValue.Name, rValue.Value );
1207
1208 aEvent.PropertyName = rValue.Name;
1209 aEvent.OldValue = aOldValue;
1210 aEvent.NewValue = rValue.Value;
1211
1212 aChanges.getArray()[ nChanged ] = aEvent;
1213 nChanged++;
1214 }
1215 }
1216 catch ( beans::UnknownPropertyException const & e )
1217 {
1218 aRetRange[ n ] <<= e;
1219 }
1220 catch ( lang::WrappedTargetException const & e )
1221 {
1222 aRetRange[ n ] <<= e;
1223 }
1224 catch ( beans::PropertyVetoException const & e )
1225 {
1226 aRetRange[ n ] <<= e;
1227 }
1228 catch ( lang::IllegalArgumentException const & e )
1229 {
1230 aRetRange[ n ] <<= e;
1231 }
1232 }
1233 else
1234 {
1235 aRetRange[ n ] <<= uno::Exception(
1236 "No property set for storing the value!",
1237 static_cast< cppu::OWeakObject * >( this ) );
1238 }
1239 }
1240 }
1241
1242 if ( bExchange )
1243 {
1244 uno::Reference< ucb::XContentIdentifier > xOldId
1245 = m_xIdentifier;
1246 uno::Reference< ucb::XContentIdentifier > xNewId
1248
1249 aGuard.clear();
1250 if ( exchangeIdentity( xNewId ) )
1251 {
1252 // Adapt persistent data.
1253 renameData( xOldId, xNewId );
1254
1255 // Adapt Additional Core Properties.
1256 renameAdditionalPropertySet( xOldId->getContentIdentifier(),
1257 xNewId->getContentIdentifier() );
1258 }
1259 else
1260 {
1261 // Roll-back.
1262 m_aProps.setTitle( aOldTitle );
1263 aOldTitle.clear();
1264
1265 // Set error .
1266 aRetRange[ nTitlePos ] <<= uno::Exception(
1267 "Exchange failed!",
1268 static_cast< cppu::OWeakObject * >( this ) );
1269 }
1270 }
1271
1272 if ( !aOldTitle.isEmpty() )
1273 {
1274 aEvent.PropertyName = "Title";
1275 aEvent.OldValue <<= aOldTitle;
1276 aEvent.NewValue <<= m_aProps.getTitle();
1277
1278 aChanges.getArray()[ nChanged ] = aEvent;
1279 nChanged++;
1280 }
1281
1282 if ( nChanged > 0 )
1283 {
1284 // Save changes, if content was already made persistent.
1285 if ( !bExchange && ( m_eState == PERSISTENT ) )
1286 {
1287 if ( !storeData( uno::Reference< io::XInputStream >(), xEnv ) )
1288 {
1289 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1290 {
1291 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1292 }));
1294 ucb::IOErrorCode_CANT_WRITE,
1295 aArgs,
1296 xEnv,
1297 "Cannot store persistent data!",
1298 this );
1299 // Unreachable
1300 }
1301 }
1302
1303 aChanges.realloc( nChanged );
1304
1305 aGuard.clear();
1306 notifyPropertiesChange( aChanges );
1307 }
1308
1309 return aRet;
1310}
1311
1312
1314 const ucb::OpenCommandArgument2& rArg,
1315 const uno::Reference< ucb::XCommandEnvironment >& xEnv )
1316{
1317 if ( rArg.Mode == ucb::OpenMode::ALL ||
1318 rArg.Mode == ucb::OpenMode::FOLDERS ||
1319 rArg.Mode == ucb::OpenMode::DOCUMENTS )
1320 {
1321
1322 // open command for a folder content
1323
1324
1325 uno::Reference< ucb::XDynamicResultSet > xSet
1326 = new DynamicResultSet( m_xContext, this, rArg );
1327 return uno::Any( xSet );
1328 }
1329 else
1330 {
1331
1332 // open command for a document content
1333
1334
1335 if ( ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_NONE ) ||
1336 ( rArg.Mode == ucb::OpenMode::DOCUMENT_SHARE_DENY_WRITE ) )
1337 {
1338 // Currently(?) unsupported.
1340 uno::Any( ucb::UnsupportedOpenModeException(
1341 OUString(),
1342 static_cast< cppu::OWeakObject * >( this ),
1343 sal_Int16( rArg.Mode ) ) ),
1344 xEnv );
1345 // Unreachable
1346 }
1347
1348 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1349
1350 uno::Reference< io::XActiveDataStreamer > xDataStreamer(
1351 rArg.Sink, uno::UNO_QUERY );
1352 if ( xDataStreamer.is() )
1353 {
1354 // May throw CommandFailedException, DocumentPasswordRequest!
1355 uno::Reference< io::XStream > xStream = getStream( xEnv );
1356 if ( !xStream.is() )
1357 {
1358 // No interaction if we are not persistent!
1359 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1360 {
1361 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1362 }));
1364 ucb::IOErrorCode_CANT_READ,
1365 aArgs,
1367 ? xEnv
1368 : uno::Reference< ucb::XCommandEnvironment >(),
1369 "Got no data stream!",
1370 this );
1371 // Unreachable
1372 }
1373
1374 // Done.
1375 xDataStreamer->setStream( xStream );
1376 }
1377 else
1378 {
1379 uno::Reference< io::XOutputStream > xOut( rArg.Sink, uno::UNO_QUERY );
1380 if ( xOut.is() )
1381 {
1382 // PUSH: write data into xOut
1383
1384 // May throw CommandFailedException, DocumentPasswordRequest!
1385 uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
1386 if ( !xIn.is() )
1387 {
1388 // No interaction if we are not persistent!
1389 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1390 {
1391 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1392 }));
1394 ucb::IOErrorCode_CANT_READ,
1395 aArgs,
1397 ? xEnv
1398 : uno::Reference< ucb::XCommandEnvironment >(),
1399 "Got no data stream!",
1400 this );
1401 // Unreachable
1402 }
1403
1404 try
1405 {
1406 uno::Sequence< sal_Int8 > aBuffer;
1407
1408 while (true)
1409 {
1410 sal_Int32 nRead = xIn->readSomeBytes( aBuffer, 65536 );
1411 if (!nRead)
1412 break;
1413 aBuffer.realloc( nRead );
1414 xOut->writeBytes( aBuffer );
1415 }
1416
1417 xOut->closeOutput();
1418 }
1419 catch ( io::NotConnectedException const & )
1420 {
1421 // closeOutput, readSomeBytes, writeBytes
1422 }
1423 catch ( io::BufferSizeExceededException const & )
1424 {
1425 // closeOutput, readSomeBytes, writeBytes
1426 }
1427 catch ( io::IOException const & )
1428 {
1429 // closeOutput, readSomeBytes, writeBytes
1430 }
1431 }
1432 else
1433 {
1434 uno::Reference< io::XActiveDataSink > xDataSink(
1435 rArg.Sink, uno::UNO_QUERY );
1436 if ( xDataSink.is() )
1437 {
1438 // PULL: wait for client read
1439
1440 // May throw CommandFailedException, DocumentPasswordRequest!
1441 uno::Reference< io::XInputStream > xIn = getInputStream( xEnv );
1442 if ( !xIn.is() )
1443 {
1444 // No interaction if we are not persistent!
1445 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1446 {
1447 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1448 }));
1450 ucb::IOErrorCode_CANT_READ,
1451 aArgs,
1453 ? xEnv
1454 : uno::Reference<
1455 ucb::XCommandEnvironment >(),
1456 "Got no data stream!",
1457 this );
1458 // Unreachable
1459 }
1460
1461 // Done.
1462 xDataSink->setInputStream( xIn );
1463 }
1464 else
1465 {
1467 uno::Any(
1468 ucb::UnsupportedDataSinkException(
1469 OUString(),
1470 static_cast< cppu::OWeakObject * >( this ),
1471 rArg.Sink ) ),
1472 xEnv );
1473 // Unreachable
1474 }
1475 }
1476 }
1477 }
1478
1479 return uno::Any();
1480}
1481
1482
1483void Content::insert( const uno::Reference< io::XInputStream >& xData,
1484 sal_Int32 nNameClashResolve,
1485 const uno::Reference<
1486 ucb::XCommandEnvironment > & xEnv )
1487{
1488 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1489
1491
1492 OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
1493 "insert command only supported by streams and folders!" );
1494
1495 Uri aUri( m_xIdentifier->getContentIdentifier() );
1496
1497#if OSL_DEBUG_LEVEL > 0
1498 if ( eType == STREAM )
1499 {
1500 Uri aParentUri( aUri.getParentUri() );
1501 OSL_ENSURE( !aParentUri.isDocument(),
1502 "insert command not supported by streams that are direct "
1503 "children of document root!" );
1504 }
1505#endif
1506
1507 // Check, if all required properties were set.
1508 if ( eType == FOLDER )
1509 {
1510 // Required: Title
1511
1512 if ( m_aProps.getTitle().isEmpty() )
1513 m_aProps.setTitle( aUri.getDecodedName() );
1514 }
1515 else // stream
1516 {
1517 // Required: data
1518
1519 if ( !xData.is() )
1520 {
1522 uno::Any( ucb::MissingInputStreamException(
1523 OUString(),
1524 static_cast< cppu::OWeakObject * >( this ) ) ),
1525 xEnv );
1526 // Unreachable
1527 }
1528
1529 // Required: Title
1530
1531 if ( m_aProps.getTitle().isEmpty() )
1532 m_aProps.setTitle( aUri.getDecodedName() );
1533 }
1534
1535 Uri aNewUri( aUri.getParentUri() + m_aProps.getTitle() );
1536
1537 // Handle possible name clash...
1538 switch ( nNameClashResolve )
1539 {
1540 // fail.
1541 case ucb::NameClash::ERROR:
1542 if ( hasData( aNewUri ) )
1543 {
1545 uno::Any( ucb::NameClashException(
1546 OUString(),
1547 static_cast< cppu::OWeakObject * >( this ),
1548 task::InteractionClassification_ERROR,
1549 m_aProps.getTitle() ) ),
1550 xEnv );
1551 // Unreachable
1552 }
1553 break;
1554
1555 // replace (possibly) existing object.
1556 case ucb::NameClash::OVERWRITE:
1557 break;
1558
1559 // "invent" a new valid title.
1560 case ucb::NameClash::RENAME:
1561 if ( hasData( aNewUri ) )
1562 {
1563 sal_Int32 nTry = 0;
1564
1565 do
1566 {
1567 OUStringBuffer aNew(aNewUri.getUri());
1568 aNew.append( "_" );
1569 aNew.append( ++nTry );
1570 aNewUri.setUri( aNew.makeStringAndClear() );
1571 }
1572 while ( hasData( aNewUri ) && ( nTry < 1000 ) );
1573
1574 if ( nTry == 1000 )
1575 {
1577 uno::Any(
1578 ucb::UnsupportedNameClashException(
1579 "Unable to resolve name clash!",
1580 static_cast< cppu::OWeakObject * >( this ),
1581 nNameClashResolve ) ),
1582 xEnv );
1583 // Unreachable
1584 }
1585 else
1586 {
1587 OUStringBuffer aNewTitle(m_aProps.getTitle());
1588 aNewTitle.append( "_" );
1589 aNewTitle.append( ++nTry );
1590 m_aProps.setTitle( aNewTitle.makeStringAndClear() );
1591 }
1592 }
1593 break;
1594
1595 case ucb::NameClash::KEEP: // deprecated
1596 case ucb::NameClash::ASK:
1597 default:
1598 if ( hasData( aNewUri ) )
1599 {
1601 uno::Any(
1602 ucb::UnsupportedNameClashException(
1603 OUString(),
1604 static_cast< cppu::OWeakObject * >( this ),
1605 nNameClashResolve ) ),
1606 xEnv );
1607 // Unreachable
1608 }
1609 break;
1610 }
1611
1612 // Identifier changed?
1613 bool bNewId = ( aUri != aNewUri );
1614
1615 if ( bNewId )
1616 {
1618 = new ::ucbhelper::ContentIdentifier( aNewUri.getUri() );
1619 }
1620
1621 if ( !storeData( xData, xEnv ) )
1622 {
1623 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1624 {
1625 {"Uri", uno::Any(m_xIdentifier->getContentIdentifier())}
1626 }));
1628 ucb::IOErrorCode_CANT_WRITE,
1629 aArgs,
1630 xEnv,
1631 "Cannot store persistent data!",
1632 this );
1633 // Unreachable
1634 }
1635
1637
1638 if ( bNewId )
1639 {
1640 //loadData( m_pProvider, m_aUri, m_aProps );
1641
1642 aGuard.clear();
1643 inserted();
1644 }
1645}
1646
1647
1648void Content::destroy( bool bDeletePhysical,
1649 const uno::Reference<
1650 ucb::XCommandEnvironment > & xEnv )
1651{
1652 // @@@ take care about bDeletePhysical -> trashcan support
1653
1654 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1655
1657
1658 OSL_ENSURE( ( eType == FOLDER ) || ( eType == STREAM ),
1659 "delete command only supported by streams and folders!" );
1660
1661 uno::Reference< ucb::XContent > xThis = this;
1662
1663 // Persistent?
1664 if ( m_eState != PERSISTENT )
1665 {
1667 uno::Any( ucb::UnsupportedCommandException(
1668 "Not persistent!",
1669 static_cast< cppu::OWeakObject * >( this ) ) ),
1670 xEnv );
1671 // Unreachable
1672 }
1673
1674 m_eState = DEAD;
1675
1676 aGuard.clear();
1677 deleted();
1678
1679 if ( eType == FOLDER )
1680 {
1681 // Process instantiated children...
1682
1683 ContentRefList aChildren;
1684 queryChildren( aChildren );
1685
1686 for ( auto& rChild : aChildren )
1687 {
1688 rChild->destroy( bDeletePhysical, xEnv );
1689 }
1690 }
1691}
1692
1693
1695{
1696 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1697
1698 m_eState = DEAD;
1699
1700 // @@@ anything else to reset or such?
1701
1702 // callback follows!
1703 aGuard.clear();
1704
1705 // Propagate destruction to content event listeners
1706 // Remove this from provider's content list.
1707 deleted();
1708}
1709
1710
1711uno::Reference< ucb::XContent >
1712Content::queryChildContent( std::u16string_view rRelativeChildUri )
1713{
1714 osl::Guard< osl::Mutex > aGuard( m_aMutex );
1715
1716 const OUString aMyId = getIdentifier()->getContentIdentifier();
1717 OUStringBuffer aBuf( aMyId );
1718 if ( !aMyId.endsWith("/") )
1719 aBuf.append( "/" );
1720 if ( !o3tl::starts_with(rRelativeChildUri, u"/") )
1721 aBuf.append( rRelativeChildUri );
1722 else
1723 aBuf.append( rRelativeChildUri.substr(1) );
1724
1725 uno::Reference< ucb::XContentIdentifier > xChildId
1726 = new ::ucbhelper::ContentIdentifier( aBuf.makeStringAndClear() );
1727
1728 uno::Reference< ucb::XContent > xChild;
1729 try
1730 {
1731 xChild = m_pProvider->queryContent( xChildId );
1732 }
1733 catch ( ucb::IllegalIdentifierException const & )
1734 {
1735 // handled below.
1736 }
1737
1738 OSL_ENSURE( xChild.is(),
1739 "Content::queryChildContent - unable to create child content!" );
1740 return xChild;
1741}
1742
1743
1744void Content::notifyChildRemoved( std::u16string_view rRelativeChildUri )
1745{
1746 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1747
1748 // Ugly! Need to create child content object, just to fill event properly.
1749 uno::Reference< ucb::XContent > xChild
1750 = queryChildContent( rRelativeChildUri );
1751
1752 if ( !xChild.is() )
1753 return;
1754
1755 // callback follows!
1756 aGuard.clear();
1757
1758 // Notify "REMOVED" event.
1759 ucb::ContentEvent aEvt(
1760 static_cast< cppu::OWeakObject * >( this ),
1761 ucb::ContentAction::REMOVED,
1762 xChild,
1763 getIdentifier() );
1764 notifyContentEvent( aEvt );
1765}
1766
1767
1768void Content::notifyChildInserted( std::u16string_view rRelativeChildUri )
1769{
1770 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1771
1772 // Ugly! Need to create child content object, just to fill event properly.
1773 uno::Reference< ucb::XContent > xChild
1774 = queryChildContent( rRelativeChildUri );
1775
1776 if ( !xChild.is() )
1777 return;
1778
1779 // callback follows!
1780 aGuard.clear();
1781
1782 // Notify "INSERTED" event.
1783 ucb::ContentEvent aEvt(
1784 static_cast< cppu::OWeakObject * >( this ),
1785 ucb::ContentAction::INSERTED,
1786 xChild,
1787 getIdentifier() );
1788 notifyContentEvent( aEvt );
1789}
1790
1791
1793 const ucb::TransferInfo& rInfo,
1794 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
1795{
1796 osl::ClearableGuard< osl::Mutex > aGuard( m_aMutex );
1797
1798 // Persistent?
1799 if ( m_eState != PERSISTENT )
1800 {
1802 uno::Any( ucb::UnsupportedCommandException(
1803 "Not persistent!",
1804 static_cast< cppu::OWeakObject * >( this ) ) ),
1805 xEnv );
1806 // Unreachable
1807 }
1808
1809 // Does source URI scheme match? Only vnd.sun.star.tdoc is supported.
1810
1811 if ( rInfo.SourceURL.getLength() < TDOC_URL_SCHEME_LENGTH + 2 )
1812 {
1813 // Invalid length (to short).
1815 uno::Any( ucb::InteractiveBadTransferURLException(
1816 OUString(),
1817 static_cast< cppu::OWeakObject * >( this ) ) ),
1818 xEnv );
1819 // Unreachable
1820 }
1821
1822 OUString aScheme
1823 = rInfo.SourceURL.copy( 0, TDOC_URL_SCHEME_LENGTH + 2 )
1824 .toAsciiLowerCase();
1825 if ( aScheme != TDOC_URL_SCHEME ":/" )
1826 {
1827 // Invalid scheme.
1829 uno::Any( ucb::InteractiveBadTransferURLException(
1830 OUString(),
1831 static_cast< cppu::OWeakObject * >( this ) ) ),
1832 xEnv );
1833 // Unreachable
1834 }
1835
1836 // Does source URI describe a tdoc folder or stream?
1837 Uri aSourceUri( rInfo.SourceURL );
1838 if ( !aSourceUri.isValid() )
1839 {
1841 uno::Any( lang::IllegalArgumentException(
1842 "Invalid source URI! Syntax!",
1843 static_cast< cppu::OWeakObject * >( this ),
1844 -1 ) ),
1845 xEnv );
1846 // Unreachable
1847 }
1848
1849 if ( aSourceUri.isRoot() || aSourceUri.isDocument() )
1850 {
1852 uno::Any( lang::IllegalArgumentException(
1853 "Invalid source URI! Must describe a folder or stream!",
1854 static_cast< cppu::OWeakObject * >( this ),
1855 -1 ) ),
1856 xEnv );
1857 // Unreachable
1858 }
1859
1860 // Is source not a parent of me / not me?
1861 OUString aId = m_xIdentifier->getContentIdentifier();
1862 sal_Int32 nPos = aId.lastIndexOf( '/' );
1863 if ( nPos != ( aId.getLength() - 1 ) )
1864 {
1865 // No trailing slash found. Append.
1866 aId += "/";
1867 }
1868
1869 if ( rInfo.SourceURL.getLength() <= aId.getLength() )
1870 {
1871 if ( aId.startsWith( rInfo.SourceURL ) )
1872 {
1873 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1874 {
1875 {"Uri", uno::Any(rInfo.SourceURL)}
1876 }));
1878 ucb::IOErrorCode_RECURSIVE,
1879 aArgs,
1880 xEnv,
1881 "Target is equal to or is a child of source!",
1882 this );
1883 // Unreachable
1884 }
1885 }
1886
1887 if ( m_aProps.getType() == DOCUMENT )
1888 {
1889 bool bOK = false;
1890
1891 uno::Reference< embed::XStorage > xStorage
1893 aSourceUri.getParentUri(), READ_WRITE_NOCREATE );
1894 if ( xStorage.is() )
1895 {
1896 try
1897 {
1898 if ( xStorage->isStreamElement( aSourceUri.getDecodedName() ) )
1899 {
1901 uno::Any( lang::IllegalArgumentException(
1902 "Invalid source URI! "
1903 "Streams cannot be created as "
1904 "children of document root!",
1905 static_cast< cppu::OWeakObject * >(
1906 this ),
1907 -1 ) ),
1908 xEnv );
1909 // Unreachable
1910 }
1911 bOK = true;
1912 }
1913 catch ( container::NoSuchElementException const & )
1914 {
1915 // handled below.
1916 }
1917 catch ( lang::IllegalArgumentException const & )
1918 {
1919 // handled below.
1920 }
1921 catch ( embed::InvalidStorageException const & )
1922 {
1923 // handled below.
1924 }
1925 }
1926
1927 if ( !bOK )
1928 {
1930 uno::Any( lang::IllegalArgumentException(
1931 "Invalid source URI! Unable to determine source type!",
1932 static_cast< cppu::OWeakObject * >( this ),
1933 -1 ) ),
1934 xEnv );
1935 // Unreachable
1936 }
1937 }
1938
1939
1940 // Copy data.
1941
1942
1943 OUString aNewName( !rInfo.NewTitle.isEmpty()
1944 ? rInfo.NewTitle
1945 : aSourceUri.getDecodedName() );
1946
1947 if ( !copyData( aSourceUri, aNewName ) )
1948 {
1949 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1950 {
1951 {"Uri", uno::Any(rInfo.SourceURL)}
1952 }));
1954 ucb::IOErrorCode_CANT_WRITE,
1955 aArgs,
1956 xEnv,
1957 "Cannot copy data!",
1958 this );
1959 // Unreachable
1960 }
1961
1962
1963 // Copy own and all children's Additional Core Properties.
1964
1965
1966 OUString aTargetUri = m_xIdentifier->getContentIdentifier();
1967 if ( ( aTargetUri.lastIndexOf( '/' ) + 1 ) != aTargetUri.getLength() )
1968 aTargetUri += "/";
1969
1970 if ( !rInfo.NewTitle.isEmpty() )
1971 aTargetUri += ::ucb_impl::urihelper::encodeSegment( rInfo.NewTitle );
1972 else
1973 aTargetUri += aSourceUri.getName();
1974
1975 if ( !copyAdditionalPropertySet( aSourceUri.getUri(), aTargetUri ) )
1976 {
1977 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
1978 {
1979 {"Uri", uno::Any(rInfo.SourceURL)}
1980 }));
1982 ucb::IOErrorCode_CANT_WRITE,
1983 aArgs,
1984 xEnv,
1985 "Cannot copy additional properties!",
1986 this );
1987 // Unreachable
1988 }
1989
1990
1991 // Propagate new content.
1992
1993
1995 try
1996 {
1997 uno::Reference< ucb::XContentIdentifier > xTargetId
1998 = new ::ucbhelper::ContentIdentifier( aTargetUri );
1999
2000 // Note: The static cast is okay here, because its sure that
2001 // m_xProvider is always the WebDAVContentProvider.
2002 xTarget = static_cast< Content * >(
2003 m_pProvider->queryContent( xTargetId ).get() );
2004
2005 }
2006 catch ( ucb::IllegalIdentifierException const & )
2007 {
2008 // queryContent
2009 }
2010
2011 if ( !xTarget.is() )
2012 {
2013 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2014 {
2015 {"Uri", uno::Any(aTargetUri)}
2016 }));
2018 ucb::IOErrorCode_CANT_READ,
2019 aArgs,
2020 xEnv,
2021 "Cannot instantiate target object!",
2022 this );
2023 // Unreachable
2024 }
2025
2026 // Announce transferred content in its new folder.
2027 xTarget->inserted();
2028
2029
2030 // Remove source, if requested
2031
2032
2033 if ( !rInfo.MoveData )
2034 return;
2035
2037 try
2038 {
2039 uno::Reference< ucb::XContentIdentifier >
2040 xSourceId = new ::ucbhelper::ContentIdentifier( rInfo.SourceURL );
2041
2042 // Note: The static cast is okay here, because its sure
2043 // that m_xProvider is always the ContentProvider.
2044 xSource = static_cast< Content * >(
2045 m_xProvider->queryContent( xSourceId ).get() );
2046 }
2047 catch ( ucb::IllegalIdentifierException const & )
2048 {
2049 // queryContent
2050 }
2051
2052 if ( !xSource.is() )
2053 {
2054 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2055 {
2056 {"Uri", uno::Any(rInfo.SourceURL)}
2057 }));
2059 ucb::IOErrorCode_CANT_READ,
2060 aArgs,
2061 xEnv,
2062 "Cannot instantiate target object!",
2063 this );
2064 // Unreachable
2065 }
2066
2067 // Propagate destruction (recursively).
2068 xSource->destroy( true, xEnv );
2069
2070 // Remove all persistent data of source and its children.
2071 if ( !xSource->removeData() )
2072 {
2073 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2074 {
2075 {"Uri", uno::Any(rInfo.SourceURL)}
2076 }));
2078 ucb::IOErrorCode_CANT_WRITE,
2079 aArgs,
2080 xEnv,
2081 "Cannot remove persistent data of source object!",
2082 this );
2083 // Unreachable
2084 }
2085
2086 // Remove own and all children's Additional Core Properties.
2087 if ( xSource->removeAdditionalPropertySet() )
2088 return;
2089
2090 uno::Sequence<uno::Any> aArgs(comphelper::InitAnyPropertySequence(
2091 {
2092 {"Uri", uno::Any(rInfo.SourceURL)}
2093 }));
2095 ucb::IOErrorCode_CANT_WRITE,
2096 aArgs,
2097 xEnv,
2098 "Cannot remove additional properties of source object!",
2099 this );
2100 // Unreachable
2101}
2102
2103
2104//static
2105bool Content::hasData( ContentProvider const * pProvider, const Uri & rUri )
2106{
2107 if ( rUri.isRoot() )
2108 {
2109 return true; // root has no storage
2110 }
2111 else if ( rUri.isDocument() )
2112 {
2113 uno::Reference< embed::XStorage > xStorage
2114 = pProvider->queryStorage( rUri.getUri(), READ );
2115 return xStorage.is();
2116 }
2117 else
2118 {
2119 // folder or stream
2120
2121 // Ask parent storage. In case that rUri describes a stream,
2122 // ContentProvider::queryStorage( rUri ) would return null.
2123
2124 uno::Reference< embed::XStorage > xStorage
2125 = pProvider->queryStorage( rUri.getParentUri(), READ );
2126
2127 if ( !xStorage.is() )
2128 return false;
2129
2130 return xStorage->hasByName( rUri.getDecodedName() );
2131 }
2132}
2133
2134
2135//static
2136bool Content::loadData( ContentProvider const * pProvider,
2137 const Uri & rUri,
2138 ContentProperties& rProps )
2139{
2140 if ( rUri.isRoot() ) // root has no storage, but can always be created
2141 {
2142 rProps
2144 ROOT, pProvider->queryStorageTitle( rUri.getUri() ) );
2145 }
2146 else if ( rUri.isDocument() ) // document must have storage
2147 {
2148 uno::Reference< embed::XStorage > xStorage
2149 = pProvider->queryStorage( rUri.getUri(), READ );
2150
2151 if ( !xStorage.is() )
2152 return false;
2153
2154 rProps
2156 DOCUMENT, pProvider->queryStorageTitle( rUri.getUri() ) );
2157 }
2158 else // stream or folder; stream has no storage; folder has storage
2159 {
2160 uno::Reference< embed::XStorage > xStorage
2161 = pProvider->queryStorage( rUri.getParentUri(), READ );
2162
2163 if ( !xStorage.is() )
2164 return false;
2165
2166 // Check whether exists at all, is stream or folder
2167 try
2168 {
2169 // return: true -> folder
2170 // return: false -> stream
2171 // NoSuchElementException -> neither folder nor stream
2172 bool bIsFolder
2173 = xStorage->isStorageElement( rUri.getDecodedName() );
2174
2175 rProps
2177 bIsFolder ? FOLDER : STREAM,
2178 pProvider->queryStorageTitle( rUri.getUri() ) );
2179 }
2180 catch ( container::NoSuchElementException const & )
2181 {
2182 // there is no element with such name
2183 //OSL_ENSURE( false, "Caught NoSuchElementException!" );
2184 return false;
2185 }
2186 catch ( lang::IllegalArgumentException const & )
2187 {
2188 // an illegal argument is provided
2189 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2190 return false;
2191 }
2192 catch ( embed::InvalidStorageException const & )
2193 {
2194 // this storage is in invalid state for any reason
2195 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2196 return false;
2197 }
2198 }
2199 return true;
2200}
2201
2202
2203bool Content::storeData( const uno::Reference< io::XInputStream >& xData,
2204 const uno::Reference<
2205 ucb::XCommandEnvironment >& xEnv )
2206{
2207 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2208
2210 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2211 {
2212 OSL_FAIL( "storeData not supported by root and documents!" );
2213 return false;
2214 }
2215
2216 Uri aUri( m_xIdentifier->getContentIdentifier() );
2217
2218 if ( eType == FOLDER )
2219 {
2220 uno::Reference< embed::XStorage > xStorage
2221 = m_pProvider->queryStorage( aUri.getUri(), READ_WRITE_CREATE );
2222
2223 if ( !xStorage.is() )
2224 return false;
2225
2226 uno::Reference< beans::XPropertySet > xPropSet(
2227 xStorage, uno::UNO_QUERY );
2228 OSL_ENSURE( xPropSet.is(),
2229 "Content::storeData - Got no XPropertySet interface!" );
2230 if ( !xPropSet.is() )
2231 return false;
2232
2233 try
2234 {
2235 // According to MBA, if no mediatype is set, folder and all
2236 // its contents will be lost on save of the document!!!
2237 xPropSet->setPropertyValue(
2238 "MediaType",
2239 uno::Any(
2240 OUString( // @@@ better mediatype
2241 "application/binary" ) ) );
2242 }
2243 catch ( beans::UnknownPropertyException const & )
2244 {
2245 OSL_FAIL( "Property MediaType not supported!" );
2246 return false;
2247 }
2248 catch ( beans::PropertyVetoException const & )
2249 {
2250 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2251 return false;
2252 }
2253 catch ( lang::IllegalArgumentException const & )
2254 {
2255 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2256 return false;
2257 }
2258 catch ( lang::WrappedTargetException const & )
2259 {
2260 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2261 return false;
2262 }
2263
2264 if ( !commitStorage( xStorage ) )
2265 return false;
2266 }
2267 else if ( eType == STREAM )
2268 {
2269 // stream
2270
2271 // Important: Parent storage and output stream must be kept alive until
2272 // changes have been committed!
2273 uno::Reference< embed::XStorage > xStorage
2275 aUri.getParentUri(), READ_WRITE_CREATE );
2276 uno::Reference< io::XOutputStream > xOut;
2277
2278 if ( !xStorage.is() )
2279 return false;
2280
2281 if ( xData.is() )
2282 {
2283 // May throw CommandFailedException, DocumentPasswordRequest!
2284 xOut = getTruncatedOutputStream( xEnv );
2285
2286 OSL_ENSURE( xOut.is(), "No target data stream!" );
2287
2288 try
2289 {
2290 uno::Sequence< sal_Int8 > aBuffer;
2291 while (true)
2292 {
2293 sal_Int32 nRead = xData->readSomeBytes( aBuffer, 65536 );
2294 if (!nRead)
2295 break;
2296 aBuffer.realloc( nRead );
2297 xOut->writeBytes( aBuffer );
2298 }
2299
2300 closeOutputStream( xOut );
2301 }
2302 catch ( io::NotConnectedException const & )
2303 {
2304 // readSomeBytes, writeBytes
2305 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2306 closeOutputStream( xOut );
2307 return false;
2308 }
2309 catch ( io::BufferSizeExceededException const & )
2310 {
2311 // readSomeBytes, writeBytes
2312 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2313 closeOutputStream( xOut );
2314 return false;
2315 }
2316 catch ( io::IOException const & )
2317 {
2318 // readSomeBytes, writeBytes
2319 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2320 closeOutputStream( xOut );
2321 return false;
2322 }
2323 catch ( ... )
2324 {
2325 closeOutputStream( xOut );
2326 throw;
2327 }
2328 }
2329
2330 // Commit changes.
2331 if ( !commitStorage( xStorage ) )
2332 return false;
2333 }
2334 else
2335 {
2336 OSL_FAIL( "Unknown content type!" );
2337 return false;
2338 }
2339 return true;
2340}
2341
2342
2344 const uno::Reference< ucb::XContentIdentifier >& xOldId,
2345 const uno::Reference< ucb::XContentIdentifier >& xNewId )
2346{
2347 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2348
2350 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2351 {
2352 OSL_FAIL( "renameData not supported by root and documents!" );
2353 return;
2354 }
2355
2356 Uri aOldUri( xOldId->getContentIdentifier() );
2357 uno::Reference< embed::XStorage > xStorage
2359 aOldUri.getParentUri(), READ_WRITE_NOCREATE );
2360
2361 if ( !xStorage.is() )
2362 return;
2363
2364 try
2365 {
2366 Uri aNewUri( xNewId->getContentIdentifier() );
2367 xStorage->renameElement(
2368 aOldUri.getDecodedName(), aNewUri.getDecodedName() );
2369 }
2370 catch ( embed::InvalidStorageException const & )
2371 {
2372 // this storage is in invalid state for any reason
2373 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2374 return;
2375 }
2376 catch ( lang::IllegalArgumentException const & )
2377 {
2378 // an illegal argument is provided
2379 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2380 return;
2381 }
2382 catch ( container::NoSuchElementException const & )
2383 {
2384 // there is no element with old name in this storage
2385 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2386 return;
2387 }
2388 catch ( container::ElementExistException const & )
2389 {
2390 // an element with new name already exists in this storage
2391 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2392 return;
2393 }
2394 catch ( io::IOException const & )
2395 {
2396 // in case of io errors during renaming
2397 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2398 return;
2399 }
2400 catch ( embed::StorageWrappedTargetException const & )
2401 {
2402 // wraps other exceptions
2403 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2404 return;
2405 }
2406
2407 commitStorage( xStorage );
2408}
2409
2410
2412{
2413 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2414
2416 if ( ( eType == ROOT ) || ( eType == DOCUMENT ) )
2417 {
2418 OSL_FAIL( "removeData not supported by root and documents!" );
2419 return false;
2420 }
2421
2422 Uri aUri( m_xIdentifier->getContentIdentifier() );
2423 uno::Reference< embed::XStorage > xStorage
2426
2427 if ( !xStorage.is() )
2428 return false;
2429
2430 try
2431 {
2432 xStorage->removeElement( aUri.getDecodedName() );
2433 }
2434 catch ( embed::InvalidStorageException const & )
2435 {
2436 // this storage is in invalid state for any reason
2437 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2438 return false;
2439 }
2440 catch ( lang::IllegalArgumentException const & )
2441 {
2442 // an illegal argument is provided
2443 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2444 return false;
2445 }
2446 catch ( container::NoSuchElementException const & )
2447 {
2448 // there is no element with this name in this storage
2449 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2450 return false;
2451 }
2452 catch ( io::IOException const & )
2453 {
2454 // in case of io errors during renaming
2455 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2456 return false;
2457 }
2458 catch ( embed::StorageWrappedTargetException const & )
2459 {
2460 // wraps other exceptions
2461 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2462 return false;
2463 }
2464
2465 return commitStorage( xStorage );
2466}
2467
2468
2469bool Content::copyData( const Uri & rSourceUri, const OUString & rNewName )
2470{
2471 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2472
2474 if ( ( eType == ROOT ) || ( eType == STREAM ) )
2475 {
2476 OSL_FAIL( "copyData not supported by root and streams!" );
2477 return false;
2478 }
2479
2480 Uri aDestUri( m_xIdentifier->getContentIdentifier() );
2481 uno::Reference< embed::XStorage > xDestStorage
2483
2484 if ( !xDestStorage.is() )
2485 return false;
2486
2487 uno::Reference< embed::XStorage > xSourceStorage
2488 = m_pProvider->queryStorage( rSourceUri.getParentUri(), READ );
2489
2490 if ( !xSourceStorage.is() )
2491 return false;
2492
2493 try
2494 {
2495 xSourceStorage->copyElementTo( rSourceUri.getDecodedName(),
2496 xDestStorage,
2497 rNewName );
2498 }
2499 catch ( embed::InvalidStorageException const & )
2500 {
2501 // this storage is in invalid state for any reason
2502 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2503 return false;
2504 }
2505 catch ( lang::IllegalArgumentException const & )
2506 {
2507 // an illegal argument is provided
2508 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2509 return false;
2510 }
2511 catch ( container::NoSuchElementException const & )
2512 {
2513 // there is no element with this name in this storage
2514 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2515 return false;
2516 }
2517 catch ( container::ElementExistException const & )
2518 {
2519 // there is no element with this name in this storage
2520 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2521 return false;
2522 }
2523 catch ( io::IOException const & )
2524 {
2525 // in case of io errors during renaming
2526 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2527 return false;
2528 }
2529 catch ( embed::StorageWrappedTargetException const & )
2530 {
2531 // wraps other exceptions
2532 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2533 return false;
2534 }
2535
2536 return commitStorage( xDestStorage );
2537}
2538
2539
2540// static
2541bool Content::commitStorage( const uno::Reference< embed::XStorage > & xStorage )
2542{
2543 // Commit changes
2544 uno::Reference< embed::XTransactedObject > xTO( xStorage, uno::UNO_QUERY );
2545
2546 OSL_ENSURE( xTO.is(),
2547 "Required interface css.embed.XTransactedObject missing!" );
2548 try
2549 {
2550 xTO->commit();
2551 }
2552 catch ( io::IOException const & )
2553 {
2554 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2555 return false;
2556 }
2557 catch ( lang::WrappedTargetException const & )
2558 {
2559 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2560 return false;
2561 }
2562
2563 return true;
2564}
2565
2566
2567// static
2569 const uno::Reference< io::XOutputStream > & xOut )
2570{
2571 if ( xOut.is() )
2572 {
2573 try
2574 {
2575 xOut->closeOutput();
2576 return true;
2577 }
2578 catch ( io::NotConnectedException const & )
2579 {
2580 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2581 }
2582 catch ( io::BufferSizeExceededException const & )
2583 {
2584 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2585 }
2586 catch ( io::IOException const & )
2587 {
2588 TOOLS_WARN_EXCEPTION("ucb.ucp", "");
2589 }
2590 }
2591 return false;
2592}
2593
2596static OUString obtainPassword(
2597 const OUString & rName,
2598 task::PasswordRequestMode eMode,
2599 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2600{
2602 = new DocumentPasswordRequest( eMode, rName );
2603
2604 if ( xEnv.is() )
2605 {
2606 uno::Reference< task::XInteractionHandler > xIH
2607 = xEnv->getInteractionHandler();
2608 if ( xIH.is() )
2609 {
2610 xIH->handle( xRequest );
2611
2613 = xRequest->getSelection();
2614
2615 if ( xSelection.is() )
2616 {
2617 // Handler handled the request.
2618 uno::Reference< task::XInteractionAbort > xAbort(
2619 xSelection.get(), uno::UNO_QUERY );
2620 if ( xAbort.is() )
2621 {
2622 throw ucb::CommandFailedException(
2623 "Abort requested by Interaction Handler.",
2624 uno::Reference< uno::XInterface >(),
2625 xRequest->getRequest() );
2626 }
2627
2628 uno::Reference< task::XInteractionPassword > xPassword(
2629 xSelection.get(), uno::UNO_QUERY );
2630 if ( xPassword.is() )
2631 {
2632 return xPassword->getPassword();
2633 }
2634
2635 // Unknown selection. Should never happen.
2636 throw ucb::CommandFailedException(
2637 "Interaction Handler selected unknown continuation!",
2638 uno::Reference< uno::XInterface >(),
2639 xRequest->getRequest() );
2640 }
2641 }
2642 }
2643
2644 // No IH or IH did not handle exception.
2645 task::DocumentPasswordRequest aRequest;
2646 xRequest->getRequest() >>= aRequest;
2647 throw aRequest;
2648}
2649
2650
2651uno::Reference< io::XInputStream > Content::getInputStream(
2652 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2653{
2654 OUString aUri;
2655 OUString aPassword;
2656 bool bPasswordRequested = false;
2657
2658 {
2659 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2660
2661 OSL_ENSURE( m_aProps.getType() == STREAM,
2662 "Content::getInputStream - content is no stream!" );
2663
2664 aUri = Uri( m_xIdentifier->getContentIdentifier() ).getUri();
2665 }
2666
2667 for ( ;; )
2668 {
2669 try
2670 {
2671 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2672 return m_pProvider->queryInputStream( aUri, aPassword );
2673 }
2674 catch ( packages::WrongPasswordException const & )
2675 {
2676 // Obtain (new) password.
2677 aPassword
2678 = obtainPassword( aUri, /* @@@ find better title */
2679 bPasswordRequested
2680 ? task::PasswordRequestMode_PASSWORD_REENTER
2681 : task::PasswordRequestMode_PASSWORD_ENTER,
2682 xEnv );
2683 bPasswordRequested = true;
2684 }
2685 }
2686}
2687
2691static uno::Reference< io::XOutputStream > lcl_getTruncatedOutputStream(
2692 const OUString & rUri,
2693 ContentProvider const * pProvider,
2694 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2695{
2696 OUString aPassword;
2697 bool bPasswordRequested = false;
2698 for ( ;; )
2699 {
2700 try
2701 {
2702 return pProvider->queryOutputStream(
2703 rUri, aPassword, true /* truncate */ );
2704 }
2705 catch ( packages::WrongPasswordException const & )
2706 {
2707 // Obtain (new) password.
2708 aPassword
2709 = obtainPassword( rUri, /* @@@ find better title */
2710 bPasswordRequested
2711 ? task::PasswordRequestMode_PASSWORD_REENTER
2712 : task::PasswordRequestMode_PASSWORD_ENTER,
2713 xEnv );
2714 bPasswordRequested = true;
2715 }
2716 }
2717}
2718
2719
2720uno::Reference< io::XOutputStream > Content::getTruncatedOutputStream(
2721 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2722{
2723 OSL_ENSURE( m_aProps.getType() == STREAM,
2724 "Content::getTruncatedOutputStream - content is no stream!" );
2725
2727 Uri( m_xIdentifier->getContentIdentifier() ).getUri(),
2729 xEnv );
2730}
2731
2732
2733uno::Reference< io::XStream > Content::getStream(
2734 const uno::Reference< ucb::XCommandEnvironment > & xEnv )
2735{
2736 osl::Guard< osl::Mutex > aGuard( m_aMutex );
2737
2738 OSL_ENSURE( m_aProps.getType() == STREAM,
2739 "Content::getStream - content is no stream!" );
2740
2741 OUString aUri( Uri( m_xIdentifier->getContentIdentifier() ).getUri() );
2742 OUString aPassword;
2743 bool bPasswordRequested = false;
2744 for ( ;; )
2745 {
2746 try
2747 {
2748 return m_pProvider->queryStream(
2749 aUri, aPassword, false /* no truncate */ );
2750 }
2751 catch ( packages::WrongPasswordException const & )
2752 {
2753 // Obtain (new) password.
2754 aPassword
2755 = obtainPassword( aUri, /* @@@ find better title */
2756 bPasswordRequested
2757 ? task::PasswordRequestMode_PASSWORD_REENTER
2758 : task::PasswordRequestMode_PASSWORD_ENTER,
2759 xEnv );
2760 bPasswordRequested = true;
2761 }
2762 }
2763}
2764
2765
2766// ContentProperties Implementation.
2767
2768
2769uno::Sequence< ucb::ContentInfo >
2771{
2772 if ( isContentCreator() )
2773 {
2774 uno::Sequence< beans::Property > aProps( 1 );
2775 aProps.getArray()[ 0 ] = beans::Property(
2776 "Title",
2777 -1,
2779 beans::PropertyAttribute::BOUND );
2780
2781 if ( getType() == DOCUMENT )
2782 {
2783 // streams cannot be created as direct children of document root
2784 uno::Sequence< ucb::ContentInfo > aSeq( 1 );
2785
2786 // Folder.
2787 aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
2788 aSeq.getArray()[ 0 ].Attributes = ucb::ContentInfoAttribute::KIND_FOLDER;
2789 aSeq.getArray()[ 0 ].Properties = aProps;
2790
2791 return aSeq;
2792 }
2793 else
2794 {
2795 uno::Sequence< ucb::ContentInfo > aSeq( 2 );
2796
2797 // Folder.
2798 aSeq.getArray()[ 0 ].Type = TDOC_FOLDER_CONTENT_TYPE;
2799 aSeq.getArray()[ 0 ].Attributes
2800 = ucb::ContentInfoAttribute::KIND_FOLDER;
2801 aSeq.getArray()[ 0 ].Properties = aProps;
2802
2803 // Stream.
2804 aSeq.getArray()[ 1 ].Type = TDOC_STREAM_CONTENT_TYPE;
2805 aSeq.getArray()[ 1 ].Attributes
2806 = ucb::ContentInfoAttribute::INSERT_WITH_INPUTSTREAM
2807 | ucb::ContentInfoAttribute::KIND_DOCUMENT;
2808 aSeq.getArray()[ 1 ].Properties = aProps;
2809
2810 return aSeq;
2811 }
2812 }
2813 else
2814 {
2815 OSL_FAIL( "getCreatableContentsInfo called on non-contentcreator "
2816 "object!" );
2817
2818 return uno::Sequence< ucb::ContentInfo >( 0 );
2819 }
2820}
2821
2822
2824{
2825 return ( getType() == FOLDER ) || ( getType() == DOCUMENT );
2826}
2827
2828/* 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
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
css::uno::Reference< css::ucb::XPersistentPropertySet > getAdditionalPropertySet(const OUString &rKey, bool bCreate)
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