1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/io/NotConnectedException.hpp>
21 #include <com/sun/star/io/BufferSizeExceededException.hpp>
22 #include <com/sun/star/uno/RuntimeException.hpp>
23 #include <ucbhelper/content.hxx>
24 #include <com/sun/star/uno/Reference.h>
25 #include <com/sun/star/ucb/NameClash.hpp>
26 #include <unotools/tempfile.hxx>
27 #include <unotools/ucbstreamhelper.hxx>
28 #include <com/sun/star/io/XInputStream.hpp>
29 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
30 #include <com/sun/star/ucb/ResultSetException.hpp>
31 #include <com/sun/star/uno/Sequence.h>
32 #include <com/sun/star/sdbc/XResultSet.hpp>
33 #include <com/sun/star/sdbc/XRow.hpp>
34 #include <com/sun/star/ucb/CommandAbortedException.hpp>
35 #include <com/sun/star/datatransfer/DataFlavor.hpp>
36 #include <com/sun/star/ucb/ContentInfo.hpp>
37 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
38 #include <com/sun/star/beans/Property.hpp>
39 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
40 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
41 #include <com/sun/star/ucb/InteractiveIOException.hpp>
42 #include <com/sun/star/ucb/ContentCreationException.hpp>
46 #include <o3tl/safeint.hxx>
47 #include <osl/diagnose.h>
48 #include <osl/file.hxx>
49 #include <sal/log.hxx>
50 #include <comphelper/diagnose_ex.hxx>
51 #include <tools/ref.hxx>
52 #include <tools/debug.hxx>
53 #include <unotools/streamwrap.hxx>
54 #include <unotools/ucbhelper.hxx>
55 #include <tools/urlobj.hxx>
56 #include <comphelper/processfactory.hxx>
57 #include <comphelper/propertyvalue.hxx>
58 #include <cppuhelper/implbase.hxx>
59 #include <ucbhelper/commandenvironment.hxx>
61 #include <sot/stg.hxx>
62 #include <sot/storinfo.hxx>
63 #include <sot/exchange.hxx>
64 #include <sot/formats.hxx>
65 #include <comphelper/classids.hxx>
71 namespace com::sun::star::ucb
{ class XCommandEnvironment
; }
73 using namespace ::com::sun::star::lang
;
74 using namespace ::com::sun::star::beans
;
75 using namespace ::com::sun::star::uno
;
76 using namespace ::com::sun::star::ucb
;
77 using namespace ::com::sun::star::io
;
78 using namespace ::com::sun::star::sdbc
;
79 using namespace ::ucbhelper
;
81 #if OSL_DEBUG_LEVEL > 0
82 static int nOpenFiles
=0;
83 static int nOpenStreams
=0;
86 typedef ::cppu::WeakImplHelper
< XInputStream
, XSeekable
> FileInputStreamWrapper_Base
;
90 class FileStreamWrapper_Impl
: public FileInputStreamWrapper_Base
95 std::unique_ptr
<SvStream
> m_pSvStream
;
98 explicit FileStreamWrapper_Impl(OUString aName
);
99 virtual ~FileStreamWrapper_Impl() override
;
101 virtual void SAL_CALL
seek( sal_Int64 _nLocation
) override
;
102 virtual sal_Int64 SAL_CALL
getPosition( ) override
;
103 virtual sal_Int64 SAL_CALL
getLength( ) override
;
104 virtual sal_Int32 SAL_CALL
readBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
) override
;
105 virtual sal_Int32 SAL_CALL
readSomeBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
) override
;
106 virtual void SAL_CALL
skipBytes(sal_Int32 nBytesToSkip
) override
;
107 virtual sal_Int32 SAL_CALL
available() override
;
108 virtual void SAL_CALL
closeInput() override
;
111 void checkConnected();
117 FileStreamWrapper_Impl::FileStreamWrapper_Impl( OUString aName
)
118 : m_aURL(std::move( aName
))
120 // if no URL is provided the stream is empty
124 FileStreamWrapper_Impl::~FileStreamWrapper_Impl()
129 #if OSL_DEBUG_LEVEL > 0
134 if (!m_aURL
.isEmpty())
135 osl::File::remove(m_aURL
);
139 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
141 if ( m_aURL
.isEmpty() )
149 if (nBytesToRead
< 0)
150 throw BufferSizeExceededException(OUString(), getXWeak());
152 std::scoped_lock
aGuard( m_aMutex
);
154 if (aData
.getLength() < nBytesToRead
)
155 aData
.realloc(nBytesToRead
);
157 sal_uInt32 nRead
= m_pSvStream
->ReadBytes(static_cast<void*>(aData
.getArray()), nBytesToRead
);
160 // if read characters < MaxLength, adjust sequence
161 if (nRead
< o3tl::make_unsigned(aData
.getLength()))
162 aData
.realloc( nRead
);
168 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
170 if ( m_aURL
.isEmpty() )
178 if (nMaxBytesToRead
< 0)
179 throw BufferSizeExceededException(OUString(), getXWeak());
181 if (m_pSvStream
->eof())
187 return readBytes(aData
, nMaxBytesToRead
);
191 void SAL_CALL
FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip
)
193 if ( m_aURL
.isEmpty() )
196 std::scoped_lock
aGuard( m_aMutex
);
199 m_pSvStream
->SeekRel(nBytesToSkip
);
204 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::available()
206 if ( m_aURL
.isEmpty() )
209 std::scoped_lock
aGuard( m_aMutex
);
212 sal_Int64 nAvailable
= m_pSvStream
->remainingSize();
215 return std::min
<sal_Int64
>(SAL_MAX_INT32
, nAvailable
);
219 void SAL_CALL
FileStreamWrapper_Impl::closeInput()
221 if ( m_aURL
.isEmpty() )
224 std::scoped_lock
aGuard( m_aMutex
);
227 #if OSL_DEBUG_LEVEL > 0
230 osl::File::remove(m_aURL
);
235 void SAL_CALL
FileStreamWrapper_Impl::seek( sal_Int64 _nLocation
)
237 if ( m_aURL
.isEmpty() )
240 std::scoped_lock
aGuard( m_aMutex
);
243 m_pSvStream
->Seek(static_cast<sal_uInt32
>(_nLocation
));
248 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getPosition( )
250 if ( m_aURL
.isEmpty() )
253 std::scoped_lock
aGuard( m_aMutex
);
256 sal_uInt32 nPos
= m_pSvStream
->Tell();
258 return static_cast<sal_Int64
>(nPos
);
262 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getLength( )
264 if ( m_aURL
.isEmpty() )
267 std::scoped_lock
aGuard( m_aMutex
);
272 sal_Int64 nEndPos
= m_pSvStream
->TellEnd();
278 void FileStreamWrapper_Impl::checkConnected()
280 if ( m_aURL
.isEmpty() )
281 throw NotConnectedException(OUString(), getXWeak());
284 m_pSvStream
= ::utl::UcbStreamHelper::CreateStream( m_aURL
, StreamMode::STD_READ
);
285 #if OSL_DEBUG_LEVEL > 0
292 void FileStreamWrapper_Impl::checkError()
296 if (m_pSvStream
->SvStream::GetError() != ERRCODE_NONE
)
297 // TODO: really evaluate the error
298 throw NotConnectedException(OUString(), getXWeak());
302 #define COMMIT_RESULT_FAILURE 0
303 #define COMMIT_RESULT_NOTHING_TO_DO 1
304 #define COMMIT_RESULT_SUCCESS 2
306 static SotClipboardFormatId
GetFormatId_Impl( const SvGlobalName
& aName
)
308 if ( aName
== SvGlobalName( SO3_SW_CLASSID_60
) )
309 return SotClipboardFormatId::STARWRITER_60
;
310 if ( aName
== SvGlobalName( SO3_SWWEB_CLASSID_60
) )
311 return SotClipboardFormatId::STARWRITERWEB_60
;
312 if ( aName
== SvGlobalName( SO3_SWGLOB_CLASSID_60
) )
313 return SotClipboardFormatId::STARWRITERGLOB_60
;
314 if ( aName
== SvGlobalName( SO3_SDRAW_CLASSID_60
) )
315 return SotClipboardFormatId::STARDRAW_60
;
316 if ( aName
== SvGlobalName( SO3_SIMPRESS_CLASSID_60
) )
317 return SotClipboardFormatId::STARIMPRESS_60
;
318 if ( aName
== SvGlobalName( SO3_SC_CLASSID_60
) )
319 return SotClipboardFormatId::STARCALC_60
;
320 if ( aName
== SvGlobalName( SO3_SCH_CLASSID_60
) )
321 return SotClipboardFormatId::STARCHART_60
;
322 if ( aName
== SvGlobalName( SO3_SM_CLASSID_60
) )
323 return SotClipboardFormatId::STARMATH_60
;
324 if ( aName
== SvGlobalName( SO3_OUT_CLASSID
) ||
325 aName
== SvGlobalName( SO3_APPLET_CLASSID
) ||
326 aName
== SvGlobalName( SO3_PLUGIN_CLASSID
) ||
327 aName
== SvGlobalName( SO3_IFRAME_CLASSID
) )
328 // allowed, but not supported
329 return SotClipboardFormatId::NONE
;
332 OSL_FAIL( "Unknown UCB storage format!" );
333 return SotClipboardFormatId::NONE
;
338 static SvGlobalName
GetClassId_Impl( SotClipboardFormatId nFormat
)
342 case SotClipboardFormatId::STARWRITER_8
:
343 case SotClipboardFormatId::STARWRITER_8_TEMPLATE
:
344 return SvGlobalName( SO3_SW_CLASSID_60
);
345 case SotClipboardFormatId::STARWRITERWEB_8
:
346 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
347 case SotClipboardFormatId::STARWRITERGLOB_8
:
348 case SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE
:
349 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
350 case SotClipboardFormatId::STARDRAW_8
:
351 case SotClipboardFormatId::STARDRAW_8_TEMPLATE
:
352 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
353 case SotClipboardFormatId::STARIMPRESS_8
:
354 case SotClipboardFormatId::STARIMPRESS_8_TEMPLATE
:
355 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
356 case SotClipboardFormatId::STARCALC_8
:
357 case SotClipboardFormatId::STARCALC_8_TEMPLATE
:
358 return SvGlobalName( SO3_SC_CLASSID_60
);
359 case SotClipboardFormatId::STARCHART_8
:
360 case SotClipboardFormatId::STARCHART_8_TEMPLATE
:
361 return SvGlobalName( SO3_SCH_CLASSID_60
);
362 case SotClipboardFormatId::STARMATH_8
:
363 case SotClipboardFormatId::STARMATH_8_TEMPLATE
:
364 return SvGlobalName( SO3_SM_CLASSID_60
);
365 case SotClipboardFormatId::STARWRITER_60
:
366 return SvGlobalName( SO3_SW_CLASSID_60
);
367 case SotClipboardFormatId::STARWRITERWEB_60
:
368 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
369 case SotClipboardFormatId::STARWRITERGLOB_60
:
370 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
371 case SotClipboardFormatId::STARDRAW_60
:
372 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
373 case SotClipboardFormatId::STARIMPRESS_60
:
374 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
375 case SotClipboardFormatId::STARCALC_60
:
376 return SvGlobalName( SO3_SC_CLASSID_60
);
377 case SotClipboardFormatId::STARCHART_60
:
378 return SvGlobalName( SO3_SCH_CLASSID_60
);
379 case SotClipboardFormatId::STARMATH_60
:
380 return SvGlobalName( SO3_SM_CLASSID_60
);
382 return SvGlobalName();
386 // All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle
387 // class, that uses the refcounted object as impl-class.
389 class UCBStorageStream_Impl
: public SvRefBase
, public SvStream
391 virtual ~UCBStorageStream_Impl() override
;
394 virtual std::size_t GetData(void* pData
, std::size_t nSize
) override
;
395 virtual std::size_t PutData(const void* pData
, std::size_t nSize
) override
;
396 virtual sal_uInt64
SeekPos( sal_uInt64 nPos
) override
;
397 virtual void SetSize( sal_uInt64 nSize
) override
;
398 virtual void FlushData() override
;
399 virtual void ResetError() override
;
401 UCBStorageStream
* m_pAntiImpl
; // only valid if an external reference exists
403 OUString m_aOriginalName
;// the original name before accessing the stream
404 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
405 OUString m_aURL
; // the full path name to create the content
406 OUString m_aContentType
;
407 OUString m_aOriginalContentType
;
409 ::ucbhelper::Content
* m_pContent
; // the content that provides the data
410 Reference
<XInputStream
> m_rSource
; // the stream covering the original data of the content
411 std::unique_ptr
<SvStream
> m_pStream
; // the stream worked on; for readonly streams it is the original stream of the content
412 // for read/write streams it's a copy into a temporary file
413 OUString m_aTempURL
; // URL of this temporary stream
415 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
416 bool m_bSourceRead
; // Source still contains useful information
417 bool m_bModified
; // only modified streams will be sent to the original content
418 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
419 bool m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
420 // this means that the root storage does an autocommit when its external
421 // reference is destroyed
422 bool m_bIsOLEStorage
;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream
424 UCBStorageStream_Impl( const OUString
&, StreamMode
, UCBStorageStream
*, bool,
425 bool bRepair
, Reference
< XProgressHandler
> const & xProgress
);
430 sal_Int16
Commit(); // if modified and committed: transfer an XInputStream to the content
431 void Revert(); // discard all changes
432 BaseStorage
* CreateStorage();// create an OLE Storage on the UCBStorageStream
433 sal_uInt64
GetSize();
435 sal_uInt64
ReadSourceWriteTemporary( sal_uInt64 aLength
); // read aLength from source and copy to temporary,
436 // no seeking is produced
437 void ReadSourceWriteTemporary(); // read source till the end and copy to temporary,
439 void CopySourceToTemporary(); // same as ReadSourceWriteToTemporary()
440 // but the writing is done at the end of temporary
441 // pointer position is not changed
442 using SvStream::SetError
;
443 void SetError( ErrCode nError
);
444 void PrepareCachedForReopen( StreamMode nMode
);
447 typedef tools::SvRef
<UCBStorageStream_Impl
> UCBStorageStream_ImplRef
;
449 struct UCBStorageElement_Impl
;
450 typedef std::vector
<std::unique_ptr
<UCBStorageElement_Impl
>> UCBStorageElementList_Impl
;
452 class UCBStorage_Impl
: public SvRefBase
454 virtual ~UCBStorage_Impl() override
;
456 UCBStorage
* m_pAntiImpl
; // only valid if external references exists
458 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
459 OUString m_aURL
; // the full path name to create the content
460 OUString m_aContentType
;
461 OUString m_aOriginalContentType
;
462 std::optional
<::ucbhelper::Content
> m_oContent
; // the content that provides the storage elements
463 std::unique_ptr
<::utl::TempFileNamed
> m_pTempFile
; // temporary file, only for storages on stream
464 SvStream
* m_pSource
; // original stream, only for storages on a stream
466 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
467 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
468 bool m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
469 // this means that the root storage does an autocommit when its external
470 // reference is destroyed
471 bool m_bIsRoot
; // marks this storage as root storages that manages all commits and reverts
474 SotClipboardFormatId m_nFormat
;
475 OUString m_aUserTypeName
;
476 SvGlobalName m_aClassId
;
478 UCBStorageElementList_Impl m_aChildrenList
;
480 bool m_bRepairPackage
;
481 Reference
< XProgressHandler
> m_xProgressHandler
;
483 UCBStorage_Impl( const ::ucbhelper::Content
&, const OUString
&, StreamMode
, UCBStorage
*, bool,
484 bool, bool = false, Reference
< XProgressHandler
> const & = Reference
< XProgressHandler
>() );
485 UCBStorage_Impl( const OUString
&, StreamMode
, UCBStorage
*, bool, bool,
486 bool, Reference
< XProgressHandler
> const & );
487 UCBStorage_Impl( SvStream
&, UCBStorage
*, bool );
491 bool Insert( ::ucbhelper::Content
*pContent
);
492 UCBStorage_Impl
* OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
);
493 void OpenStream( UCBStorageElement_Impl
*, StreamMode
, bool );
494 void SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
495 void GetProps( sal_Int32
&, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
496 sal_Int32
GetObjectCount();
498 void CreateContent();
499 ::ucbhelper::Content
* GetContent()
503 return m_oContent
? &*m_oContent
: nullptr;
505 UCBStorageElementList_Impl
& GetChildrenList()
507 const ErrCode nError
= m_nError
;
509 if ( m_nMode
& StreamMode::WRITE
)
514 m_pAntiImpl
->ResetError();
515 m_pAntiImpl
->SetError( nError
);
518 return m_aChildrenList
;
521 void SetError( ErrCode nError
);
524 typedef tools::SvRef
<UCBStorage_Impl
> UCBStorage_ImplRef
;
526 // this struct contains all necessary information on an element inside a UCBStorage
527 struct UCBStorageElement_Impl
529 OUString m_aName
; // the actual URL relative to the root "folder"
530 OUString m_aOriginalName
;// the original name in the content
532 bool m_bIsFolder
; // Only true when it is a UCBStorage !
533 bool m_bIsStorage
; // Also true when it is an OLEStorage !
534 bool m_bIsRemoved
; // element will be removed on commit
535 bool m_bIsInserted
; // element will be removed on revert
536 UCBStorage_ImplRef m_xStorage
; // reference to the "real" storage
537 UCBStorageStream_ImplRef m_xStream
; // reference to the "real" stream
539 UCBStorageElement_Impl( const OUString
& rName
,
540 bool bIsFolder
= false, sal_uInt64 nSize
= 0 )
542 , m_aOriginalName( rName
)
544 , m_bIsFolder( bIsFolder
)
545 , m_bIsStorage( bIsFolder
)
546 , m_bIsRemoved( false )
547 , m_bIsInserted( false )
551 ::ucbhelper::Content
* GetContent();
552 bool IsModified() const;
553 OUString
GetContentType() const;
554 void SetContentType( const OUString
& );
555 OUString
GetOriginalContentType() const;
556 bool IsLoaded() const
557 { return m_xStream
.is() || m_xStorage
.is(); }
560 ::ucbhelper::Content
* UCBStorageElement_Impl::GetContent()
562 if ( m_xStream
.is() )
563 return m_xStream
->m_pContent
;
564 else if ( m_xStorage
.is() )
565 return m_xStorage
->GetContent();
570 OUString
UCBStorageElement_Impl::GetContentType() const
572 if ( m_xStream
.is() )
573 return m_xStream
->m_aContentType
;
574 else if ( m_xStorage
.is() )
575 return m_xStorage
->m_aContentType
;
578 OSL_FAIL("Element not loaded!");
583 void UCBStorageElement_Impl::SetContentType( const OUString
& rType
)
585 if ( m_xStream
.is() ) {
586 m_xStream
->m_aContentType
= m_xStream
->m_aOriginalContentType
= rType
;
588 else if ( m_xStorage
.is() ) {
589 m_xStorage
->m_aContentType
= m_xStorage
->m_aOriginalContentType
= rType
;
592 OSL_FAIL("Element not loaded!");
596 OUString
UCBStorageElement_Impl::GetOriginalContentType() const
598 if ( m_xStream
.is() )
599 return m_xStream
->m_aOriginalContentType
;
600 else if ( m_xStorage
.is() )
601 return m_xStorage
->m_aOriginalContentType
;
606 bool UCBStorageElement_Impl::IsModified() const
608 bool bModified
= m_bIsRemoved
|| m_bIsInserted
|| m_aName
!= m_aOriginalName
;
611 if ( m_xStream
.is() )
612 bModified
= m_xStream
->m_aContentType
!= m_xStream
->m_aOriginalContentType
;
613 else if ( m_xStorage
.is() )
614 bModified
= m_xStorage
->m_aContentType
!= m_xStorage
->m_aOriginalContentType
;
620 UCBStorageStream_Impl::UCBStorageStream_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorageStream
* pStream
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
621 : m_pAntiImpl( pStream
)
623 , m_pContent( nullptr )
624 , m_nError( ERRCODE_NONE
)
626 , m_bSourceRead( !( nMode
& StreamMode::TRUNC
) )
627 , m_bModified( false )
628 , m_bCommited( false )
629 , m_bDirect( bDirect
)
630 , m_bIsOLEStorage( false )
632 // name is last segment in URL
633 INetURLObject
aObj( rName
);
634 m_aName
= m_aOriginalName
= aObj
.GetLastName();
637 // create the content
638 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
640 OUString
aTemp( rName
);
644 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(), xProgress
);
645 aTemp
+= "?repairpackage";
648 m_pContent
= new ::ucbhelper::Content( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
650 catch (const ContentCreationException
&)
652 // content could not be created
653 SetError( SVSTREAM_CANNOT_MAKE
);
655 catch (const RuntimeException
&)
657 // any other error - not specified
658 SetError( ERRCODE_IO_GENERAL
);
662 UCBStorageStream_Impl::~UCBStorageStream_Impl()
669 if (!m_aTempURL
.isEmpty())
670 osl::File::remove(m_aTempURL
);
676 bool UCBStorageStream_Impl::Init()
680 // no temporary stream was created
683 if ( m_aTempURL
.isEmpty() )
684 m_aTempURL
= ::utl::CreateTempURL();
686 m_pStream
= ::utl::UcbStreamHelper::CreateStream( m_aTempURL
, StreamMode::STD_READWRITE
, true /* bFileExists */ );
687 #if OSL_DEBUG_LEVEL > 0
693 OSL_FAIL( "Suspicious temporary stream creation!" );
694 SetError( SVSTREAM_CANNOT_MAKE
);
698 SetError( m_pStream
->GetError() );
701 if( m_bSourceRead
&& !m_rSource
.is() )
703 // source file contain useful information and is not opened
704 // open it from the point of noncopied data
708 m_rSource
= m_pContent
->openStream();
710 catch (const Exception
&)
712 // usually means that stream could not be opened
717 m_pStream
->Seek( STREAM_SEEK_TO_END
);
721 m_rSource
->skipBytes( m_pStream
->Tell() );
723 catch (const BufferSizeExceededException
&)
725 // the temporary stream already contain all the data
726 m_bSourceRead
= false;
728 catch (const Exception
&)
730 // something is really wrong
731 m_bSourceRead
= false;
732 OSL_FAIL( "Can not operate original stream!" );
733 SetError( SVSTREAM_CANNOT_MAKE
);
736 m_pStream
->Seek( 0 );
740 // if the new file is edited then no source exist
741 m_bSourceRead
= false;
742 //SetError( SVSTREAM_CANNOT_MAKE );
746 DBG_ASSERT( m_rSource
.is() || !m_bSourceRead
, "Unreadable source stream!" );
751 void UCBStorageStream_Impl::ReadSourceWriteTemporary()
753 // read source stream till the end and copy all the data to
754 // the current position of the temporary stream
758 Sequence
<sal_Int8
> aData(32000);
765 aReaded
= m_rSource
->readBytes( aData
, 32000 );
766 m_pStream
->WriteBytes(aData
.getConstArray(), aReaded
);
767 } while( aReaded
== 32000 );
769 catch (const Exception
&)
771 TOOLS_WARN_EXCEPTION("sot", "");
775 m_bSourceRead
= false;
778 sal_uInt64
UCBStorageStream_Impl::ReadSourceWriteTemporary(sal_uInt64 aLength
)
780 // read aLength bite from the source stream and copy them to the current
781 // position of the temporary stream
783 sal_uInt64 aResult
= 0;
787 Sequence
<sal_Int8
> aData(32000);
792 sal_Int32 aReaded
= 32000;
794 for (sal_uInt64 nInd
= 0; nInd
< aLength
&& aReaded
== 32000 ; nInd
+= 32000)
796 sal_Int32 aToCopy
= std::min
<sal_Int32
>( aLength
- nInd
, 32000 );
797 aReaded
= m_rSource
->readBytes( aData
, aToCopy
);
798 aResult
+= m_pStream
->WriteBytes(aData
.getConstArray(), aReaded
);
801 if( aResult
< aLength
)
802 m_bSourceRead
= false;
804 catch( const Exception
& )
806 TOOLS_WARN_EXCEPTION("sot", "");
813 void UCBStorageStream_Impl::CopySourceToTemporary()
815 // current position of the temporary stream is not changed
818 sal_uInt64 aPos
= m_pStream
->Tell();
819 m_pStream
->Seek( STREAM_SEEK_TO_END
);
820 ReadSourceWriteTemporary();
821 m_pStream
->Seek( aPos
);
825 // UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream
826 // of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified
827 std::size_t UCBStorageStream_Impl::GetData(void* pData
, std::size_t const nSize
)
829 std::size_t aResult
= 0;
835 // read data that is in temporary stream
836 aResult
= m_pStream
->ReadBytes( pData
, nSize
);
837 if( m_bSourceRead
&& aResult
< nSize
)
839 // read the tail of the data from original stream
840 // copy this tail to the temporary stream
842 std::size_t aToRead
= nSize
- aResult
;
843 pData
= static_cast<void*>( static_cast<char*>(pData
) + aResult
);
847 Sequence
<sal_Int8
> aData( aToRead
);
848 std::size_t aReaded
= m_rSource
->readBytes( aData
, aToRead
);
849 aResult
+= m_pStream
->WriteBytes(static_cast<const void*>(aData
.getConstArray()), aReaded
);
850 memcpy( pData
, aData
.getArray(), aReaded
);
852 catch (const Exception
&)
854 TOOLS_WARN_EXCEPTION("sot", "");
857 if( aResult
< nSize
)
858 m_bSourceRead
= false;
864 std::size_t UCBStorageStream_Impl::PutData(const void* pData
, std::size_t const nSize
)
866 if ( !(m_nMode
& StreamMode::WRITE
) )
868 SetError( ERRCODE_IO_ACCESSDENIED
);
872 if( !nSize
|| !Init() )
875 std::size_t aResult
= m_pStream
->WriteBytes( pData
, nSize
);
877 m_bModified
= aResult
> 0;
883 sal_uInt64
UCBStorageStream_Impl::SeekPos(sal_uInt64
const nPos
)
885 // check if a truncated STREAM_SEEK_TO_END was passed
886 assert(nPos
!= SAL_MAX_UINT32
);
893 if( nPos
== STREAM_SEEK_TO_END
)
895 m_pStream
->Seek( STREAM_SEEK_TO_END
);
896 ReadSourceWriteTemporary();
897 aResult
= m_pStream
->Tell();
901 // the problem is that even if nPos is larger the length
902 // of the stream, the stream pointer will be moved to this position
903 // so we have to check if temporary stream does not contain required position
905 if( m_pStream
->Tell() > nPos
906 || m_pStream
->Seek( STREAM_SEEK_TO_END
) > nPos
)
908 // no copying is required
909 aResult
= m_pStream
->Seek( nPos
);
913 // the temp stream pointer points to the end now
914 aResult
= m_pStream
->Tell();
920 aResult
+= ReadSourceWriteTemporary( nPos
- aResult
);
922 m_bSourceRead
= false;
924 DBG_ASSERT( aResult
== m_pStream
->Tell(), "Error in stream arithmetic!\n" );
927 if( (m_nMode
& StreamMode::WRITE
) && !m_bSourceRead
&& aResult
< nPos
)
929 // it means that all the Source stream was copied already
930 // but the required position still was not reached
931 // for writable streams it should be done
932 m_pStream
->SetStreamSize( nPos
);
933 aResult
= m_pStream
->Seek( STREAM_SEEK_TO_END
);
934 DBG_ASSERT( aResult
== nPos
, "Error in stream arithmetic!\n" );
943 void UCBStorageStream_Impl::SetSize(sal_uInt64
const nSize
)
945 if ( !(m_nMode
& StreamMode::WRITE
) )
947 SetError( ERRCODE_IO_ACCESSDENIED
);
958 sal_uInt64
const aPos
= m_pStream
->Tell();
959 m_pStream
->Seek( STREAM_SEEK_TO_END
);
960 if( m_pStream
->Tell() < nSize
)
961 ReadSourceWriteTemporary( nSize
- m_pStream
->Tell() );
962 m_pStream
->Seek( aPos
);
965 m_pStream
->SetStreamSize( nSize
);
966 m_bSourceRead
= false;
969 void UCBStorageStream_Impl::FlushData()
973 CopySourceToTemporary();
980 void UCBStorageStream_Impl::SetError( ErrCode nErr
)
985 SvStream::SetError( nErr
);
986 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nErr
);
990 void UCBStorageStream_Impl::ResetError()
992 m_nError
= ERRCODE_NONE
;
993 SvStream::ResetError();
995 m_pAntiImpl
->ResetError();
998 sal_uInt64
UCBStorageStream_Impl::GetSize()
1003 sal_uInt64 nPos
= m_pStream
->Tell();
1004 m_pStream
->Seek( STREAM_SEEK_TO_END
);
1005 ReadSourceWriteTemporary();
1006 sal_uInt64 nRet
= m_pStream
->Tell();
1007 m_pStream
->Seek( nPos
);
1012 BaseStorage
* UCBStorageStream_Impl::CreateStorage()
1014 // create an OLEStorage on a SvStream ( = this )
1015 // it gets the root attribute because otherwise it would probably not write before my root is committed
1016 UCBStorageStream
* pNewStorageStream
= new UCBStorageStream( this );
1017 Storage
*pStorage
= new Storage( *pNewStorageStream
, m_bDirect
);
1019 // GetError() call clears error code for OLE storages, must be changed in future
1020 const ErrCode nTmpErr
= pStorage
->GetError();
1021 pStorage
->SetError( nTmpErr
);
1023 m_bIsOLEStorage
= !nTmpErr
;
1024 return static_cast< BaseStorage
* > ( pStorage
);
1027 sal_Int16
UCBStorageStream_Impl::Commit()
1029 // send stream to the original content
1030 // the parent storage is responsible for the correct handling of deleted contents
1031 if ( m_bCommited
|| m_bIsOLEStorage
|| m_bDirect
)
1033 // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage
1034 // was committed as well ( if not opened in direct mode )
1040 CopySourceToTemporary();
1042 // release all stream handles
1045 // the temporary file does not exist only for truncated streams
1046 DBG_ASSERT( !m_aTempURL
.isEmpty() || ( m_nMode
& StreamMode::TRUNC
), "No temporary file to read from!");
1047 if ( m_aTempURL
.isEmpty() && !( m_nMode
& StreamMode::TRUNC
) )
1048 throw RuntimeException();
1050 // create wrapper to stream that is only used while reading inside package component
1051 Reference
< XInputStream
> xStream
= new FileStreamWrapper_Impl( m_aTempURL
);
1053 InsertCommandArgument aArg
;
1054 aArg
.Data
= xStream
;
1055 aArg
.ReplaceExisting
= true;
1056 m_pContent
->executeCommand( "insert", Any(aArg
) );
1058 // wrapper now controls lifetime of temporary file
1061 INetURLObject
aObj( m_aURL
);
1062 aObj
.setName( m_aName
);
1063 m_aURL
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1064 m_bModified
= false;
1065 m_bSourceRead
= true;
1067 catch (const CommandAbortedException
&)
1069 // any command wasn't executed successfully - not specified
1070 SetError( ERRCODE_IO_GENERAL
);
1071 return COMMIT_RESULT_FAILURE
;
1073 catch (const RuntimeException
&)
1075 // any other error - not specified
1076 SetError( ERRCODE_IO_GENERAL
);
1077 return COMMIT_RESULT_FAILURE
;
1079 catch (const Exception
&)
1081 // any other error - not specified
1082 SetError( ERRCODE_IO_GENERAL
);
1083 return COMMIT_RESULT_FAILURE
;
1086 m_bCommited
= false;
1087 return COMMIT_RESULT_SUCCESS
;
1091 return COMMIT_RESULT_NOTHING_TO_DO
;
1094 void UCBStorageStream_Impl::Revert()
1096 // if an OLEStorage is created on this stream, no "revert" is necessary because OLEStorages do nothing on "Revert" !
1099 OSL_FAIL("Revert while commit is in progress!" );
1104 if ( !m_aTempURL
.isEmpty() )
1106 osl::File::remove(m_aTempURL
);
1110 m_bSourceRead
= false;
1113 m_rSource
= m_pContent
->openStream();
1114 if( m_rSource
.is() )
1116 if ( m_pAntiImpl
&& ( m_nMode
& StreamMode::TRUNC
) )
1117 // stream is in use and should be truncated
1118 m_bSourceRead
= false;
1121 m_nMode
&= ~StreamMode::TRUNC
;
1122 m_bSourceRead
= true;
1126 SetError( SVSTREAM_CANNOT_MAKE
);
1128 catch (const ContentCreationException
&)
1130 SetError( ERRCODE_IO_GENERAL
);
1132 catch (const RuntimeException
&)
1134 SetError( ERRCODE_IO_GENERAL
);
1136 catch (const Exception
&)
1140 m_bModified
= false;
1141 m_aName
= m_aOriginalName
;
1142 m_aContentType
= m_aOriginalContentType
;
1145 bool UCBStorageStream_Impl::Clear()
1147 bool bRet
= ( m_pAntiImpl
== nullptr );
1148 DBG_ASSERT( bRet
, "Removing used stream!" );
1157 void UCBStorageStream_Impl::Free()
1159 #if OSL_DEBUG_LEVEL > 0
1162 if ( !m_aTempURL
.isEmpty() )
1173 void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode
)
1175 bool isWritable
= bool( m_nMode
& StreamMode::WRITE
);
1178 // once stream was writable, never reset to readonly
1179 nMode
|= StreamMode::WRITE
;
1185 if ( nMode
& StreamMode::TRUNC
)
1187 m_bSourceRead
= false; // usually it should be 0 already but just in case...
1189 if ( !m_aTempURL
.isEmpty() )
1191 osl::File::remove(m_aTempURL
);
1197 UCBStorageStream::UCBStorageStream( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
1199 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1200 // to class UCBStorageStream !
1201 pImp
= new UCBStorageStream_Impl( rName
, nMode
, this, bDirect
, bRepair
, xProgress
);
1202 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1203 StorageBase::m_nMode
= pImp
->m_nMode
;
1206 UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl
*pImpl
)
1209 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1210 pImp
->m_pAntiImpl
= this;
1211 SetError( pImp
->m_nError
);
1212 StorageBase::m_nMode
= pImp
->m_nMode
;
1215 UCBStorageStream::~UCBStorageStream()
1217 if ( pImp
->m_nMode
& StreamMode::WRITE
)
1219 pImp
->m_pAntiImpl
= nullptr;
1224 sal_Int32
UCBStorageStream::Read( void * pData
, sal_Int32 nSize
)
1226 //return pImp->m_pStream->Read( pData, nSize );
1227 return pImp
->GetData( pData
, nSize
);
1230 sal_Int32
UCBStorageStream::Write( const void* pData
, sal_Int32 nSize
)
1232 return pImp
->PutData( pData
, nSize
);
1235 sal_uInt64
UCBStorageStream::Seek( sal_uInt64 nPos
)
1237 //return pImp->m_pStream->Seek( nPos );
1238 return pImp
->Seek( nPos
);
1241 sal_uInt64
UCBStorageStream::Tell()
1245 return pImp
->m_pStream
->Tell();
1248 void UCBStorageStream::Flush()
1250 // streams are never really transacted, so flush also means commit !
1254 bool UCBStorageStream::SetSize( sal_uInt64 nNewSize
)
1256 pImp
->SetSize( nNewSize
);
1257 return !pImp
->GetError();
1260 bool UCBStorageStream::Validate( bool bWrite
) const
1262 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
1265 bool UCBStorageStream::ValidateMode( StreamMode m
) const
1268 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
1270 if( ( m
& StreamMode::READWRITE
) == StreamMode::READ
)
1272 // only SHARE_DENYWRITE or SHARE_DENYALL allowed
1273 if( ( m
& StreamMode::SHARE_DENYWRITE
)
1274 || ( m
& StreamMode::SHARE_DENYALL
) )
1279 // only SHARE_DENYALL allowed
1280 // storages open in r/o mode are OK, since only
1281 // the commit may fail
1282 if( m
& StreamMode::SHARE_DENYALL
)
1289 SvStream
* UCBStorageStream::GetModifySvStream()
1291 return static_cast<SvStream
*>(pImp
);
1294 bool UCBStorageStream::Equals( const BaseStorageStream
& rStream
) const
1297 return static_cast<BaseStorageStream
const *>(this) == &rStream
;
1300 bool UCBStorageStream::Commit()
1302 // mark this stream for sending it on root commit
1307 void UCBStorageStream::CopyTo( BaseStorageStream
* pDestStm
)
1312 UCBStorageStream
* pStg
= dynamic_cast<UCBStorageStream
*>( pDestStm
);
1314 pStg
->pImp
->m_aContentType
= pImp
->m_aContentType
;
1316 pDestStm
->SetSize( 0 );
1317 Seek( STREAM_SEEK_TO_END
);
1318 sal_Int32 n
= Tell();
1322 if( !pDestStm
->SetSize( n
) || !n
)
1325 std::unique_ptr
<sal_uInt8
[]> p(new sal_uInt8
[ 4096 ]);
1327 pDestStm
->Seek( 0 );
1333 if( Read( p
.get(), nn
) != nn
)
1335 if( pDestStm
->Write( p
.get(), nn
) != nn
)
1341 bool UCBStorageStream::SetProperty( const OUString
& rName
, const css::uno::Any
& rValue
)
1343 if ( rName
== "Title")
1346 if ( rName
== "MediaType")
1350 pImp
->m_aContentType
= aTmp
;
1355 if ( pImp
->m_pContent
)
1357 pImp
->m_pContent
->setPropertyValue( rName
, rValue
);
1361 catch (const Exception
&)
1368 sal_uInt64
UCBStorageStream::GetSize() const
1370 return pImp
->GetSize();
1373 UCBStorage::UCBStorage( SvStream
& rStrm
, bool bDirect
)
1375 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1376 // to class UCBStorage !
1377 pImp
= new UCBStorage_Impl( rStrm
, this, bDirect
);
1379 pImp
->AddFirstRef();
1381 StorageBase::m_nMode
= pImp
->m_nMode
;
1384 UCBStorage::UCBStorage( const ::ucbhelper::Content
& rContent
, const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1386 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1387 // to class UCBStorage !
1388 pImp
= new UCBStorage_Impl( rContent
, rName
, nMode
, this, bDirect
, bIsRoot
);
1389 pImp
->AddFirstRef();
1391 StorageBase::m_nMode
= pImp
->m_nMode
;
1394 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1396 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1397 // to class UCBStorage !
1398 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, bIsRepair
, xProgressHandler
);
1399 pImp
->AddFirstRef();
1401 StorageBase::m_nMode
= pImp
->m_nMode
;
1404 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1406 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1407 // to class UCBStorage !
1408 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, false, Reference
< XProgressHandler
>() );
1409 pImp
->AddFirstRef();
1411 StorageBase::m_nMode
= pImp
->m_nMode
;
1414 UCBStorage::UCBStorage( UCBStorage_Impl
*pImpl
)
1417 pImp
->m_pAntiImpl
= this;
1418 SetError( pImp
->m_nError
);
1419 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1420 StorageBase::m_nMode
= pImp
->m_nMode
;
1423 UCBStorage::~UCBStorage()
1425 if ( pImp
->m_bIsRoot
&& pImp
->m_bDirect
&& ( !pImp
->m_pTempFile
|| pImp
->m_pSource
) )
1426 // DirectMode is simulated with an AutoCommit
1429 pImp
->m_pAntiImpl
= nullptr;
1433 UCBStorage_Impl::UCBStorage_Impl( const ::ucbhelper::Content
& rContent
, const OUString
& rName
, StreamMode nMode
, UCBStorage
* pStorage
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1434 : m_pAntiImpl( pStorage
)
1435 , m_oContent( rContent
)
1436 , m_pSource( nullptr )
1437 //, m_pStream( NULL )
1438 , m_nError( ERRCODE_NONE
)
1440 , m_bCommited( false )
1441 , m_bDirect( bDirect
)
1442 , m_bIsRoot( bIsRoot
)
1443 , m_bIsLinked( true )
1444 , m_bListCreated( false )
1445 , m_nFormat( SotClipboardFormatId::NONE
)
1446 , m_bRepairPackage( bIsRepair
)
1447 , m_xProgressHandler( xProgressHandler
)
1449 OUString
aName( rName
);
1450 if( aName
.isEmpty() )
1452 // no name given = use temporary name!
1453 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1454 m_pTempFile
.reset(new ::utl::TempFileNamed
);
1455 m_pTempFile
->EnableKillingFile();
1456 m_aName
= aName
= m_pTempFile
->GetURL();
1462 UCBStorage_Impl::UCBStorage_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorage
* pStorage
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1463 : m_pAntiImpl( pStorage
)
1464 , m_pSource( nullptr )
1465 //, m_pStream( NULL )
1466 , m_nError( ERRCODE_NONE
)
1468 , m_bCommited( false )
1469 , m_bDirect( bDirect
)
1470 , m_bIsRoot( bIsRoot
)
1471 , m_bIsLinked( false )
1472 , m_bListCreated( false )
1473 , m_nFormat( SotClipboardFormatId::NONE
)
1474 , m_bRepairPackage( bIsRepair
)
1475 , m_xProgressHandler( xProgressHandler
)
1477 OUString
aName( rName
);
1478 if( aName
.isEmpty() )
1480 // no name given = use temporary name!
1481 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1482 m_pTempFile
.reset(new ::utl::TempFileNamed
);
1483 m_pTempFile
->EnableKillingFile();
1484 m_aName
= aName
= m_pTempFile
->GetURL();
1489 // create the special package URL for the package content
1490 m_aURL
= "vnd.sun.star.pkg://" +
1491 INetURLObject::encode( aName
, INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1493 if ( m_nMode
& StreamMode::WRITE
)
1495 // the root storage opens the package, so make sure that there is any
1496 ::utl::UcbStreamHelper::CreateStream( aName
, StreamMode::STD_READWRITE
, m_pTempFile
!= nullptr /* bFileExists */ );
1501 // substorages are opened like streams: the URL is a "child URL" of the root package URL
1503 if ( !m_aURL
.startsWith( "vnd.sun.star.pkg://") )
1508 UCBStorage_Impl::UCBStorage_Impl( SvStream
& rStream
, UCBStorage
* pStorage
, bool bDirect
)
1509 : m_pAntiImpl( pStorage
)
1510 , m_pTempFile( new ::utl::TempFileNamed
)
1511 , m_pSource( &rStream
)
1512 , m_nError( ERRCODE_NONE
)
1513 , m_bCommited( false )
1514 , m_bDirect( bDirect
)
1516 , m_bIsLinked( false )
1517 , m_bListCreated( false )
1518 , m_nFormat( SotClipboardFormatId::NONE
)
1519 , m_bRepairPackage( false )
1521 // opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call,
1522 // which will be called in the storages' dtor
1523 m_pTempFile
->EnableKillingFile();
1524 DBG_ASSERT( !bDirect
, "Storage on a stream must not be opened in direct mode!" );
1526 // UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only
1527 // accessed readonly
1528 // the root storage opens the package; create the special package URL for the package content
1529 m_aURL
= "vnd.sun.star.pkg://" +
1530 INetURLObject::encode( m_pTempFile
->GetURL(), INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1532 // copy data into the temporary file
1533 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READWRITE
, true /* bFileExists */ ));
1537 rStream
.ReadStream( *pStream
);
1542 // close stream and let content access the file
1545 // check opening mode
1546 m_nMode
= StreamMode::READ
;
1547 if( rStream
.IsWritable() )
1548 m_nMode
= StreamMode::READ
| StreamMode::WRITE
;
1551 void UCBStorage_Impl::Init()
1553 // name is last segment in URL
1554 INetURLObject
aObj( m_aURL
);
1555 if ( m_aName
.isEmpty() )
1556 // if the name was not already set to a temp name
1557 m_aName
= aObj
.GetLastName();
1569 if ( m_nError
== ERRCODE_NONE
)
1571 // read the manifest.xml file
1572 aObj
.Append( u
"META-INF" );
1573 aObj
.Append( u
"manifest.xml" );
1575 // create input stream
1576 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::STD_READ
));
1577 // no stream means no manifest.xml
1580 if ( !pStream
->GetError() )
1582 rtl::Reference
<::utl::OInputStreamWrapper
> pHelper
= new ::utl::OInputStreamWrapper( *pStream
);
1584 // create a manifest reader object that will read in the manifest from the stream
1585 Reference
< css::packages::manifest::XManifestReader
> xReader
=
1586 css::packages::manifest::ManifestReader::create(
1587 ::comphelper::getProcessComponentContext() ) ;
1588 Sequence
< Sequence
< PropertyValue
> > aProps
= xReader
->readManifestSequence( pHelper
);
1593 SetProps( aProps
, OUString() );
1603 // get the manifest information from the package
1605 Any aAny
= m_oContent
->getPropertyValue("MediaType");
1607 if ( ( aAny
>>= aTmp
) && !aTmp
.isEmpty() )
1608 m_aContentType
= m_aOriginalContentType
= aTmp
;
1610 catch (const Exception
&)
1613 "getPropertyValue has thrown an exception! Please let developers know the scenario!" );
1618 if ( m_aContentType
.isEmpty() )
1621 // get the clipboard format using the content type
1622 css::datatransfer::DataFlavor aDataFlavor
;
1623 aDataFlavor
.MimeType
= m_aContentType
;
1624 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1626 // get the ClassId using the clipboard format ( internal table )
1627 m_aClassId
= GetClassId_Impl( m_nFormat
);
1629 // get human presentable name using the clipboard format
1630 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1631 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1633 if( m_oContent
&& !m_bIsLinked
&& m_aClassId
!= SvGlobalName() )
1637 void UCBStorage_Impl::CreateContent()
1641 // create content; where to put StreamMode ?! ( already done when opening the file of the package ? )
1642 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1644 OUString
aTemp( m_aURL
);
1646 if ( m_bRepairPackage
)
1648 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1649 m_xProgressHandler
);
1650 aTemp
+= "?repairpackage";
1653 m_oContent
.emplace( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
1655 catch (const ContentCreationException
&)
1657 // content could not be created
1658 SetError( SVSTREAM_CANNOT_MAKE
);
1660 catch (const RuntimeException
&)
1662 // any other error - not specified
1663 SetError( SVSTREAM_CANNOT_MAKE
);
1667 void UCBStorage_Impl::ReadContent()
1669 if ( m_bListCreated
)
1672 m_bListCreated
= true;
1680 // create cursor for access to children
1681 Reference
< XResultSet
> xResultSet
= m_oContent
->createCursor( { "Title", "IsFolder", "MediaType", "Size" }, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS
);
1682 Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
1683 if ( xResultSet
.is() )
1685 while ( xResultSet
->next() )
1687 // insert all into the children list
1688 OUString
aTitle( xRow
->getString(1) );
1691 // unpacked storages have to deal with the meta-inf folder by themselves
1692 if ( aTitle
== "META-INF" )
1696 bool bIsFolder( xRow
->getBoolean(2) );
1697 sal_Int64 nSize
= xRow
->getLong(4);
1698 UCBStorageElement_Impl
* pElement
= new UCBStorageElement_Impl( aTitle
, bIsFolder
, nSize
);
1699 m_aChildrenList
.emplace_back( pElement
);
1701 bool bIsOfficeDocument
= m_bIsLinked
|| ( m_aClassId
!= SvGlobalName() );
1705 OpenStorage( pElement
, m_nMode
, m_bDirect
);
1706 if ( pElement
->m_xStorage
.is() )
1707 pElement
->m_xStorage
->Init();
1709 else if ( bIsOfficeDocument
)
1711 // streams can be external OLE objects, so they are now folders, but storages!
1712 OUString
aName( m_aURL
+ "/" + xRow
->getString(1));
1714 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1715 if ( m_bRepairPackage
)
1717 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1718 m_xProgressHandler
);
1719 aName
+= "?repairpackage";
1722 ::ucbhelper::Content
aContent( aName
, xComEnv
, comphelper::getProcessComponentContext() );
1724 OUString aMediaType
;
1725 Any aAny
= aContent
.getPropertyValue("MediaType");
1726 if ( ( aAny
>>= aMediaType
) && ( aMediaType
== "application/vnd.sun.star.oleobject" ) )
1727 pElement
->m_bIsStorage
= true;
1728 else if ( aMediaType
.isEmpty() )
1730 // older files didn't have that special content type, so they must be detected
1731 OpenStream( pElement
, StreamMode::STD_READ
, m_bDirect
);
1732 if ( Storage::IsStorageFile( pElement
->m_xStream
.get() ) )
1733 pElement
->m_bIsStorage
= true;
1735 pElement
->m_xStream
->Free();
1741 catch (const InteractiveIOException
& r
)
1743 if ( r
.Code
!= IOErrorCode_NOT_EXISTING
)
1744 SetError( ERRCODE_IO_GENERAL
);
1746 catch (const CommandAbortedException
&)
1748 // any command wasn't executed successfully - not specified
1749 if ( !( m_nMode
& StreamMode::WRITE
) )
1750 // if the folder was just inserted and not already committed, this is not an error!
1751 SetError( ERRCODE_IO_GENERAL
);
1753 catch (const RuntimeException
&)
1755 // any other error - not specified
1756 SetError( ERRCODE_IO_GENERAL
);
1758 catch (const ResultSetException
&)
1760 // means that the package file is broken
1761 SetError( ERRCODE_IO_BROKENPACKAGE
);
1763 catch (const SQLException
&)
1765 // means that the file can be broken
1766 SetError( ERRCODE_IO_WRONGFORMAT
);
1768 catch (const Exception
&)
1770 // any other error - not specified
1771 SetError( ERRCODE_IO_GENERAL
);
1775 void UCBStorage_Impl::SetError( ErrCode nError
)
1780 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nError
);
1784 sal_Int32
UCBStorage_Impl::GetObjectCount()
1786 sal_Int32 nCount
= m_aChildrenList
.size();
1787 for (auto& pElement
: m_aChildrenList
)
1789 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1790 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1791 nCount
+= pElement
->m_xStorage
->GetObjectCount();
1797 static OUString
Find_Impl( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, std::u16string_view rPath
)
1799 bool bFound
= false;
1800 for ( const Sequence
< PropertyValue
>& rMyProps
: rSequence
)
1804 for ( const PropertyValue
& rAny
: rMyProps
)
1806 if ( rAny
.Name
== "FullPath" )
1809 if ( ( rAny
.Value
>>= aTmp
) && aTmp
== rPath
)
1811 if ( !aType
.isEmpty() )
1814 else if ( rAny
.Name
== "MediaType" )
1816 if ( ( rAny
.Value
>>= aType
) && !aType
.isEmpty() && bFound
)
1828 void UCBStorage_Impl::SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1830 OUString
aPath( rPath
);
1835 m_aContentType
= m_aOriginalContentType
= Find_Impl( rSequence
, aPath
);
1838 // the "FullPath" of a child always starts without '/'
1841 for (auto& pElement
: m_aChildrenList
)
1843 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1844 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1845 pElement
->m_xStorage
->SetProps( rSequence
, aPath
);
1848 OUString aElementPath
= aPath
+ pElement
->m_aName
;
1849 pElement
->SetContentType( Find_Impl( rSequence
, aElementPath
) );
1853 if ( m_aContentType
.isEmpty() )
1856 // get the clipboard format using the content type
1857 css::datatransfer::DataFlavor aDataFlavor
;
1858 aDataFlavor
.MimeType
= m_aContentType
;
1859 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1861 // get the ClassId using the clipboard format ( internal table )
1862 m_aClassId
= GetClassId_Impl( m_nFormat
);
1864 // get human presentable name using the clipboard format
1865 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1866 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1869 void UCBStorage_Impl::GetProps( sal_Int32
& nProps
, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1871 auto pSequence
= rSequence
.getArray();
1873 // first my own properties
1874 // first property is the "FullPath" name
1875 // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder
1876 OUString
aPath( rPath
);
1880 Sequence
< PropertyValue
> aProps
{ comphelper::makePropertyValue("MediaType", m_aContentType
),
1881 comphelper::makePropertyValue("FullPath", aPath
) };
1882 pSequence
[nProps
++] = aProps
;
1885 // the "FullPath" of a child always starts without '/'
1888 // now the properties of my elements
1889 for (auto& pElement
: m_aChildrenList
)
1891 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1892 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1893 // storages add there properties by themselves ( see above )
1894 pElement
->m_xStorage
->GetProps( nProps
, rSequence
, aPath
);
1897 // properties of streams
1898 OUString aElementPath
= aPath
+ pElement
->m_aName
;
1899 aProps
= { comphelper::makePropertyValue("MediaType", pElement
->GetContentType()),
1900 comphelper::makePropertyValue("FullPath", aElementPath
) };
1901 pSequence
[ nProps
++ ] = aProps
;
1906 UCBStorage_Impl::~UCBStorage_Impl()
1908 m_aChildrenList
.clear();
1911 m_pTempFile
.reset();
1914 bool UCBStorage_Impl::Insert( ::ucbhelper::Content
*pContent
)
1916 // a new substorage is inserted into a UCBStorage ( given by the parameter pContent )
1917 // it must be inserted with a title and a type
1922 const Sequence
< ContentInfo
> aInfo
= pContent
->queryCreatableContentsInfo();
1923 if ( !aInfo
.hasElements() )
1926 for ( const ContentInfo
& rCurr
: aInfo
)
1928 // Simply look for the first KIND_FOLDER...
1929 if ( rCurr
.Attributes
& ContentInfoAttribute::KIND_FOLDER
)
1931 // Make sure the only required bootstrap property is "Title",
1932 const Sequence
< Property
> & rProps
= rCurr
.Properties
;
1933 if ( rProps
.getLength() != 1 )
1936 if ( rProps
[ 0 ].Name
!= "Title" )
1940 if ( !pContent
->insertNewContent( rCurr
.Type
, { "Title" }, { Any(m_aName
) }, aNewFolder
) )
1943 // remove old content, create an "empty" new one and initialize it with the new inserted
1944 m_oContent
.emplace( aNewFolder
);
1949 catch (const CommandAbortedException
&)
1951 // any command wasn't executed successfully - not specified
1952 SetError( ERRCODE_IO_GENERAL
);
1954 catch (const RuntimeException
&)
1956 // any other error - not specified
1957 SetError( ERRCODE_IO_GENERAL
);
1959 catch (const Exception
&)
1961 // any other error - not specified
1962 SetError( ERRCODE_IO_GENERAL
);
1968 sal_Int16
UCBStorage_Impl::Commit()
1970 // send all changes to the package
1971 sal_Int16 nRet
= COMMIT_RESULT_NOTHING_TO_DO
;
1973 // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no
1974 // commit command has been sent
1975 if ( ( m_nMode
& StreamMode::WRITE
) && ( m_bCommited
|| m_bDirect
) )
1979 // all errors will be caught in the "catch" statement outside the loop
1980 for ( size_t i
= 0; i
< m_aChildrenList
.size() && nRet
; ++i
)
1982 auto& pElement
= m_aChildrenList
[ i
];
1983 ::ucbhelper::Content
* pContent
= pElement
->GetContent();
1984 std::unique_ptr
< ::ucbhelper::Content
> xDeleteContent
;
1985 if ( !pContent
&& pElement
->IsModified() )
1987 // if the element has never been opened, no content has been created until now
1988 OUString aName
= m_aURL
+ "/" + pElement
->m_aOriginalName
;
1989 pContent
= new ::ucbhelper::Content( aName
, Reference
< css::ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
1990 xDeleteContent
.reset(pContent
); // delete it later on exit scope
1993 if ( pElement
->m_bIsRemoved
)
1995 // was it inserted, then removed (so there would be nothing to do!)
1996 if ( !pElement
->m_bIsInserted
)
1998 // first remove all open stream handles
1999 if (pContent
&& (!pElement
->m_xStream
.is() || pElement
->m_xStream
->Clear()))
2001 pContent
->executeCommand( "delete", Any( true ) );
2002 nRet
= COMMIT_RESULT_SUCCESS
;
2005 // couldn't release stream because there are external references to it
2006 nRet
= COMMIT_RESULT_FAILURE
;
2011 sal_Int16 nLocalRet
= COMMIT_RESULT_NOTHING_TO_DO
;
2012 if ( pElement
->m_xStorage
.is() )
2014 // element is a storage
2015 // do a commit in the following cases:
2016 // - if storage is already inserted, and changed
2017 // - storage is not in a package
2018 // - it's a new storage, try to insert and commit if successful inserted
2019 if ( !pElement
->m_bIsInserted
|| m_bIsLinked
2020 || pElement
->m_xStorage
->Insert( m_oContent
? &*m_oContent
: nullptr ) )
2022 nLocalRet
= pElement
->m_xStorage
->Commit();
2023 pContent
= pElement
->GetContent();
2026 else if ( pElement
->m_xStream
.is() )
2028 // element is a stream
2029 nLocalRet
= pElement
->m_xStream
->Commit();
2030 if ( pElement
->m_xStream
->m_bIsOLEStorage
)
2032 // OLE storage should be stored encrypted, if the storage uses encryption
2033 pElement
->m_xStream
->m_aContentType
= "application/vnd.sun.star.oleobject";
2036 pElement
->m_xStream
->m_pContent
->setPropertyValue("Encrypted", aValue
);
2039 pContent
= pElement
->GetContent();
2042 if (pContent
&& pElement
->m_aName
!= pElement
->m_aOriginalName
)
2044 // name ( title ) of the element was changed
2045 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2046 pContent
->setPropertyValue("Title", Any(pElement
->m_aName
) );
2049 if (pContent
&& pElement
->IsLoaded() && pElement
->GetContentType() != pElement
->GetOriginalContentType())
2051 // mediatype of the element was changed
2052 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2053 pContent
->setPropertyValue("MediaType", Any(pElement
->GetContentType()) );
2056 if ( nLocalRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2060 if ( nRet
== COMMIT_RESULT_FAILURE
)
2064 catch (const ContentCreationException
&)
2066 // content could not be created
2067 SetError( ERRCODE_IO_NOTEXISTS
);
2068 return COMMIT_RESULT_FAILURE
;
2070 catch (const CommandAbortedException
&)
2072 // any command wasn't executed successfully - not specified
2073 SetError( ERRCODE_IO_GENERAL
);
2074 return COMMIT_RESULT_FAILURE
;
2076 catch (const RuntimeException
&)
2078 // any other error - not specified
2079 SetError( ERRCODE_IO_GENERAL
);
2080 return COMMIT_RESULT_FAILURE
;
2082 catch (const Exception
&)
2084 // any other error - not specified
2085 SetError( ERRCODE_IO_GENERAL
);
2086 return COMMIT_RESULT_FAILURE
;
2089 if ( m_bIsRoot
&& m_oContent
)
2091 // the root storage must flush the root package content
2092 if ( nRet
== COMMIT_RESULT_SUCCESS
)
2096 // commit the media type to the JAR file
2097 // clipboard format and ClassId will be retrieved from the media type when the file is loaded again
2099 aType
<<= m_aContentType
;
2100 m_oContent
->setPropertyValue("MediaType", aType
);
2104 // write a manifest file
2105 // first create a subfolder "META-inf"
2106 Content aNewSubFolder
;
2107 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_oContent
, "META-INF", aNewSubFolder
);
2110 // create a stream to write the manifest file - use a temp file
2111 OUString
aURL( aNewSubFolder
.getURL() );
2112 std::optional
< ::utl::TempFileNamed
> pTempFile(&aURL
);
2114 // get the stream from the temp file and create an output stream wrapper
2115 SvStream
* pStream
= pTempFile
->GetStream( StreamMode::STD_READWRITE
);
2116 rtl::Reference
<::utl::OOutputStreamWrapper
> xOutputStream
= new ::utl::OOutputStreamWrapper( *pStream
);
2118 // create a manifest writer object that will fill the stream
2119 Reference
< css::packages::manifest::XManifestWriter
> xWriter
=
2120 css::packages::manifest::ManifestWriter::create(
2121 ::comphelper::getProcessComponentContext() );
2122 sal_Int32 nCount
= GetObjectCount() + 1;
2123 Sequence
< Sequence
< PropertyValue
> > aProps( nCount
);
2124 sal_Int32 nProps
= 0;
2125 GetProps( nProps
, aProps
, OUString() );
2126 xWriter
->writeManifestSequence( xOutputStream
, aProps
);
2128 // move the stream to its desired location
2129 Content
aSource( pTempFile
->GetURL(), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2131 xOutputStream
= nullptr;
2133 aNewSubFolder
.transferContent( aSource
, InsertOperation::Move
, "manifest.xml", NameClash::OVERWRITE
);
2138 #if OSL_DEBUG_LEVEL > 0
2139 SAL_INFO("sot", "Files: " << nOpenFiles
);
2140 SAL_INFO("sot", "Streams: " << nOpenStreams
);
2144 m_oContent
->executeCommand( "flush", aAny
);
2145 if ( m_pSource
!= nullptr )
2147 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READ
));
2148 m_pSource
->SetStreamSize(0);
2149 // m_pSource->Seek(0);
2150 pStream
->ReadStream( *m_pSource
);
2156 catch (const CommandAbortedException
&)
2158 // how to tell the content : forget all changes ?!
2159 // or should we assume that the content does it by itself because he threw an exception ?!
2160 // any command wasn't executed successfully - not specified
2161 SetError( ERRCODE_IO_GENERAL
);
2162 return COMMIT_RESULT_FAILURE
;
2164 catch (const RuntimeException
&)
2166 // how to tell the content : forget all changes ?!
2167 // or should we assume that the content does it by itself because he threw an exception ?!
2168 // any other error - not specified
2169 SetError( ERRCODE_IO_GENERAL
);
2170 return COMMIT_RESULT_FAILURE
;
2172 catch (const InteractiveIOException
& r
)
2174 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
|| r
.Code
== IOErrorCode_LOCKING_VIOLATION
)
2175 SetError( ERRCODE_IO_ACCESSDENIED
);
2176 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
2177 SetError( ERRCODE_IO_NOTEXISTS
);
2178 else if ( r
.Code
== IOErrorCode_CANT_READ
)
2179 SetError( ERRCODE_IO_CANTREAD
);
2180 else if ( r
.Code
== IOErrorCode_CANT_WRITE
)
2181 SetError( ERRCODE_IO_CANTWRITE
);
2183 SetError( ERRCODE_IO_GENERAL
);
2185 return COMMIT_RESULT_FAILURE
;
2187 catch (const Exception
&)
2189 // how to tell the content : forget all changes ?!
2190 // or should we assume that the content does it by itself because he threw an exception ?!
2191 // any other error - not specified
2192 SetError( ERRCODE_IO_GENERAL
);
2193 return COMMIT_RESULT_FAILURE
;
2196 else if ( nRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2198 // how to tell the content : forget all changes ?! Should we ?!
2199 SetError( ERRCODE_IO_GENERAL
);
2203 // after successful root commit all elements names and types are adjusted and all removed elements
2204 // are also removed from the lists
2205 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2207 auto& pInnerElement
= m_aChildrenList
[ i
];
2208 if ( pInnerElement
->m_bIsRemoved
)
2209 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2212 pInnerElement
->m_aOriginalName
= pInnerElement
->m_aName
;
2213 pInnerElement
->m_bIsInserted
= false;
2219 m_bCommited
= false;
2225 void UCBStorage_Impl::Revert()
2227 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2229 auto& pElement
= m_aChildrenList
[ i
];
2230 pElement
->m_bIsRemoved
= false;
2231 if ( pElement
->m_bIsInserted
)
2232 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2235 if ( pElement
->m_xStream
.is() )
2237 pElement
->m_xStream
->m_bCommited
= false;
2238 pElement
->m_xStream
->Revert();
2240 else if ( pElement
->m_xStorage
.is() )
2242 pElement
->m_xStorage
->m_bCommited
= false;
2243 pElement
->m_xStorage
->Revert();
2246 pElement
->m_aName
= pElement
->m_aOriginalName
;
2247 pElement
->m_bIsRemoved
= false;
2253 const OUString
& UCBStorage::GetName() const
2255 return pImp
->m_aName
; // pImp->m_aURL ?!
2258 bool UCBStorage::IsRoot() const
2260 return pImp
->m_bIsRoot
;
2263 void UCBStorage::SetDirty()
2267 void UCBStorage::SetClass( const SvGlobalName
& rClass
, SotClipboardFormatId nOriginalClipFormat
, const OUString
& rUserTypeName
)
2269 pImp
->m_aClassId
= rClass
;
2270 pImp
->m_nFormat
= nOriginalClipFormat
;
2271 pImp
->m_aUserTypeName
= rUserTypeName
;
2273 // in UCB storages only the content type will be stored, all other information can be reconstructed
2274 // ( see the UCBStorage_Impl::Init() method )
2275 css::datatransfer::DataFlavor aDataFlavor
;
2276 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2277 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2280 void UCBStorage::SetClassId( const ClsId
& rClsId
)
2282 pImp
->m_aClassId
= SvGlobalName( rClsId
);
2283 if ( pImp
->m_aClassId
== SvGlobalName() )
2286 // in OLE storages the clipboard format and the user name will be transferred when a storage is copied because both are
2287 // stored in one the substreams
2288 // UCB storages store the content type information as content type in the manifest file and so this information must be
2289 // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from
2291 pImp
->m_nFormat
= GetFormatId_Impl( pImp
->m_aClassId
);
2292 if ( pImp
->m_nFormat
!= SotClipboardFormatId::NONE
)
2294 css::datatransfer::DataFlavor aDataFlavor
;
2295 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2296 pImp
->m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
2297 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2301 const ClsId
& UCBStorage::GetClassId() const
2303 return pImp
->m_aClassId
.GetCLSID();
2306 SvGlobalName
UCBStorage::GetClassName()
2308 return pImp
->m_aClassId
;
2311 SotClipboardFormatId
UCBStorage::GetFormat()
2313 return pImp
->m_nFormat
;
2316 OUString
UCBStorage::GetUserName()
2318 OSL_FAIL("UserName is not implemented in UCB storages!" );
2319 return pImp
->m_aUserTypeName
;
2322 void UCBStorage::FillInfoList( SvStorageInfoList
* pList
) const
2324 // put information in childrenlist into StorageInfoList
2325 for (auto& pElement
: pImp
->GetChildrenList())
2327 if ( !pElement
->m_bIsRemoved
)
2329 // problem: what about the size of a substorage ?!
2330 sal_uInt64 nSize
= pElement
->m_nSize
;
2331 if ( pElement
->m_xStream
.is() )
2332 nSize
= pElement
->m_xStream
->GetSize();
2333 SvStorageInfo
aInfo( pElement
->m_aName
, nSize
, pElement
->m_bIsStorage
);
2334 pList
->push_back( aInfo
);
2339 bool UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl
const & rElement
, BaseStorage
* pDest
, const OUString
& rNew
) const
2341 // insert stream or storage into the list or stream of the destination storage
2342 // not into the content, this will be done on commit !
2343 // be aware of name changes !
2344 if ( !rElement
.m_bIsStorage
)
2346 // copy the streams data
2347 // the destination stream must not be open
2348 tools::SvRef
<BaseStorageStream
> pOtherStream(pDest
->OpenStream( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2349 BaseStorageStream
* pStream
= nullptr;
2350 bool bDeleteStream
= false;
2352 // if stream is already open, it is allowed to copy it, so be aware of this
2353 if ( rElement
.m_xStream
.is() )
2354 pStream
= rElement
.m_xStream
->m_pAntiImpl
;
2357 pStream
= const_cast< UCBStorage
* >(this)->OpenStream( rElement
.m_aName
, StreamMode::STD_READ
, pImp
->m_bDirect
);
2358 bDeleteStream
= true;
2361 pStream
->CopyTo( pOtherStream
.get() );
2362 SetError( pStream
->GetError() );
2363 if( pOtherStream
->GetError() )
2364 pDest
->SetError( pOtherStream
->GetError() );
2366 pOtherStream
->Commit();
2368 if ( bDeleteStream
)
2373 // copy the storage content
2374 // the destination storage must not be open
2375 BaseStorage
* pStorage
= nullptr;
2377 // if stream is already open, it is allowed to copy it, so be aware of this
2378 bool bDeleteStorage
= false;
2379 if ( rElement
.m_xStorage
.is() )
2380 pStorage
= rElement
.m_xStorage
->m_pAntiImpl
;
2383 pStorage
= const_cast<UCBStorage
*>(this)->OpenStorage( rElement
.m_aName
, pImp
->m_nMode
, pImp
->m_bDirect
);
2384 bDeleteStorage
= true;
2387 UCBStorage
* pUCBDest
= dynamic_cast<UCBStorage
*>( pDest
);
2388 UCBStorage
* pUCBCopy
= dynamic_cast<UCBStorage
*>( pStorage
);
2390 bool bOpenUCBStorage
= pUCBDest
&& pUCBCopy
;
2391 tools::SvRef
<BaseStorage
> pOtherStorage(bOpenUCBStorage
?
2392 pDest
->OpenUCBStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
) :
2393 pDest
->OpenOLEStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2395 // For UCB storages, the class id and the format id may differ,
2396 // do passing the class id is not sufficient.
2397 if( bOpenUCBStorage
)
2398 pOtherStorage
->SetClass( pStorage
->GetClassName(),
2399 pStorage
->GetFormat(),
2400 pUCBCopy
->pImp
->m_aUserTypeName
);
2402 pOtherStorage
->SetClassId( pStorage
->GetClassId() );
2403 pStorage
->CopyTo( pOtherStorage
.get() );
2404 SetError( pStorage
->GetError() );
2405 if( pOtherStorage
->GetError() )
2406 pDest
->SetError( pOtherStorage
->GetError() );
2408 pOtherStorage
->Commit();
2410 if ( bDeleteStorage
)
2414 return Good() && pDest
->Good();
2417 UCBStorageElement_Impl
* UCBStorage::FindElement_Impl( std::u16string_view rName
) const
2419 DBG_ASSERT( !rName
.empty(), "Name is empty!" );
2420 for (const auto& pElement
: pImp
->GetChildrenList())
2422 if ( pElement
->m_aName
== rName
&& !pElement
->m_bIsRemoved
)
2423 return pElement
.get();
2428 bool UCBStorage::CopyTo( BaseStorage
* pDestStg
) const
2430 DBG_ASSERT( pDestStg
!= static_cast<BaseStorage
const *>(this), "Self-Copying is not possible!" );
2431 if ( pDestStg
== static_cast<BaseStorage
const *>(this) )
2434 // perhaps it's also a problem if one storage is a parent of the other ?!
2435 // or if not: could be optimized ?!
2437 // For UCB storages, the class id and the format id may differ,
2438 // do passing the class id is not sufficient.
2439 if( dynamic_cast<const UCBStorage
*>(pDestStg
) != nullptr )
2440 pDestStg
->SetClass( pImp
->m_aClassId
, pImp
->m_nFormat
,
2441 pImp
->m_aUserTypeName
);
2443 pDestStg
->SetClassId( GetClassId() );
2444 pDestStg
->SetDirty();
2447 for ( size_t i
= 0; i
< pImp
->GetChildrenList().size() && bRet
; ++i
)
2449 auto& pElement
= pImp
->GetChildrenList()[ i
];
2450 if ( !pElement
->m_bIsRemoved
)
2451 bRet
= CopyStorageElement_Impl( *pElement
, pDestStg
, pElement
->m_aName
);
2455 SetError( pDestStg
->GetError() );
2456 return Good() && pDestStg
->Good();
2459 bool UCBStorage::CopyTo( const OUString
& rElemName
, BaseStorage
* pDest
, const OUString
& rNew
)
2461 if( rElemName
.isEmpty() )
2464 if ( pDest
== static_cast<BaseStorage
*>(this) )
2466 // can't double an element
2471 // for copying no optimization is useful, because in every case the stream data must be copied
2472 UCBStorageElement_Impl
* pElement
= FindElement_Impl( rElemName
);
2474 return CopyStorageElement_Impl( *pElement
, pDest
, rNew
);
2477 SetError( SVSTREAM_FILE_NOT_FOUND
);
2483 bool UCBStorage::Commit()
2485 // mark this storage for sending it on root commit
2486 pImp
->m_bCommited
= true;
2487 if ( pImp
->m_bIsRoot
)
2488 // the root storage coordinates committing by sending a Commit command to its content
2489 return ( pImp
->Commit() != COMMIT_RESULT_FAILURE
);
2494 bool UCBStorage::Revert()
2500 BaseStorageStream
* UCBStorage::OpenStream( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2502 if( rEleName
.isEmpty() )
2505 // try to find the storage element
2506 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2509 // element does not exist, check if creation is allowed
2510 if( nMode
& StreamMode::NOCREATE
)
2512 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2513 OUString aName
= pImp
->m_aURL
+ "/" + rEleName
;
2514 UCBStorageStream
* pStream
= new UCBStorageStream( aName
, nMode
, bDirect
, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2515 pStream
->SetError( GetError() );
2516 pStream
->pImp
->m_aName
= rEleName
;
2521 // create a new UCBStorageElement and insert it into the list
2522 pElement
= new UCBStorageElement_Impl( rEleName
);
2523 pElement
->m_bIsInserted
= true;
2524 pImp
->m_aChildrenList
.emplace_back( pElement
);
2528 if ( !pElement
->m_bIsFolder
)
2530 // check if stream is already created
2531 if ( pElement
->m_xStream
.is() )
2533 // stream has already been created; if it has no external reference, it may be opened another time
2534 if ( pElement
->m_xStream
->m_pAntiImpl
)
2536 OSL_FAIL("Stream is already open!" );
2537 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2542 // check if stream is opened with the same keyword as before
2543 // if not, generate a new stream because it could be encrypted vs. decrypted!
2544 if ( pElement
->m_xStream
->m_aKey
.isEmpty() )
2546 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2548 return new UCBStorageStream( pElement
->m_xStream
.get() );
2553 // stream is opened the first time
2554 pImp
->OpenStream( pElement
, nMode
, bDirect
);
2556 // if name has been changed before creating the stream: set name!
2557 pElement
->m_xStream
->m_aName
= rEleName
;
2558 return new UCBStorageStream( pElement
->m_xStream
.get() );
2564 void UCBStorage_Impl::OpenStream( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2566 OUString aName
= m_aURL
+ "/" +pElement
->m_aOriginalName
;
2567 pElement
->m_xStream
= new UCBStorageStream_Impl( aName
, nMode
, nullptr, bDirect
, m_bRepairPackage
, m_xProgressHandler
);
2570 BaseStorage
* UCBStorage::OpenUCBStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2572 if( rEleName
.isEmpty() )
2575 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2578 BaseStorage
* UCBStorage::OpenOLEStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2580 if( rEleName
.isEmpty() )
2583 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, false );
2586 BaseStorage
* UCBStorage::OpenStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2588 if( rEleName
.isEmpty() )
2591 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2594 BaseStorage
* UCBStorage::OpenStorage_Impl( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
, bool bForceUCBStorage
)
2596 // try to find the storage element
2597 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2600 // element does not exist, check if creation is allowed
2601 if( nMode
& StreamMode::NOCREATE
)
2603 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2604 OUString aName
= pImp
->m_aURL
+ "/" + rEleName
; // ???
2605 UCBStorage
*pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2606 pStorage
->pImp
->m_bIsRoot
= false;
2607 pStorage
->pImp
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2608 pStorage
->SetError( GetError() );
2612 // create a new UCBStorageElement and insert it into the list
2613 // problem: perhaps an OLEStorage should be created ?!
2614 // Because nothing is known about the element that should be created, an external parameter is needed !
2615 pElement
= new UCBStorageElement_Impl( rEleName
);
2616 pElement
->m_bIsInserted
= true;
2617 pImp
->m_aChildrenList
.emplace_back( pElement
);
2620 if ( !pElement
->m_bIsFolder
&& ( pElement
->m_bIsStorage
|| !bForceUCBStorage
) )
2622 // create OLE storages on a stream ( see ctor of SotStorage )
2623 // Such a storage will be created on a UCBStorageStream; it will write into the stream
2624 // if it is opened in direct mode or when it is committed. In this case the stream will be
2625 // modified and then it MUST be treated as committed.
2626 if ( !pElement
->m_xStream
.is() )
2628 BaseStorageStream
* pStr
= OpenStream( rEleName
, nMode
, bDirect
);
2629 UCBStorageStream
* pStream
= dynamic_cast<UCBStorageStream
*>( pStr
);
2632 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2636 pElement
->m_xStream
= pStream
->pImp
;
2640 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2641 bool bInited
= pElement
->m_xStream
->Init();
2644 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2648 pElement
->m_bIsStorage
= true;
2649 return pElement
->m_xStream
->CreateStorage(); // can only be created in transacted mode
2651 else if ( pElement
->m_xStorage
.is() )
2653 // storage has already been opened; if it has no external reference, it may be opened another time
2654 if ( pElement
->m_xStorage
->m_pAntiImpl
)
2656 OSL_FAIL("Storage is already open!" );
2657 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2661 bool bIsWritable
= bool( pElement
->m_xStorage
->m_nMode
& StreamMode::WRITE
);
2662 if ( !bIsWritable
&& ( nMode
& StreamMode::WRITE
) )
2664 OUString aName
= pImp
->m_aURL
+ "/" + pElement
->m_aOriginalName
;
2665 UCBStorage
* pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2666 pElement
->m_xStorage
= pStorage
->pImp
;
2671 return new UCBStorage( pElement
->m_xStorage
.get() );
2675 else if ( !pElement
->m_xStream
.is() )
2677 // storage is opened the first time
2678 bool bIsWritable
= bool(pImp
->m_nMode
& StreamMode::WRITE
);
2679 if ( pImp
->m_bIsLinked
&& pImp
->m_bIsRoot
&& bIsWritable
)
2681 // make sure that the root storage object has been created before substorages will be created
2682 INetURLObject
aFolderObj( pImp
->m_aURL
);
2683 aFolderObj
.removeSegment();
2685 Content
aFolder( aFolderObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2686 pImp
->m_oContent
.emplace();
2687 bool bRet
= ::utl::UCBContentHelper::MakeFolder( aFolder
, pImp
->m_aName
, *pImp
->m_oContent
);
2690 SetError( SVSTREAM_CANNOT_MAKE
);
2695 UCBStorage_Impl
* pStor
= pImp
->OpenStorage( pElement
, nMode
, bDirect
);
2698 if ( pElement
->m_bIsInserted
)
2699 pStor
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2701 return new UCBStorage( pStor
);
2708 UCBStorage_Impl
* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2710 UCBStorage_Impl
* pRet
= nullptr;
2711 OUString aName
= m_aURL
+ "/" + pElement
->m_aOriginalName
; // ???
2713 pElement
->m_bIsStorage
= pElement
->m_bIsFolder
= true;
2715 if ( m_bIsLinked
&& !::utl::UCBContentHelper::Exists( aName
) )
2718 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_oContent
, pElement
->m_aOriginalName
, aNewFolder
);
2720 pRet
= new UCBStorage_Impl( aNewFolder
, aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2724 pRet
= new UCBStorage_Impl( aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2729 pRet
->m_bIsLinked
= m_bIsLinked
;
2730 pRet
->m_bIsRoot
= false;
2732 // if name has been changed before creating the stream: set name!
2733 pRet
->m_aName
= pElement
->m_aOriginalName
;
2734 pElement
->m_xStorage
= pRet
;
2743 bool UCBStorage::IsStorage( const OUString
& rEleName
) const
2745 if( rEleName
.isEmpty() )
2748 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2749 return ( pElement
&& pElement
->m_bIsStorage
);
2752 bool UCBStorage::IsStream( const OUString
& rEleName
) const
2754 if( rEleName
.isEmpty() )
2757 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2758 return ( pElement
&& !pElement
->m_bIsStorage
);
2761 bool UCBStorage::IsContained( const OUString
& rEleName
) const
2763 if( rEleName
.isEmpty() )
2765 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2766 return ( pElement
!= nullptr );
2769 void UCBStorage::Remove( const OUString
& rEleName
)
2771 if( rEleName
.isEmpty() )
2774 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2777 pElement
->m_bIsRemoved
= true;
2780 SetError( SVSTREAM_FILE_NOT_FOUND
);
2783 bool UCBStorage::ValidateFAT()
2789 bool UCBStorage::Validate( bool bWrite
) const
2792 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
2795 bool UCBStorage::ValidateMode( StreamMode m
) const
2798 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
2800 // only SHARE_DENYALL allowed
2801 // storages open in r/o mode are OK, since only
2802 // the commit may fail
2803 if( m
& StreamMode::SHARE_DENYALL
)
2809 bool UCBStorage::Equals( const BaseStorage
& rStorage
) const
2812 return static_cast<BaseStorage
const *>(this) == &rStorage
;
2815 bool UCBStorage::IsStorageFile( SvStream
* pFile
)
2820 sal_uInt64 nPos
= pFile
->Tell();
2821 if ( pFile
->TellEnd() < 4 )
2825 sal_uInt32
nBytes(0);
2826 pFile
->ReadUInt32( nBytes
);
2828 // search for the magic bytes
2829 bool bRet
= ( nBytes
== 0x04034b50 );
2832 // disk spanned file have an additional header in front of the usual one
2833 bRet
= ( nBytes
== 0x08074b50 );
2837 pFile
->ReadUInt32( nBytes
);
2838 bRet
= ( nBytes
== 0x04034b50 );
2842 pFile
->Seek( nPos
);
2846 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */