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
, public comphelper::ByteReader
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
;
110 virtual sal_Int32
readSomeBytes(sal_Int8
* aData
, sal_Int32 nBytesToRead
) override
;
113 void checkConnected();
119 FileStreamWrapper_Impl::FileStreamWrapper_Impl( OUString aName
)
120 : m_aURL(std::move( aName
))
122 // if no URL is provided the stream is empty
126 FileStreamWrapper_Impl::~FileStreamWrapper_Impl()
131 #if OSL_DEBUG_LEVEL > 0
136 if (!m_aURL
.isEmpty())
137 osl::File::remove(m_aURL
);
141 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
143 if ( m_aURL
.isEmpty() )
151 if (nBytesToRead
< 0)
152 throw BufferSizeExceededException(OUString(), getXWeak());
154 std::scoped_lock
aGuard( m_aMutex
);
156 if (aData
.getLength() < nBytesToRead
)
157 aData
.realloc(nBytesToRead
);
159 sal_uInt32 nRead
= m_pSvStream
->ReadBytes(static_cast<void*>(aData
.getArray()), nBytesToRead
);
162 // if read characters < MaxLength, adjust sequence
163 if (nRead
< o3tl::make_unsigned(aData
.getLength()))
164 aData
.realloc( nRead
);
169 sal_Int32
FileStreamWrapper_Impl::readSomeBytes(sal_Int8
* aData
, sal_Int32 nBytesToRead
)
171 if ( m_aURL
.isEmpty() )
176 if (nBytesToRead
< 0)
177 throw BufferSizeExceededException(OUString(), getXWeak());
179 std::scoped_lock
aGuard( m_aMutex
);
181 sal_uInt32 nRead
= m_pSvStream
->ReadBytes(static_cast<void*>(aData
), nBytesToRead
);
187 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
189 if ( m_aURL
.isEmpty() )
197 if (nMaxBytesToRead
< 0)
198 throw BufferSizeExceededException(OUString(), getXWeak());
200 if (m_pSvStream
->eof())
206 return readBytes(aData
, nMaxBytesToRead
);
210 void SAL_CALL
FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip
)
212 if ( m_aURL
.isEmpty() )
215 std::scoped_lock
aGuard( m_aMutex
);
218 m_pSvStream
->SeekRel(nBytesToSkip
);
223 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::available()
225 if ( m_aURL
.isEmpty() )
228 std::scoped_lock
aGuard( m_aMutex
);
231 sal_Int64 nAvailable
= m_pSvStream
->remainingSize();
234 return std::min
<sal_Int64
>(SAL_MAX_INT32
, nAvailable
);
238 void SAL_CALL
FileStreamWrapper_Impl::closeInput()
240 if ( m_aURL
.isEmpty() )
243 std::scoped_lock
aGuard( m_aMutex
);
246 #if OSL_DEBUG_LEVEL > 0
249 osl::File::remove(m_aURL
);
254 void SAL_CALL
FileStreamWrapper_Impl::seek( sal_Int64 _nLocation
)
256 if ( m_aURL
.isEmpty() )
259 std::scoped_lock
aGuard( m_aMutex
);
262 m_pSvStream
->Seek(static_cast<sal_uInt32
>(_nLocation
));
267 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getPosition( )
269 if ( m_aURL
.isEmpty() )
272 std::scoped_lock
aGuard( m_aMutex
);
275 sal_uInt64 nPos
= m_pSvStream
->Tell();
281 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getLength( )
283 if ( m_aURL
.isEmpty() )
286 std::scoped_lock
aGuard( m_aMutex
);
291 sal_Int64 nEndPos
= m_pSvStream
->TellEnd();
297 void FileStreamWrapper_Impl::checkConnected()
299 if ( m_aURL
.isEmpty() )
300 throw NotConnectedException(OUString(), getXWeak());
303 m_pSvStream
= ::utl::UcbStreamHelper::CreateStream( m_aURL
, StreamMode::STD_READ
);
304 #if OSL_DEBUG_LEVEL > 0
311 void FileStreamWrapper_Impl::checkError()
315 if (m_pSvStream
->SvStream::GetError() != ERRCODE_NONE
)
316 // TODO: really evaluate the error
317 throw NotConnectedException(OUString(), getXWeak());
321 #define COMMIT_RESULT_FAILURE 0
322 #define COMMIT_RESULT_NOTHING_TO_DO 1
323 #define COMMIT_RESULT_SUCCESS 2
325 static SotClipboardFormatId
GetFormatId_Impl( const SvGlobalName
& aName
)
327 if ( aName
== SvGlobalName( SO3_SW_CLASSID_60
) )
328 return SotClipboardFormatId::STARWRITER_60
;
329 if ( aName
== SvGlobalName( SO3_SWWEB_CLASSID_60
) )
330 return SotClipboardFormatId::STARWRITERWEB_60
;
331 if ( aName
== SvGlobalName( SO3_SWGLOB_CLASSID_60
) )
332 return SotClipboardFormatId::STARWRITERGLOB_60
;
333 if ( aName
== SvGlobalName( SO3_SDRAW_CLASSID_60
) )
334 return SotClipboardFormatId::STARDRAW_60
;
335 if ( aName
== SvGlobalName( SO3_SIMPRESS_CLASSID_60
) )
336 return SotClipboardFormatId::STARIMPRESS_60
;
337 if ( aName
== SvGlobalName( SO3_SC_CLASSID_60
) )
338 return SotClipboardFormatId::STARCALC_60
;
339 if ( aName
== SvGlobalName( SO3_SCH_CLASSID_60
) )
340 return SotClipboardFormatId::STARCHART_60
;
341 if ( aName
== SvGlobalName( SO3_SM_CLASSID_60
) )
342 return SotClipboardFormatId::STARMATH_60
;
343 if ( aName
== SvGlobalName( SO3_OUT_CLASSID
) ||
344 aName
== SvGlobalName( SO3_APPLET_CLASSID
) ||
345 aName
== SvGlobalName( SO3_PLUGIN_CLASSID
) ||
346 aName
== SvGlobalName( SO3_IFRAME_CLASSID
) )
347 // allowed, but not supported
348 return SotClipboardFormatId::NONE
;
351 OSL_FAIL( "Unknown UCB storage format!" );
352 return SotClipboardFormatId::NONE
;
357 static SvGlobalName
GetClassId_Impl( SotClipboardFormatId nFormat
)
361 case SotClipboardFormatId::STARWRITER_8
:
362 case SotClipboardFormatId::STARWRITER_8_TEMPLATE
:
363 return SvGlobalName( SO3_SW_CLASSID_60
);
364 case SotClipboardFormatId::STARWRITERWEB_8
:
365 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
366 case SotClipboardFormatId::STARWRITERGLOB_8
:
367 case SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE
:
368 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
369 case SotClipboardFormatId::STARDRAW_8
:
370 case SotClipboardFormatId::STARDRAW_8_TEMPLATE
:
371 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
372 case SotClipboardFormatId::STARIMPRESS_8
:
373 case SotClipboardFormatId::STARIMPRESS_8_TEMPLATE
:
374 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
375 case SotClipboardFormatId::STARCALC_8
:
376 case SotClipboardFormatId::STARCALC_8_TEMPLATE
:
377 return SvGlobalName( SO3_SC_CLASSID_60
);
378 case SotClipboardFormatId::STARCHART_8
:
379 case SotClipboardFormatId::STARCHART_8_TEMPLATE
:
380 return SvGlobalName( SO3_SCH_CLASSID_60
);
381 case SotClipboardFormatId::STARMATH_8
:
382 case SotClipboardFormatId::STARMATH_8_TEMPLATE
:
383 return SvGlobalName( SO3_SM_CLASSID_60
);
384 case SotClipboardFormatId::STARWRITER_60
:
385 return SvGlobalName( SO3_SW_CLASSID_60
);
386 case SotClipboardFormatId::STARWRITERWEB_60
:
387 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
388 case SotClipboardFormatId::STARWRITERGLOB_60
:
389 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
390 case SotClipboardFormatId::STARDRAW_60
:
391 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
392 case SotClipboardFormatId::STARIMPRESS_60
:
393 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
394 case SotClipboardFormatId::STARCALC_60
:
395 return SvGlobalName( SO3_SC_CLASSID_60
);
396 case SotClipboardFormatId::STARCHART_60
:
397 return SvGlobalName( SO3_SCH_CLASSID_60
);
398 case SotClipboardFormatId::STARMATH_60
:
399 return SvGlobalName( SO3_SM_CLASSID_60
);
401 return SvGlobalName();
405 // All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle
406 // class, that uses the refcounted object as impl-class.
408 class UCBStorageStream_Impl
: public SvRefBase
, public SvStream
410 virtual ~UCBStorageStream_Impl() override
;
413 virtual std::size_t GetData(void* pData
, std::size_t nSize
) override
;
414 virtual std::size_t PutData(const void* pData
, std::size_t nSize
) override
;
415 virtual sal_uInt64
SeekPos( sal_uInt64 nPos
) override
;
416 virtual void SetSize( sal_uInt64 nSize
) override
;
417 virtual void FlushData() override
;
418 virtual void ResetError() override
;
420 UCBStorageStream
* m_pAntiImpl
; // only valid if an external reference exists
422 OUString m_aOriginalName
;// the original name before accessing the stream
423 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
424 OUString m_aURL
; // the full path name to create the content
425 OUString m_aContentType
;
426 OUString m_aOriginalContentType
;
428 ::ucbhelper::Content
* m_pContent
; // the content that provides the data
429 Reference
<XInputStream
> m_rSource
; // the stream covering the original data of the content
430 std::unique_ptr
<SvStream
> m_pStream
; // the stream worked on; for readonly streams it is the original stream of the content
431 // for read/write streams it's a copy into a temporary file
432 OUString m_aTempURL
; // URL of this temporary stream
434 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
435 bool m_bSourceRead
; // Source still contains useful information
436 bool m_bModified
; // only modified streams will be sent to the original content
437 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
438 bool m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
439 // this means that the root storage does an autocommit when its external
440 // reference is destroyed
441 bool m_bIsOLEStorage
;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream
443 UCBStorageStream_Impl( const OUString
&, StreamMode
, UCBStorageStream
*, bool,
444 bool bRepair
, Reference
< XProgressHandler
> const & xProgress
);
449 sal_Int16
Commit(); // if modified and committed: transfer an XInputStream to the content
450 void Revert(); // discard all changes
451 BaseStorage
* CreateStorage();// create an OLE Storage on the UCBStorageStream
452 sal_uInt64
GetSize();
454 sal_uInt64
ReadSourceWriteTemporary( sal_uInt64 aLength
); // read aLength from source and copy to temporary,
455 // no seeking is produced
456 void ReadSourceWriteTemporary(); // read source till the end and copy to temporary,
458 void CopySourceToTemporary(); // same as ReadSourceWriteToTemporary()
459 // but the writing is done at the end of temporary
460 // pointer position is not changed
461 using SvStream::SetError
;
462 void SetError( ErrCode nError
);
463 void PrepareCachedForReopen( StreamMode nMode
);
466 typedef tools::SvRef
<UCBStorageStream_Impl
> UCBStorageStream_ImplRef
;
468 struct UCBStorageElement_Impl
;
469 typedef std::vector
<std::unique_ptr
<UCBStorageElement_Impl
>> UCBStorageElementList_Impl
;
471 class UCBStorage_Impl
: public SvRefBase
473 virtual ~UCBStorage_Impl() override
;
475 UCBStorage
* m_pAntiImpl
; // only valid if external references exists
477 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
478 OUString m_aURL
; // the full path name to create the content
479 OUString m_aContentType
;
480 OUString m_aOriginalContentType
;
481 std::optional
<::ucbhelper::Content
> m_oContent
; // the content that provides the storage elements
482 std::unique_ptr
<::utl::TempFileNamed
> m_pTempFile
; // temporary file, only for storages on stream
483 SvStream
* m_pSource
; // original stream, only for storages on a stream
485 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
486 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
487 bool m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
488 // this means that the root storage does an autocommit when its external
489 // reference is destroyed
490 bool m_bIsRoot
; // marks this storage as root storages that manages all commits and reverts
493 SotClipboardFormatId m_nFormat
;
494 OUString m_aUserTypeName
;
495 SvGlobalName m_aClassId
;
497 UCBStorageElementList_Impl m_aChildrenList
;
499 bool m_bRepairPackage
;
500 Reference
< XProgressHandler
> m_xProgressHandler
;
502 UCBStorage_Impl( const ::ucbhelper::Content
&, const OUString
&, StreamMode
, UCBStorage
*, bool,
503 bool, bool = false, Reference
< XProgressHandler
> const & = Reference
< XProgressHandler
>() );
504 UCBStorage_Impl( const OUString
&, StreamMode
, UCBStorage
*, bool, bool,
505 bool, Reference
< XProgressHandler
> const & );
506 UCBStorage_Impl( SvStream
&, UCBStorage
*, bool );
510 bool Insert( ::ucbhelper::Content
*pContent
);
511 UCBStorage_Impl
* OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
);
512 void OpenStream( UCBStorageElement_Impl
*, StreamMode
, bool );
513 void SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
514 void GetProps( sal_Int32
&, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
515 sal_Int32
GetObjectCount();
517 void CreateContent();
518 ::ucbhelper::Content
* GetContent()
522 return m_oContent
? &*m_oContent
: nullptr;
524 UCBStorageElementList_Impl
& GetChildrenList()
526 const ErrCode nError
= m_nError
;
528 if ( m_nMode
& StreamMode::WRITE
)
533 m_pAntiImpl
->ResetError();
534 m_pAntiImpl
->SetError( nError
);
537 return m_aChildrenList
;
540 void SetError( ErrCode nError
);
543 typedef tools::SvRef
<UCBStorage_Impl
> UCBStorage_ImplRef
;
545 // this struct contains all necessary information on an element inside a UCBStorage
546 struct UCBStorageElement_Impl
548 OUString m_aName
; // the actual URL relative to the root "folder"
549 OUString m_aOriginalName
;// the original name in the content
551 bool m_bIsFolder
; // Only true when it is a UCBStorage !
552 bool m_bIsStorage
; // Also true when it is an OLEStorage !
553 bool m_bIsRemoved
; // element will be removed on commit
554 bool m_bIsInserted
; // element will be removed on revert
555 UCBStorage_ImplRef m_xStorage
; // reference to the "real" storage
556 UCBStorageStream_ImplRef m_xStream
; // reference to the "real" stream
558 UCBStorageElement_Impl( const OUString
& rName
,
559 bool bIsFolder
= false, sal_uInt64 nSize
= 0 )
561 , m_aOriginalName( rName
)
563 , m_bIsFolder( bIsFolder
)
564 , m_bIsStorage( bIsFolder
)
565 , m_bIsRemoved( false )
566 , m_bIsInserted( false )
570 ::ucbhelper::Content
* GetContent();
571 bool IsModified() const;
572 const OUString
& GetContentType() const;
573 void SetContentType( const OUString
& );
574 const OUString
& GetOriginalContentType() const;
575 bool IsLoaded() const
576 { return m_xStream
.is() || m_xStorage
.is(); }
579 ::ucbhelper::Content
* UCBStorageElement_Impl::GetContent()
581 if ( m_xStream
.is() )
582 return m_xStream
->m_pContent
;
583 else if ( m_xStorage
.is() )
584 return m_xStorage
->GetContent();
589 const OUString
& UCBStorageElement_Impl::GetContentType() const
591 if ( m_xStream
.is() )
592 return m_xStream
->m_aContentType
;
593 else if ( m_xStorage
.is() )
594 return m_xStorage
->m_aContentType
;
597 OSL_FAIL("Element not loaded!");
598 return EMPTY_OUSTRING
;
602 void UCBStorageElement_Impl::SetContentType( const OUString
& rType
)
604 if ( m_xStream
.is() ) {
605 m_xStream
->m_aContentType
= m_xStream
->m_aOriginalContentType
= rType
;
607 else if ( m_xStorage
.is() ) {
608 m_xStorage
->m_aContentType
= m_xStorage
->m_aOriginalContentType
= rType
;
611 OSL_FAIL("Element not loaded!");
615 const OUString
& UCBStorageElement_Impl::GetOriginalContentType() const
617 if ( m_xStream
.is() )
618 return m_xStream
->m_aOriginalContentType
;
619 else if ( m_xStorage
.is() )
620 return m_xStorage
->m_aOriginalContentType
;
622 return EMPTY_OUSTRING
;
625 bool UCBStorageElement_Impl::IsModified() const
627 bool bModified
= m_bIsRemoved
|| m_bIsInserted
|| m_aName
!= m_aOriginalName
;
630 if ( m_xStream
.is() )
631 bModified
= m_xStream
->m_aContentType
!= m_xStream
->m_aOriginalContentType
;
632 else if ( m_xStorage
.is() )
633 bModified
= m_xStorage
->m_aContentType
!= m_xStorage
->m_aOriginalContentType
;
639 UCBStorageStream_Impl::UCBStorageStream_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorageStream
* pStream
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
640 : m_pAntiImpl( pStream
)
642 , m_pContent( nullptr )
643 , m_nError( ERRCODE_NONE
)
645 , m_bSourceRead( !( nMode
& StreamMode::TRUNC
) )
646 , m_bModified( false )
647 , m_bCommited( false )
648 , m_bDirect( bDirect
)
649 , m_bIsOLEStorage( false )
651 // name is last segment in URL
652 INetURLObject
aObj( rName
);
653 m_aName
= m_aOriginalName
= aObj
.GetLastName();
656 // create the content
657 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
659 OUString
aTemp( rName
);
663 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(), xProgress
);
664 aTemp
+= "?repairpackage";
667 m_pContent
= new ::ucbhelper::Content( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
669 catch (const ContentCreationException
&)
671 // content could not be created
672 SetError( SVSTREAM_CANNOT_MAKE
);
674 catch (const RuntimeException
&)
676 // any other error - not specified
677 SetError( ERRCODE_IO_GENERAL
);
681 UCBStorageStream_Impl::~UCBStorageStream_Impl()
688 if (!m_aTempURL
.isEmpty())
689 osl::File::remove(m_aTempURL
);
695 bool UCBStorageStream_Impl::Init()
699 // no temporary stream was created
702 if ( m_aTempURL
.isEmpty() )
703 m_aTempURL
= ::utl::CreateTempURL();
705 m_pStream
= ::utl::UcbStreamHelper::CreateStream( m_aTempURL
, StreamMode::STD_READWRITE
, true /* bFileExists */ );
706 #if OSL_DEBUG_LEVEL > 0
712 OSL_FAIL( "Suspicious temporary stream creation!" );
713 SetError( SVSTREAM_CANNOT_MAKE
);
717 SetError( m_pStream
->GetError() );
720 if( m_bSourceRead
&& !m_rSource
.is() )
722 // source file contain useful information and is not opened
723 // open it from the point of noncopied data
727 m_rSource
= m_pContent
->openStream();
729 catch (const Exception
&)
731 // usually means that stream could not be opened
736 m_pStream
->Seek( STREAM_SEEK_TO_END
);
740 m_rSource
->skipBytes( m_pStream
->Tell() );
742 catch (const BufferSizeExceededException
&)
744 // the temporary stream already contain all the data
745 m_bSourceRead
= false;
747 catch (const Exception
&)
749 // something is really wrong
750 m_bSourceRead
= false;
751 OSL_FAIL( "Can not operate original stream!" );
752 SetError( SVSTREAM_CANNOT_MAKE
);
755 m_pStream
->Seek( 0 );
759 // if the new file is edited then no source exist
760 m_bSourceRead
= false;
761 //SetError( SVSTREAM_CANNOT_MAKE );
765 DBG_ASSERT( m_rSource
.is() || !m_bSourceRead
, "Unreadable source stream!" );
770 void UCBStorageStream_Impl::ReadSourceWriteTemporary()
772 // read source stream till the end and copy all the data to
773 // the current position of the temporary stream
777 Sequence
<sal_Int8
> aData(32000);
784 aReaded
= m_rSource
->readBytes( aData
, 32000 );
785 m_pStream
->WriteBytes(aData
.getConstArray(), aReaded
);
786 } while( aReaded
== 32000 );
788 catch (const Exception
&)
790 TOOLS_WARN_EXCEPTION("sot", "");
794 m_bSourceRead
= false;
797 sal_uInt64
UCBStorageStream_Impl::ReadSourceWriteTemporary(sal_uInt64 aLength
)
799 // read aLength byte from the source stream and copy them to the current
800 // position of the temporary stream
802 sal_uInt64 aResult
= 0;
806 Sequence
<sal_Int8
> aData(32000);
811 sal_Int32 aReaded
= 32000;
813 for (sal_uInt64 nInd
= 0; nInd
< aLength
&& aReaded
== 32000 ; nInd
+= 32000)
815 sal_Int32 aToCopy
= std::min
<sal_Int32
>( aLength
- nInd
, 32000 );
816 aReaded
= m_rSource
->readBytes( aData
, aToCopy
);
817 aResult
+= m_pStream
->WriteBytes(aData
.getConstArray(), aReaded
);
820 if( aResult
< aLength
)
821 m_bSourceRead
= false;
823 catch( const Exception
& )
825 TOOLS_WARN_EXCEPTION("sot", "");
832 void UCBStorageStream_Impl::CopySourceToTemporary()
834 // current position of the temporary stream is not changed
837 sal_uInt64 aPos
= m_pStream
->Tell();
838 m_pStream
->Seek( STREAM_SEEK_TO_END
);
839 ReadSourceWriteTemporary();
840 m_pStream
->Seek( aPos
);
844 // UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream
845 // of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified
846 std::size_t UCBStorageStream_Impl::GetData(void* pData
, std::size_t const nSize
)
848 std::size_t aResult
= 0;
854 // read data that is in temporary stream
855 aResult
= m_pStream
->ReadBytes( pData
, nSize
);
856 if( m_bSourceRead
&& aResult
< nSize
)
858 // read the tail of the data from original stream
859 // copy this tail to the temporary stream
861 std::size_t aToRead
= nSize
- aResult
;
862 pData
= static_cast<void*>( static_cast<char*>(pData
) + aResult
);
866 Sequence
<sal_Int8
> aData( aToRead
);
867 std::size_t aReaded
= m_rSource
->readBytes( aData
, aToRead
);
868 aResult
+= m_pStream
->WriteBytes(static_cast<const void*>(aData
.getConstArray()), aReaded
);
869 memcpy( pData
, aData
.getArray(), aReaded
);
871 catch (const Exception
&)
873 TOOLS_WARN_EXCEPTION("sot", "");
876 if( aResult
< nSize
)
877 m_bSourceRead
= false;
883 std::size_t UCBStorageStream_Impl::PutData(const void* pData
, std::size_t const nSize
)
885 if ( !(m_nMode
& StreamMode::WRITE
) )
887 SetError( ERRCODE_IO_ACCESSDENIED
);
891 if( !nSize
|| !Init() )
894 std::size_t aResult
= m_pStream
->WriteBytes( pData
, nSize
);
896 m_bModified
= aResult
> 0;
902 sal_uInt64
UCBStorageStream_Impl::SeekPos(sal_uInt64
const nPos
)
904 // check if a truncated STREAM_SEEK_TO_END was passed
905 assert(nPos
!= SAL_MAX_UINT32
);
912 if( nPos
== STREAM_SEEK_TO_END
)
914 m_pStream
->Seek( STREAM_SEEK_TO_END
);
915 ReadSourceWriteTemporary();
916 aResult
= m_pStream
->Tell();
920 // the problem is that even if nPos is larger the length
921 // of the stream, the stream pointer will be moved to this position
922 // so we have to check if temporary stream does not contain required position
924 if( m_pStream
->Tell() > nPos
925 || m_pStream
->Seek( STREAM_SEEK_TO_END
) > nPos
)
927 // no copying is required
928 aResult
= m_pStream
->Seek( nPos
);
932 // the temp stream pointer points to the end now
933 aResult
= m_pStream
->Tell();
939 aResult
+= ReadSourceWriteTemporary( nPos
- aResult
);
941 m_bSourceRead
= false;
943 DBG_ASSERT( aResult
== m_pStream
->Tell(), "Error in stream arithmetic!\n" );
946 if( (m_nMode
& StreamMode::WRITE
) && !m_bSourceRead
&& aResult
< nPos
)
948 // it means that all the Source stream was copied already
949 // but the required position still was not reached
950 // for writable streams it should be done
951 m_pStream
->SetStreamSize( nPos
);
952 aResult
= m_pStream
->Seek( STREAM_SEEK_TO_END
);
953 DBG_ASSERT( aResult
== nPos
, "Error in stream arithmetic!\n" );
962 void UCBStorageStream_Impl::SetSize(sal_uInt64
const nSize
)
964 if ( !(m_nMode
& StreamMode::WRITE
) )
966 SetError( ERRCODE_IO_ACCESSDENIED
);
977 sal_uInt64
const aPos
= m_pStream
->Tell();
978 m_pStream
->Seek( STREAM_SEEK_TO_END
);
979 if( m_pStream
->Tell() < nSize
)
980 ReadSourceWriteTemporary( nSize
- m_pStream
->Tell() );
981 m_pStream
->Seek( aPos
);
984 m_pStream
->SetStreamSize( nSize
);
985 m_bSourceRead
= false;
988 void UCBStorageStream_Impl::FlushData()
992 CopySourceToTemporary();
999 void UCBStorageStream_Impl::SetError( ErrCode nErr
)
1004 SvStream::SetError( nErr
);
1005 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nErr
);
1009 void UCBStorageStream_Impl::ResetError()
1011 m_nError
= ERRCODE_NONE
;
1012 SvStream::ResetError();
1014 m_pAntiImpl
->ResetError();
1017 sal_uInt64
UCBStorageStream_Impl::GetSize()
1022 sal_uInt64 nPos
= m_pStream
->Tell();
1023 m_pStream
->Seek( STREAM_SEEK_TO_END
);
1024 ReadSourceWriteTemporary();
1025 sal_uInt64 nRet
= m_pStream
->Tell();
1026 m_pStream
->Seek( nPos
);
1031 BaseStorage
* UCBStorageStream_Impl::CreateStorage()
1033 // create an OLEStorage on a SvStream ( = this )
1034 // it gets the root attribute because otherwise it would probably not write before my root is committed
1035 UCBStorageStream
* pNewStorageStream
= new UCBStorageStream( this );
1036 Storage
*pStorage
= new Storage( *pNewStorageStream
, m_bDirect
);
1038 // GetError() call clears error code for OLE storages, must be changed in future
1039 const ErrCode nTmpErr
= pStorage
->GetError();
1040 pStorage
->SetError( nTmpErr
);
1042 m_bIsOLEStorage
= !nTmpErr
;
1043 return static_cast< BaseStorage
* > ( pStorage
);
1046 sal_Int16
UCBStorageStream_Impl::Commit()
1048 // send stream to the original content
1049 // the parent storage is responsible for the correct handling of deleted contents
1050 if ( m_bCommited
|| m_bIsOLEStorage
|| m_bDirect
)
1052 // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage
1053 // was committed as well ( if not opened in direct mode )
1059 CopySourceToTemporary();
1061 // release all stream handles
1064 // the temporary file does not exist only for truncated streams
1065 DBG_ASSERT( !m_aTempURL
.isEmpty() || ( m_nMode
& StreamMode::TRUNC
), "No temporary file to read from!");
1066 if ( m_aTempURL
.isEmpty() && !( m_nMode
& StreamMode::TRUNC
) )
1067 throw RuntimeException();
1069 InsertCommandArgument aArg
;
1070 // create wrapper to stream that is only used while reading inside package component
1071 aArg
.Data
.set(new FileStreamWrapper_Impl(m_aTempURL
));
1072 aArg
.ReplaceExisting
= true;
1073 m_pContent
->executeCommand( u
"insert"_ustr
, Any(aArg
) );
1075 // wrapper now controls lifetime of temporary file
1078 INetURLObject
aObj( m_aURL
);
1079 aObj
.setName( m_aName
);
1080 m_aURL
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1081 m_bModified
= false;
1082 m_bSourceRead
= true;
1084 catch (const CommandAbortedException
&)
1086 // any command wasn't executed successfully - not specified
1087 SetError( ERRCODE_IO_GENERAL
);
1088 return COMMIT_RESULT_FAILURE
;
1090 catch (const RuntimeException
&)
1092 // any other error - not specified
1093 SetError( ERRCODE_IO_GENERAL
);
1094 return COMMIT_RESULT_FAILURE
;
1096 catch (const Exception
&)
1098 // any other error - not specified
1099 SetError( ERRCODE_IO_GENERAL
);
1100 return COMMIT_RESULT_FAILURE
;
1103 m_bCommited
= false;
1104 return COMMIT_RESULT_SUCCESS
;
1108 return COMMIT_RESULT_NOTHING_TO_DO
;
1111 void UCBStorageStream_Impl::Revert()
1113 // if an OLEStorage is created on this stream, no "revert" is necessary because OLEStorages do nothing on "Revert" !
1116 OSL_FAIL("Revert while commit is in progress!" );
1121 if ( !m_aTempURL
.isEmpty() )
1123 osl::File::remove(m_aTempURL
);
1127 m_bSourceRead
= false;
1130 m_rSource
= m_pContent
->openStream();
1131 if( m_rSource
.is() )
1133 if ( m_pAntiImpl
&& ( m_nMode
& StreamMode::TRUNC
) )
1134 // stream is in use and should be truncated
1135 m_bSourceRead
= false;
1138 m_nMode
&= ~StreamMode::TRUNC
;
1139 m_bSourceRead
= true;
1143 SetError( SVSTREAM_CANNOT_MAKE
);
1145 catch (const ContentCreationException
&)
1147 SetError( ERRCODE_IO_GENERAL
);
1149 catch (const RuntimeException
&)
1151 SetError( ERRCODE_IO_GENERAL
);
1153 catch (const Exception
&)
1157 m_bModified
= false;
1158 m_aName
= m_aOriginalName
;
1159 m_aContentType
= m_aOriginalContentType
;
1162 bool UCBStorageStream_Impl::Clear()
1164 bool bRet
= ( m_pAntiImpl
== nullptr );
1165 DBG_ASSERT( bRet
, "Removing used stream!" );
1174 void UCBStorageStream_Impl::Free()
1176 #if OSL_DEBUG_LEVEL > 0
1179 if ( !m_aTempURL
.isEmpty() )
1190 void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode
)
1192 bool isWritable
= bool( m_nMode
& StreamMode::WRITE
);
1195 // once stream was writable, never reset to readonly
1196 nMode
|= StreamMode::WRITE
;
1202 if ( nMode
& StreamMode::TRUNC
)
1204 m_bSourceRead
= false; // usually it should be 0 already but just in case...
1206 if ( !m_aTempURL
.isEmpty() )
1208 osl::File::remove(m_aTempURL
);
1214 UCBStorageStream::UCBStorageStream( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
1216 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1217 // to class UCBStorageStream !
1218 pImp
= new UCBStorageStream_Impl( rName
, nMode
, this, bDirect
, bRepair
, xProgress
);
1219 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1220 StorageBase::m_nMode
= pImp
->m_nMode
;
1223 UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl
*pImpl
)
1226 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1227 pImp
->m_pAntiImpl
= this;
1228 SetError( pImp
->m_nError
);
1229 StorageBase::m_nMode
= pImp
->m_nMode
;
1232 UCBStorageStream::~UCBStorageStream()
1234 if ( pImp
->m_nMode
& StreamMode::WRITE
)
1236 pImp
->m_pAntiImpl
= nullptr;
1241 sal_Int32
UCBStorageStream::Read( void * pData
, sal_Int32 nSize
)
1243 //return pImp->m_pStream->Read( pData, nSize );
1244 return pImp
->GetData( pData
, nSize
);
1247 sal_Int32
UCBStorageStream::Write( const void* pData
, sal_Int32 nSize
)
1249 return pImp
->PutData( pData
, nSize
);
1252 sal_uInt64
UCBStorageStream::Seek( sal_uInt64 nPos
)
1254 //return pImp->m_pStream->Seek( nPos );
1255 return pImp
->Seek( nPos
);
1258 sal_uInt64
UCBStorageStream::Tell()
1262 return pImp
->m_pStream
->Tell();
1265 void UCBStorageStream::Flush()
1267 // streams are never really transacted, so flush also means commit !
1271 bool UCBStorageStream::SetSize( sal_uInt64 nNewSize
)
1273 pImp
->SetSize( nNewSize
);
1274 return !pImp
->GetError();
1277 bool UCBStorageStream::Validate( bool bWrite
) const
1279 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
1282 bool UCBStorageStream::ValidateMode( StreamMode m
) const
1285 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
1287 if( ( m
& StreamMode::READWRITE
) == StreamMode::READ
)
1289 // only SHARE_DENYWRITE or SHARE_DENYALL allowed
1290 if( ( m
& StreamMode::SHARE_DENYWRITE
)
1291 || ( m
& StreamMode::SHARE_DENYALL
) )
1296 // only SHARE_DENYALL allowed
1297 // storages open in r/o mode are OK, since only
1298 // the commit may fail
1299 if( m
& StreamMode::SHARE_DENYALL
)
1306 SvStream
* UCBStorageStream::GetModifySvStream()
1308 return static_cast<SvStream
*>(pImp
);
1311 bool UCBStorageStream::Equals( const BaseStorageStream
& rStream
) const
1314 return static_cast<BaseStorageStream
const *>(this) == &rStream
;
1317 bool UCBStorageStream::Commit()
1319 // mark this stream for sending it on root commit
1324 void UCBStorageStream::CopyTo( BaseStorageStream
* pDestStm
)
1329 UCBStorageStream
* pStg
= dynamic_cast<UCBStorageStream
*>( pDestStm
);
1331 pStg
->pImp
->m_aContentType
= pImp
->m_aContentType
;
1333 pDestStm
->SetSize( 0 );
1334 Seek( STREAM_SEEK_TO_END
);
1335 sal_Int32 n
= Tell();
1339 if( !pDestStm
->SetSize( n
) || !n
)
1342 std::unique_ptr
<sal_uInt8
[]> p(new sal_uInt8
[ 4096 ]);
1344 pDestStm
->Seek( 0 );
1350 if( Read( p
.get(), nn
) != nn
)
1352 if( pDestStm
->Write( p
.get(), nn
) != nn
)
1358 bool UCBStorageStream::SetProperty( const OUString
& rName
, const css::uno::Any
& rValue
)
1360 if ( rName
== "Title")
1363 if ( rName
== "MediaType")
1367 pImp
->m_aContentType
= aTmp
;
1372 if ( pImp
->m_pContent
)
1374 pImp
->m_pContent
->setPropertyValue( rName
, rValue
);
1378 catch (const Exception
&)
1385 sal_uInt64
UCBStorageStream::GetSize() const
1387 return pImp
->GetSize();
1390 UCBStorage::UCBStorage( SvStream
& rStrm
, bool bDirect
)
1392 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1393 // to class UCBStorage !
1394 pImp
= new UCBStorage_Impl( rStrm
, this, bDirect
);
1396 pImp
->AddFirstRef();
1398 StorageBase::m_nMode
= pImp
->m_nMode
;
1401 UCBStorage::UCBStorage( const ::ucbhelper::Content
& rContent
, const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1403 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1404 // to class UCBStorage !
1405 pImp
= new UCBStorage_Impl( rContent
, rName
, nMode
, this, bDirect
, bIsRoot
);
1406 pImp
->AddFirstRef();
1408 StorageBase::m_nMode
= pImp
->m_nMode
;
1411 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1413 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1414 // to class UCBStorage !
1415 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, bIsRepair
, xProgressHandler
);
1416 pImp
->AddFirstRef();
1418 StorageBase::m_nMode
= pImp
->m_nMode
;
1421 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1423 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1424 // to class UCBStorage !
1425 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, false, Reference
< XProgressHandler
>() );
1426 pImp
->AddFirstRef();
1428 StorageBase::m_nMode
= pImp
->m_nMode
;
1431 UCBStorage::UCBStorage( UCBStorage_Impl
*pImpl
)
1434 pImp
->m_pAntiImpl
= this;
1435 SetError( pImp
->m_nError
);
1436 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1437 StorageBase::m_nMode
= pImp
->m_nMode
;
1440 UCBStorage::~UCBStorage()
1442 if ( pImp
->m_bIsRoot
&& pImp
->m_bDirect
&& ( !pImp
->m_pTempFile
|| pImp
->m_pSource
) )
1443 // DirectMode is simulated with an AutoCommit
1446 pImp
->m_pAntiImpl
= nullptr;
1450 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
)
1451 : m_pAntiImpl( pStorage
)
1452 , m_oContent( rContent
)
1453 , m_pSource( nullptr )
1454 //, m_pStream( NULL )
1455 , m_nError( ERRCODE_NONE
)
1457 , m_bCommited( false )
1458 , m_bDirect( bDirect
)
1459 , m_bIsRoot( bIsRoot
)
1460 , m_bIsLinked( true )
1461 , m_bListCreated( false )
1462 , m_nFormat( SotClipboardFormatId::NONE
)
1463 , m_bRepairPackage( bIsRepair
)
1464 , m_xProgressHandler( xProgressHandler
)
1466 OUString
aName( rName
);
1467 if( aName
.isEmpty() )
1469 // no name given = use temporary name!
1470 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1471 m_pTempFile
.reset(new ::utl::TempFileNamed
);
1472 m_pTempFile
->EnableKillingFile();
1473 m_aName
= aName
= m_pTempFile
->GetURL();
1479 UCBStorage_Impl::UCBStorage_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorage
* pStorage
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1480 : m_pAntiImpl( pStorage
)
1481 , m_pSource( nullptr )
1482 //, m_pStream( NULL )
1483 , m_nError( ERRCODE_NONE
)
1485 , m_bCommited( false )
1486 , m_bDirect( bDirect
)
1487 , m_bIsRoot( bIsRoot
)
1488 , m_bIsLinked( false )
1489 , m_bListCreated( false )
1490 , m_nFormat( SotClipboardFormatId::NONE
)
1491 , m_bRepairPackage( bIsRepair
)
1492 , m_xProgressHandler( xProgressHandler
)
1494 OUString
aName( rName
);
1495 if( aName
.isEmpty() )
1497 // no name given = use temporary name!
1498 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1499 m_pTempFile
.reset(new ::utl::TempFileNamed
);
1500 m_pTempFile
->EnableKillingFile();
1501 m_aName
= aName
= m_pTempFile
->GetURL();
1506 // create the special package URL for the package content
1507 m_aURL
= "vnd.sun.star.pkg://" +
1508 INetURLObject::encode( aName
, INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1510 if ( m_nMode
& StreamMode::WRITE
)
1512 // the root storage opens the package, so make sure that there is any
1513 ::utl::UcbStreamHelper::CreateStream( aName
, StreamMode::STD_READWRITE
, m_pTempFile
!= nullptr /* bFileExists */ );
1518 // substorages are opened like streams: the URL is a "child URL" of the root package URL
1520 if ( !m_aURL
.startsWith( "vnd.sun.star.pkg://") )
1525 UCBStorage_Impl::UCBStorage_Impl( SvStream
& rStream
, UCBStorage
* pStorage
, bool bDirect
)
1526 : m_pAntiImpl( pStorage
)
1527 , m_pTempFile( new ::utl::TempFileNamed
)
1528 , m_pSource( &rStream
)
1529 , m_nError( ERRCODE_NONE
)
1530 , m_bCommited( false )
1531 , m_bDirect( bDirect
)
1533 , m_bIsLinked( false )
1534 , m_bListCreated( false )
1535 , m_nFormat( SotClipboardFormatId::NONE
)
1536 , m_bRepairPackage( false )
1538 // opening in direct mode is too fuzzy because the data is transferred to the stream in the Commit() call,
1539 // which will be called in the storages' dtor
1540 m_pTempFile
->EnableKillingFile();
1541 DBG_ASSERT( !bDirect
, "Storage on a stream must not be opened in direct mode!" );
1543 // UCBStorages work on a content, so a temporary file for a content must be created, even if the stream is only
1544 // accessed readonly
1545 // the root storage opens the package; create the special package URL for the package content
1546 m_aURL
= "vnd.sun.star.pkg://" +
1547 INetURLObject::encode( m_pTempFile
->GetURL(), INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1549 // copy data into the temporary file
1550 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READWRITE
, true /* bFileExists */ ));
1554 rStream
.ReadStream( *pStream
);
1559 // close stream and let content access the file
1562 // check opening mode
1563 m_nMode
= StreamMode::READ
;
1564 if( rStream
.IsWritable() )
1565 m_nMode
= StreamMode::READ
| StreamMode::WRITE
;
1568 void UCBStorage_Impl::Init()
1570 // name is last segment in URL
1571 INetURLObject
aObj( m_aURL
);
1572 if ( m_aName
.isEmpty() )
1573 // if the name was not already set to a temp name
1574 m_aName
= aObj
.GetLastName();
1586 if ( m_nError
== ERRCODE_NONE
)
1588 // read the manifest.xml file
1589 aObj
.Append( u
"META-INF" );
1590 aObj
.Append( u
"manifest.xml" );
1592 // create input stream
1593 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::STD_READ
));
1594 // no stream means no manifest.xml
1597 if ( !pStream
->GetError() )
1599 rtl::Reference
<::utl::OInputStreamWrapper
> pHelper
= new ::utl::OInputStreamWrapper( *pStream
);
1601 // create a manifest reader object that will read in the manifest from the stream
1602 Reference
< css::packages::manifest::XManifestReader
> xReader
=
1603 css::packages::manifest::ManifestReader::create(
1604 ::comphelper::getProcessComponentContext() ) ;
1605 Sequence
< Sequence
< PropertyValue
> > aProps
= xReader
->readManifestSequence( pHelper
);
1610 SetProps( aProps
, OUString() );
1620 // get the manifest information from the package
1622 Any aAny
= m_oContent
->getPropertyValue(u
"MediaType"_ustr
);
1624 if ( ( aAny
>>= aTmp
) && !aTmp
.isEmpty() )
1625 m_aContentType
= m_aOriginalContentType
= aTmp
;
1627 catch (const Exception
&)
1630 "getPropertyValue has thrown an exception! Please let developers know the scenario!" );
1635 if ( m_aContentType
.isEmpty() )
1638 // get the clipboard format using the content type
1639 css::datatransfer::DataFlavor aDataFlavor
;
1640 aDataFlavor
.MimeType
= m_aContentType
;
1641 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1643 // get the ClassId using the clipboard format ( internal table )
1644 m_aClassId
= GetClassId_Impl( m_nFormat
);
1646 // get human presentable name using the clipboard format
1647 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1648 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1650 if( m_oContent
&& !m_bIsLinked
&& m_aClassId
!= SvGlobalName() )
1654 void UCBStorage_Impl::CreateContent()
1658 // create content; where to put StreamMode ?! ( already done when opening the file of the package ? )
1659 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1661 OUString
aTemp( m_aURL
);
1663 if ( m_bRepairPackage
)
1665 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1666 m_xProgressHandler
);
1667 aTemp
+= "?repairpackage";
1670 m_oContent
.emplace( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
1672 catch (const ContentCreationException
&)
1674 // content could not be created
1675 SetError( SVSTREAM_CANNOT_MAKE
);
1677 catch (const RuntimeException
&)
1679 // any other error - not specified
1680 SetError( SVSTREAM_CANNOT_MAKE
);
1684 void UCBStorage_Impl::ReadContent()
1686 if ( m_bListCreated
)
1689 m_bListCreated
= true;
1697 // create cursor for access to children
1698 Reference
< XResultSet
> xResultSet
= m_oContent
->createCursor( { u
"Title"_ustr
, u
"IsFolder"_ustr
, u
"MediaType"_ustr
, u
"Size"_ustr
}, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS
);
1699 Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
1700 if ( xResultSet
.is() )
1702 while ( xResultSet
->next() )
1704 // insert all into the children list
1705 OUString
aTitle( xRow
->getString(1) );
1708 // unpacked storages have to deal with the meta-inf folder by themselves
1709 if ( aTitle
== "META-INF" )
1713 bool bIsFolder( xRow
->getBoolean(2) );
1714 sal_Int64 nSize
= xRow
->getLong(4);
1715 UCBStorageElement_Impl
* pElement
= new UCBStorageElement_Impl( aTitle
, bIsFolder
, nSize
);
1716 m_aChildrenList
.emplace_back( pElement
);
1718 bool bIsOfficeDocument
= m_bIsLinked
|| ( m_aClassId
!= SvGlobalName() );
1722 OpenStorage( pElement
, m_nMode
, m_bDirect
);
1723 if ( pElement
->m_xStorage
.is() )
1724 pElement
->m_xStorage
->Init();
1726 else if ( bIsOfficeDocument
)
1728 // streams can be external OLE objects, so they are now folders, but storages!
1729 OUString
aName( m_aURL
+ "/" + xRow
->getString(1));
1731 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1732 if ( m_bRepairPackage
)
1734 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1735 m_xProgressHandler
);
1736 aName
+= "?repairpackage";
1739 ::ucbhelper::Content
aContent( aName
, xComEnv
, comphelper::getProcessComponentContext() );
1741 OUString aMediaType
;
1742 Any aAny
= aContent
.getPropertyValue(u
"MediaType"_ustr
);
1743 if ( ( aAny
>>= aMediaType
) && ( aMediaType
== "application/vnd.sun.star.oleobject" ) )
1744 pElement
->m_bIsStorage
= true;
1745 else if ( aMediaType
.isEmpty() )
1747 // older files didn't have that special content type, so they must be detected
1748 OpenStream( pElement
, StreamMode::STD_READ
, m_bDirect
);
1749 if ( Storage::IsStorageFile( pElement
->m_xStream
.get() ) )
1750 pElement
->m_bIsStorage
= true;
1752 pElement
->m_xStream
->Free();
1758 catch (const InteractiveIOException
& r
)
1760 if ( r
.Code
!= IOErrorCode_NOT_EXISTING
)
1761 SetError( ERRCODE_IO_GENERAL
);
1763 catch (const CommandAbortedException
&)
1765 // any command wasn't executed successfully - not specified
1766 if ( !( m_nMode
& StreamMode::WRITE
) )
1767 // if the folder was just inserted and not already committed, this is not an error!
1768 SetError( ERRCODE_IO_GENERAL
);
1770 catch (const RuntimeException
&)
1772 // any other error - not specified
1773 SetError( ERRCODE_IO_GENERAL
);
1775 catch (const ResultSetException
&)
1777 // means that the package file is broken
1778 SetError( ERRCODE_IO_BROKENPACKAGE
);
1780 catch (const SQLException
&)
1782 // means that the file can be broken
1783 SetError( ERRCODE_IO_WRONGFORMAT
);
1785 catch (const Exception
&)
1787 // any other error - not specified
1788 SetError( ERRCODE_IO_GENERAL
);
1792 void UCBStorage_Impl::SetError( ErrCode nError
)
1797 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nError
);
1801 sal_Int32
UCBStorage_Impl::GetObjectCount()
1803 sal_Int32 nCount
= m_aChildrenList
.size();
1804 for (auto& pElement
: m_aChildrenList
)
1806 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1807 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1808 nCount
+= pElement
->m_xStorage
->GetObjectCount();
1814 static OUString
Find_Impl( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, std::u16string_view rPath
)
1816 bool bFound
= false;
1817 for ( const Sequence
< PropertyValue
>& rMyProps
: rSequence
)
1821 for ( const PropertyValue
& rAny
: rMyProps
)
1823 if ( rAny
.Name
== "FullPath" )
1826 if ( ( rAny
.Value
>>= aTmp
) && aTmp
== rPath
)
1828 if ( !aType
.isEmpty() )
1831 else if ( rAny
.Name
== "MediaType" )
1833 if ( ( rAny
.Value
>>= aType
) && !aType
.isEmpty() && bFound
)
1845 void UCBStorage_Impl::SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1847 OUString
aPath( rPath
);
1852 m_aContentType
= m_aOriginalContentType
= Find_Impl( rSequence
, aPath
);
1855 // the "FullPath" of a child always starts without '/'
1858 for (auto& pElement
: m_aChildrenList
)
1860 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1861 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1862 pElement
->m_xStorage
->SetProps( rSequence
, aPath
);
1865 OUString aElementPath
= aPath
+ pElement
->m_aName
;
1866 pElement
->SetContentType( Find_Impl( rSequence
, aElementPath
) );
1870 if ( m_aContentType
.isEmpty() )
1873 // get the clipboard format using the content type
1874 css::datatransfer::DataFlavor aDataFlavor
;
1875 aDataFlavor
.MimeType
= m_aContentType
;
1876 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1878 // get the ClassId using the clipboard format ( internal table )
1879 m_aClassId
= GetClassId_Impl( m_nFormat
);
1881 // get human presentable name using the clipboard format
1882 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1883 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1886 void UCBStorage_Impl::GetProps( sal_Int32
& nProps
, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1888 auto pSequence
= rSequence
.getArray();
1890 // first my own properties
1891 // first property is the "FullPath" name
1892 // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder
1893 OUString
aPath( rPath
);
1897 Sequence
< PropertyValue
> aProps
{ comphelper::makePropertyValue(u
"MediaType"_ustr
, m_aContentType
),
1898 comphelper::makePropertyValue(u
"FullPath"_ustr
, aPath
) };
1899 pSequence
[nProps
++] = aProps
;
1902 // the "FullPath" of a child always starts without '/'
1905 // now the properties of my elements
1906 for (auto& pElement
: m_aChildrenList
)
1908 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1909 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1910 // storages add there properties by themselves ( see above )
1911 pElement
->m_xStorage
->GetProps( nProps
, rSequence
, aPath
);
1914 // properties of streams
1915 OUString aElementPath
= aPath
+ pElement
->m_aName
;
1916 aProps
= { comphelper::makePropertyValue(u
"MediaType"_ustr
, pElement
->GetContentType()),
1917 comphelper::makePropertyValue(u
"FullPath"_ustr
, aElementPath
) };
1918 pSequence
[ nProps
++ ] = aProps
;
1923 UCBStorage_Impl::~UCBStorage_Impl()
1925 m_aChildrenList
.clear();
1928 m_pTempFile
.reset();
1931 bool UCBStorage_Impl::Insert( ::ucbhelper::Content
*pContent
)
1933 // a new substorage is inserted into a UCBStorage ( given by the parameter pContent )
1934 // it must be inserted with a title and a type
1939 const Sequence
< ContentInfo
> aInfo
= pContent
->queryCreatableContentsInfo();
1940 if ( !aInfo
.hasElements() )
1943 for ( const ContentInfo
& rCurr
: aInfo
)
1945 // Simply look for the first KIND_FOLDER...
1946 if ( rCurr
.Attributes
& ContentInfoAttribute::KIND_FOLDER
)
1948 // Make sure the only required bootstrap property is "Title",
1949 const Sequence
< Property
> & rProps
= rCurr
.Properties
;
1950 if ( rProps
.getLength() != 1 )
1953 if ( rProps
[ 0 ].Name
!= "Title" )
1957 if ( !pContent
->insertNewContent( rCurr
.Type
, { u
"Title"_ustr
}, { Any(m_aName
) }, aNewFolder
) )
1960 // remove old content, create an "empty" new one and initialize it with the new inserted
1961 m_oContent
.emplace( aNewFolder
);
1966 catch (const CommandAbortedException
&)
1968 // any command wasn't executed successfully - not specified
1969 SetError( ERRCODE_IO_GENERAL
);
1971 catch (const RuntimeException
&)
1973 // any other error - not specified
1974 SetError( ERRCODE_IO_GENERAL
);
1976 catch (const Exception
&)
1978 // any other error - not specified
1979 SetError( ERRCODE_IO_GENERAL
);
1985 sal_Int16
UCBStorage_Impl::Commit()
1987 // send all changes to the package
1988 sal_Int16 nRet
= COMMIT_RESULT_NOTHING_TO_DO
;
1990 // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no
1991 // commit command has been sent
1992 if ( ( m_nMode
& StreamMode::WRITE
) && ( m_bCommited
|| m_bDirect
) )
1996 // all errors will be caught in the "catch" statement outside the loop
1997 for ( size_t i
= 0; i
< m_aChildrenList
.size() && nRet
; ++i
)
1999 auto& pElement
= m_aChildrenList
[ i
];
2000 ::ucbhelper::Content
* pContent
= pElement
->GetContent();
2001 std::unique_ptr
< ::ucbhelper::Content
> xDeleteContent
;
2002 if ( !pContent
&& pElement
->IsModified() )
2004 // if the element has never been opened, no content has been created until now
2005 OUString aName
= m_aURL
+ "/" + pElement
->m_aOriginalName
;
2006 pContent
= new ::ucbhelper::Content( aName
, Reference
< css::ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2007 xDeleteContent
.reset(pContent
); // delete it later on exit scope
2010 if ( pElement
->m_bIsRemoved
)
2012 // was it inserted, then removed (so there would be nothing to do!)
2013 if ( !pElement
->m_bIsInserted
)
2015 // first remove all open stream handles
2016 if (pContent
&& (!pElement
->m_xStream
.is() || pElement
->m_xStream
->Clear()))
2018 pContent
->executeCommand( u
"delete"_ustr
, Any( true ) );
2019 nRet
= COMMIT_RESULT_SUCCESS
;
2022 // couldn't release stream because there are external references to it
2023 nRet
= COMMIT_RESULT_FAILURE
;
2028 sal_Int16 nLocalRet
= COMMIT_RESULT_NOTHING_TO_DO
;
2029 if ( pElement
->m_xStorage
.is() )
2031 // element is a storage
2032 // do a commit in the following cases:
2033 // - if storage is already inserted, and changed
2034 // - storage is not in a package
2035 // - it's a new storage, try to insert and commit if successful inserted
2036 if ( !pElement
->m_bIsInserted
|| m_bIsLinked
2037 || pElement
->m_xStorage
->Insert( m_oContent
? &*m_oContent
: nullptr ) )
2039 nLocalRet
= pElement
->m_xStorage
->Commit();
2040 pContent
= pElement
->GetContent();
2043 else if ( pElement
->m_xStream
.is() )
2045 // element is a stream
2046 nLocalRet
= pElement
->m_xStream
->Commit();
2047 if ( pElement
->m_xStream
->m_bIsOLEStorage
)
2049 // OLE storage should be stored encrypted, if the storage uses encryption
2050 pElement
->m_xStream
->m_aContentType
= "application/vnd.sun.star.oleobject";
2053 pElement
->m_xStream
->m_pContent
->setPropertyValue(u
"Encrypted"_ustr
, aValue
);
2056 pContent
= pElement
->GetContent();
2059 if (pContent
&& pElement
->m_aName
!= pElement
->m_aOriginalName
)
2061 // name ( title ) of the element was changed
2062 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2063 pContent
->setPropertyValue(u
"Title"_ustr
, Any(pElement
->m_aName
) );
2066 if (pContent
&& pElement
->IsLoaded() && pElement
->GetContentType() != pElement
->GetOriginalContentType())
2068 // mediatype of the element was changed
2069 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2070 pContent
->setPropertyValue(u
"MediaType"_ustr
, Any(pElement
->GetContentType()) );
2073 if ( nLocalRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2077 if ( nRet
== COMMIT_RESULT_FAILURE
)
2081 catch (const ContentCreationException
&)
2083 // content could not be created
2084 SetError( ERRCODE_IO_NOTEXISTS
);
2085 return COMMIT_RESULT_FAILURE
;
2087 catch (const CommandAbortedException
&)
2089 // any command wasn't executed successfully - not specified
2090 SetError( ERRCODE_IO_GENERAL
);
2091 return COMMIT_RESULT_FAILURE
;
2093 catch (const RuntimeException
&)
2095 // any other error - not specified
2096 SetError( ERRCODE_IO_GENERAL
);
2097 return COMMIT_RESULT_FAILURE
;
2099 catch (const Exception
&)
2101 // any other error - not specified
2102 SetError( ERRCODE_IO_GENERAL
);
2103 return COMMIT_RESULT_FAILURE
;
2106 if ( m_bIsRoot
&& m_oContent
)
2108 // the root storage must flush the root package content
2109 if ( nRet
== COMMIT_RESULT_SUCCESS
)
2113 // commit the media type to the JAR file
2114 // clipboard format and ClassId will be retrieved from the media type when the file is loaded again
2116 aType
<<= m_aContentType
;
2117 m_oContent
->setPropertyValue(u
"MediaType"_ustr
, aType
);
2121 // write a manifest file
2122 // first create a subfolder "META-inf"
2123 Content aNewSubFolder
;
2124 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_oContent
, u
"META-INF"_ustr
, aNewSubFolder
);
2127 // create a stream to write the manifest file - use a temp file
2128 OUString
aURL( aNewSubFolder
.getURL() );
2129 std::optional
< ::utl::TempFileNamed
> pTempFile(&aURL
);
2131 // get the stream from the temp file and create an output stream wrapper
2132 SvStream
* pStream
= pTempFile
->GetStream( StreamMode::STD_READWRITE
);
2133 rtl::Reference
<::utl::OOutputStreamWrapper
> xOutputStream
= new ::utl::OOutputStreamWrapper( *pStream
);
2135 // create a manifest writer object that will fill the stream
2136 Reference
< css::packages::manifest::XManifestWriter
> xWriter
=
2137 css::packages::manifest::ManifestWriter::create(
2138 ::comphelper::getProcessComponentContext() );
2139 sal_Int32 nCount
= GetObjectCount() + 1;
2140 Sequence
< Sequence
< PropertyValue
> > aProps( nCount
);
2141 sal_Int32 nProps
= 0;
2142 GetProps( nProps
, aProps
, OUString() );
2143 xWriter
->writeManifestSequence( xOutputStream
, aProps
);
2145 // move the stream to its desired location
2146 Content
aSource( pTempFile
->GetURL(), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2148 xOutputStream
= nullptr;
2150 aNewSubFolder
.transferContent( aSource
, InsertOperation::Move
, u
"manifest.xml"_ustr
, NameClash::OVERWRITE
);
2155 #if OSL_DEBUG_LEVEL > 0
2156 SAL_INFO("sot", "Files: " << nOpenFiles
);
2157 SAL_INFO("sot", "Streams: " << nOpenStreams
);
2161 m_oContent
->executeCommand( u
"flush"_ustr
, aAny
);
2162 if ( m_pSource
!= nullptr )
2164 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READ
));
2165 m_pSource
->SetStreamSize(0);
2166 // m_pSource->Seek(0);
2167 pStream
->ReadStream( *m_pSource
);
2173 catch (const CommandAbortedException
&)
2175 // how to tell the content : forget all changes ?!
2176 // or should we assume that the content does it by itself because he threw an exception ?!
2177 // any command wasn't executed successfully - not specified
2178 SetError( ERRCODE_IO_GENERAL
);
2179 return COMMIT_RESULT_FAILURE
;
2181 catch (const RuntimeException
&)
2183 // how to tell the content : forget all changes ?!
2184 // or should we assume that the content does it by itself because he threw an exception ?!
2185 // any other error - not specified
2186 SetError( ERRCODE_IO_GENERAL
);
2187 return COMMIT_RESULT_FAILURE
;
2189 catch (const InteractiveIOException
& r
)
2191 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
|| r
.Code
== IOErrorCode_LOCKING_VIOLATION
)
2192 SetError( ERRCODE_IO_ACCESSDENIED
);
2193 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
2194 SetError( ERRCODE_IO_NOTEXISTS
);
2195 else if ( r
.Code
== IOErrorCode_CANT_READ
)
2196 SetError( ERRCODE_IO_CANTREAD
);
2197 else if ( r
.Code
== IOErrorCode_CANT_WRITE
)
2198 SetError( ERRCODE_IO_CANTWRITE
);
2200 SetError( ERRCODE_IO_GENERAL
);
2202 return COMMIT_RESULT_FAILURE
;
2204 catch (const Exception
&)
2206 // how to tell the content : forget all changes ?!
2207 // or should we assume that the content does it by itself because he threw an exception ?!
2208 // any other error - not specified
2209 SetError( ERRCODE_IO_GENERAL
);
2210 return COMMIT_RESULT_FAILURE
;
2213 else if ( nRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2215 // how to tell the content : forget all changes ?! Should we ?!
2216 SetError( ERRCODE_IO_GENERAL
);
2220 // after successful root commit all elements names and types are adjusted and all removed elements
2221 // are also removed from the lists
2222 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2224 auto& pInnerElement
= m_aChildrenList
[ i
];
2225 if ( pInnerElement
->m_bIsRemoved
)
2226 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2229 pInnerElement
->m_aOriginalName
= pInnerElement
->m_aName
;
2230 pInnerElement
->m_bIsInserted
= false;
2236 m_bCommited
= false;
2242 void UCBStorage_Impl::Revert()
2244 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2246 auto& pElement
= m_aChildrenList
[ i
];
2247 pElement
->m_bIsRemoved
= false;
2248 if ( pElement
->m_bIsInserted
)
2249 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2252 if ( pElement
->m_xStream
.is() )
2254 pElement
->m_xStream
->m_bCommited
= false;
2255 pElement
->m_xStream
->Revert();
2257 else if ( pElement
->m_xStorage
.is() )
2259 pElement
->m_xStorage
->m_bCommited
= false;
2260 pElement
->m_xStorage
->Revert();
2263 pElement
->m_aName
= pElement
->m_aOriginalName
;
2264 pElement
->m_bIsRemoved
= false;
2270 const OUString
& UCBStorage::GetName() const
2272 return pImp
->m_aName
; // pImp->m_aURL ?!
2275 bool UCBStorage::IsRoot() const
2277 return pImp
->m_bIsRoot
;
2280 void UCBStorage::SetDirty()
2284 void UCBStorage::SetClass( const SvGlobalName
& rClass
, SotClipboardFormatId nOriginalClipFormat
, const OUString
& rUserTypeName
)
2286 pImp
->m_aClassId
= rClass
;
2287 pImp
->m_nFormat
= nOriginalClipFormat
;
2288 pImp
->m_aUserTypeName
= rUserTypeName
;
2290 // in UCB storages only the content type will be stored, all other information can be reconstructed
2291 // ( see the UCBStorage_Impl::Init() method )
2292 css::datatransfer::DataFlavor aDataFlavor
;
2293 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2294 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2297 void UCBStorage::SetClassId( const ClsId
& rClsId
)
2299 pImp
->m_aClassId
= SvGlobalName( rClsId
);
2300 if ( pImp
->m_aClassId
== SvGlobalName() )
2303 // in OLE storages the clipboard format and the user name will be transferred when a storage is copied because both are
2304 // stored in one the substreams
2305 // UCB storages store the content type information as content type in the manifest file and so this information must be
2306 // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from
2308 pImp
->m_nFormat
= GetFormatId_Impl( pImp
->m_aClassId
);
2309 if ( pImp
->m_nFormat
!= SotClipboardFormatId::NONE
)
2311 css::datatransfer::DataFlavor aDataFlavor
;
2312 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2313 pImp
->m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
2314 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2318 const ClsId
& UCBStorage::GetClassId() const
2320 return pImp
->m_aClassId
.GetCLSID();
2323 SvGlobalName
UCBStorage::GetClassName()
2325 return pImp
->m_aClassId
;
2328 SotClipboardFormatId
UCBStorage::GetFormat()
2330 return pImp
->m_nFormat
;
2333 OUString
UCBStorage::GetUserName()
2335 OSL_FAIL("UserName is not implemented in UCB storages!" );
2336 return pImp
->m_aUserTypeName
;
2339 void UCBStorage::FillInfoList( SvStorageInfoList
* pList
) const
2341 // put information in childrenlist into StorageInfoList
2342 for (auto& pElement
: pImp
->GetChildrenList())
2344 if ( !pElement
->m_bIsRemoved
)
2346 // problem: what about the size of a substorage ?!
2347 sal_uInt64 nSize
= pElement
->m_nSize
;
2348 if ( pElement
->m_xStream
.is() )
2349 nSize
= pElement
->m_xStream
->GetSize();
2350 SvStorageInfo
aInfo( pElement
->m_aName
, nSize
, pElement
->m_bIsStorage
);
2351 pList
->push_back( aInfo
);
2356 bool UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl
const & rElement
, BaseStorage
* pDest
, const OUString
& rNew
) const
2358 // insert stream or storage into the list or stream of the destination storage
2359 // not into the content, this will be done on commit !
2360 // be aware of name changes !
2361 if ( !rElement
.m_bIsStorage
)
2363 // copy the streams data
2364 // the destination stream must not be open
2365 tools::SvRef
<BaseStorageStream
> pOtherStream(pDest
->OpenStream( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2366 BaseStorageStream
* pStream
= nullptr;
2367 bool bDeleteStream
= false;
2369 // if stream is already open, it is allowed to copy it, so be aware of this
2370 if ( rElement
.m_xStream
.is() )
2371 pStream
= rElement
.m_xStream
->m_pAntiImpl
;
2374 pStream
= const_cast< UCBStorage
* >(this)->OpenStream( rElement
.m_aName
, StreamMode::STD_READ
, pImp
->m_bDirect
);
2375 bDeleteStream
= true;
2378 pStream
->CopyTo( pOtherStream
.get() );
2379 SetError( pStream
->GetError() );
2380 if( pOtherStream
->GetError() )
2381 pDest
->SetError( pOtherStream
->GetError() );
2383 pOtherStream
->Commit();
2385 if ( bDeleteStream
)
2390 // copy the storage content
2391 // the destination storage must not be open
2392 BaseStorage
* pStorage
= nullptr;
2394 // if stream is already open, it is allowed to copy it, so be aware of this
2395 bool bDeleteStorage
= false;
2396 if ( rElement
.m_xStorage
.is() )
2397 pStorage
= rElement
.m_xStorage
->m_pAntiImpl
;
2400 pStorage
= const_cast<UCBStorage
*>(this)->OpenStorage( rElement
.m_aName
, pImp
->m_nMode
, pImp
->m_bDirect
);
2401 bDeleteStorage
= true;
2404 UCBStorage
* pUCBDest
= dynamic_cast<UCBStorage
*>( pDest
);
2405 UCBStorage
* pUCBCopy
= dynamic_cast<UCBStorage
*>( pStorage
);
2407 bool bOpenUCBStorage
= pUCBDest
&& pUCBCopy
;
2408 tools::SvRef
<BaseStorage
> pOtherStorage(bOpenUCBStorage
?
2409 pDest
->OpenUCBStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
) :
2410 pDest
->OpenOLEStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2412 // For UCB storages, the class id and the format id may differ,
2413 // do passing the class id is not sufficient.
2414 if( bOpenUCBStorage
)
2415 pOtherStorage
->SetClass( pStorage
->GetClassName(),
2416 pStorage
->GetFormat(),
2417 pUCBCopy
->pImp
->m_aUserTypeName
);
2419 pOtherStorage
->SetClassId( pStorage
->GetClassId() );
2420 pStorage
->CopyTo( *pOtherStorage
);
2421 SetError( pStorage
->GetError() );
2422 if( pOtherStorage
->GetError() )
2423 pDest
->SetError( pOtherStorage
->GetError() );
2425 pOtherStorage
->Commit();
2427 if ( bDeleteStorage
)
2431 return Good() && pDest
->Good();
2434 UCBStorageElement_Impl
* UCBStorage::FindElement_Impl( std::u16string_view rName
) const
2436 DBG_ASSERT( !rName
.empty(), "Name is empty!" );
2437 for (const auto& pElement
: pImp
->GetChildrenList())
2439 if ( pElement
->m_aName
== rName
&& !pElement
->m_bIsRemoved
)
2440 return pElement
.get();
2445 bool UCBStorage::CopyTo( BaseStorage
& rDestStg
) const
2447 DBG_ASSERT( &rDestStg
!= static_cast<BaseStorage
const *>(this), "Self-Copying is not possible!" );
2448 if ( &rDestStg
== static_cast<BaseStorage
const *>(this) )
2451 // perhaps it's also a problem if one storage is a parent of the other ?!
2452 // or if not: could be optimized ?!
2454 // For UCB storages, the class id and the format id may differ,
2455 // do passing the class id is not sufficient.
2456 if( dynamic_cast<const UCBStorage
*>(&rDestStg
) != nullptr )
2457 rDestStg
.SetClass( pImp
->m_aClassId
, pImp
->m_nFormat
,
2458 pImp
->m_aUserTypeName
);
2460 rDestStg
.SetClassId( GetClassId() );
2461 rDestStg
.SetDirty();
2464 for ( size_t i
= 0; i
< pImp
->GetChildrenList().size() && bRet
; ++i
)
2466 auto& pElement
= pImp
->GetChildrenList()[ i
];
2467 if ( !pElement
->m_bIsRemoved
)
2468 bRet
= CopyStorageElement_Impl( *pElement
, &rDestStg
, pElement
->m_aName
);
2472 SetError( rDestStg
.GetError() );
2473 return Good() && rDestStg
.Good();
2476 bool UCBStorage::CopyTo( const OUString
& rElemName
, BaseStorage
* pDest
, const OUString
& rNew
)
2478 if( rElemName
.isEmpty() )
2481 if ( pDest
== static_cast<BaseStorage
*>(this) )
2483 // can't double an element
2488 // for copying no optimization is useful, because in every case the stream data must be copied
2489 UCBStorageElement_Impl
* pElement
= FindElement_Impl( rElemName
);
2491 return CopyStorageElement_Impl( *pElement
, pDest
, rNew
);
2494 SetError( SVSTREAM_FILE_NOT_FOUND
);
2500 bool UCBStorage::Commit()
2502 // mark this storage for sending it on root commit
2503 pImp
->m_bCommited
= true;
2504 if ( pImp
->m_bIsRoot
)
2505 // the root storage coordinates committing by sending a Commit command to its content
2506 return ( pImp
->Commit() != COMMIT_RESULT_FAILURE
);
2511 bool UCBStorage::Revert()
2517 BaseStorageStream
* UCBStorage::OpenStream( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2519 if( rEleName
.isEmpty() )
2522 // try to find the storage element
2523 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2526 // element does not exist, check if creation is allowed
2527 if( nMode
& StreamMode::NOCREATE
)
2529 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2530 OUString aName
= pImp
->m_aURL
+ "/" + rEleName
;
2531 UCBStorageStream
* pStream
= new UCBStorageStream( aName
, nMode
, bDirect
, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2532 pStream
->SetError( GetError() );
2533 pStream
->pImp
->m_aName
= rEleName
;
2538 // create a new UCBStorageElement and insert it into the list
2539 pElement
= new UCBStorageElement_Impl( rEleName
);
2540 pElement
->m_bIsInserted
= true;
2541 pImp
->m_aChildrenList
.emplace_back( pElement
);
2545 if ( !pElement
->m_bIsFolder
)
2547 // check if stream is already created
2548 if ( pElement
->m_xStream
.is() )
2550 // stream has already been created; if it has no external reference, it may be opened another time
2551 if ( pElement
->m_xStream
->m_pAntiImpl
)
2553 OSL_FAIL("Stream is already open!" );
2554 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2559 // check if stream is opened with the same keyword as before
2560 // if not, generate a new stream because it could be encrypted vs. decrypted!
2561 if ( pElement
->m_xStream
->m_aKey
.isEmpty() )
2563 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2565 return new UCBStorageStream( pElement
->m_xStream
.get() );
2570 // stream is opened the first time
2571 pImp
->OpenStream( pElement
, nMode
, bDirect
);
2573 // if name has been changed before creating the stream: set name!
2574 pElement
->m_xStream
->m_aName
= rEleName
;
2575 return new UCBStorageStream( pElement
->m_xStream
.get() );
2581 void UCBStorage_Impl::OpenStream( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2583 OUString aName
= m_aURL
+ "/" +pElement
->m_aOriginalName
;
2584 pElement
->m_xStream
= new UCBStorageStream_Impl( aName
, nMode
, nullptr, bDirect
, m_bRepairPackage
, m_xProgressHandler
);
2587 BaseStorage
* UCBStorage::OpenUCBStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2589 if( rEleName
.isEmpty() )
2592 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2595 BaseStorage
* UCBStorage::OpenOLEStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2597 if( rEleName
.isEmpty() )
2600 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, false );
2603 BaseStorage
* UCBStorage::OpenStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2605 if( rEleName
.isEmpty() )
2608 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2611 BaseStorage
* UCBStorage::OpenStorage_Impl( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
, bool bForceUCBStorage
)
2613 // try to find the storage element
2614 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2617 // element does not exist, check if creation is allowed
2618 if( nMode
& StreamMode::NOCREATE
)
2620 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2621 OUString aName
= pImp
->m_aURL
+ "/" + rEleName
; // ???
2622 UCBStorage
*pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2623 pStorage
->pImp
->m_bIsRoot
= false;
2624 pStorage
->pImp
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2625 pStorage
->SetError( GetError() );
2629 // create a new UCBStorageElement and insert it into the list
2630 // problem: perhaps an OLEStorage should be created ?!
2631 // Because nothing is known about the element that should be created, an external parameter is needed !
2632 pElement
= new UCBStorageElement_Impl( rEleName
);
2633 pElement
->m_bIsInserted
= true;
2634 pImp
->m_aChildrenList
.emplace_back( pElement
);
2637 if ( !pElement
->m_bIsFolder
&& ( pElement
->m_bIsStorage
|| !bForceUCBStorage
) )
2639 // create OLE storages on a stream ( see ctor of SotStorage )
2640 // Such a storage will be created on a UCBStorageStream; it will write into the stream
2641 // if it is opened in direct mode or when it is committed. In this case the stream will be
2642 // modified and then it MUST be treated as committed.
2643 if ( !pElement
->m_xStream
.is() )
2645 BaseStorageStream
* pStr
= OpenStream( rEleName
, nMode
, bDirect
);
2646 UCBStorageStream
* pStream
= dynamic_cast<UCBStorageStream
*>( pStr
);
2649 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2653 pElement
->m_xStream
= pStream
->pImp
;
2657 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2658 bool bInited
= pElement
->m_xStream
->Init();
2661 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2665 pElement
->m_bIsStorage
= true;
2666 return pElement
->m_xStream
->CreateStorage(); // can only be created in transacted mode
2668 else if ( pElement
->m_xStorage
.is() )
2670 // storage has already been opened; if it has no external reference, it may be opened another time
2671 if ( pElement
->m_xStorage
->m_pAntiImpl
)
2673 OSL_FAIL("Storage is already open!" );
2674 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2678 bool bIsWritable
= bool( pElement
->m_xStorage
->m_nMode
& StreamMode::WRITE
);
2679 if ( !bIsWritable
&& ( nMode
& StreamMode::WRITE
) )
2681 OUString aName
= pImp
->m_aURL
+ "/" + pElement
->m_aOriginalName
;
2682 UCBStorage
* pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2683 pElement
->m_xStorage
= pStorage
->pImp
;
2688 return new UCBStorage( pElement
->m_xStorage
.get() );
2692 else if ( !pElement
->m_xStream
.is() )
2694 // storage is opened the first time
2695 bool bIsWritable
= bool(pImp
->m_nMode
& StreamMode::WRITE
);
2696 if ( pImp
->m_bIsLinked
&& pImp
->m_bIsRoot
&& bIsWritable
)
2698 // make sure that the root storage object has been created before substorages will be created
2699 INetURLObject
aFolderObj( pImp
->m_aURL
);
2700 aFolderObj
.removeSegment();
2702 Content
aFolder( aFolderObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2703 pImp
->m_oContent
.emplace();
2704 bool bRet
= ::utl::UCBContentHelper::MakeFolder( aFolder
, pImp
->m_aName
, *pImp
->m_oContent
);
2707 SetError( SVSTREAM_CANNOT_MAKE
);
2712 UCBStorage_Impl
* pStor
= pImp
->OpenStorage( pElement
, nMode
, bDirect
);
2715 if ( pElement
->m_bIsInserted
)
2716 pStor
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2718 return new UCBStorage( pStor
);
2725 UCBStorage_Impl
* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2727 UCBStorage_Impl
* pRet
= nullptr;
2728 OUString aName
= m_aURL
+ "/" + pElement
->m_aOriginalName
; // ???
2730 pElement
->m_bIsStorage
= pElement
->m_bIsFolder
= true;
2732 if ( m_bIsLinked
&& !::utl::UCBContentHelper::Exists( aName
) )
2735 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_oContent
, pElement
->m_aOriginalName
, aNewFolder
);
2737 pRet
= new UCBStorage_Impl( aNewFolder
, aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2741 pRet
= new UCBStorage_Impl( aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2746 pRet
->m_bIsLinked
= m_bIsLinked
;
2747 pRet
->m_bIsRoot
= false;
2749 // if name has been changed before creating the stream: set name!
2750 pRet
->m_aName
= pElement
->m_aOriginalName
;
2751 pElement
->m_xStorage
= pRet
;
2760 bool UCBStorage::IsStorage( const OUString
& rEleName
) const
2762 if( rEleName
.isEmpty() )
2765 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2766 return ( pElement
&& pElement
->m_bIsStorage
);
2769 bool UCBStorage::IsStream( const OUString
& rEleName
) const
2771 if( rEleName
.isEmpty() )
2774 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2775 return ( pElement
&& !pElement
->m_bIsStorage
);
2778 bool UCBStorage::IsContained( const OUString
& rEleName
) const
2780 if( rEleName
.isEmpty() )
2782 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2783 return ( pElement
!= nullptr );
2786 void UCBStorage::Remove( const OUString
& rEleName
)
2788 if( rEleName
.isEmpty() )
2791 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2794 pElement
->m_bIsRemoved
= true;
2797 SetError( SVSTREAM_FILE_NOT_FOUND
);
2800 bool UCBStorage::ValidateFAT()
2806 bool UCBStorage::Validate( bool bWrite
) const
2809 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
2812 bool UCBStorage::ValidateMode( StreamMode m
) const
2815 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
2817 // only SHARE_DENYALL allowed
2818 // storages open in r/o mode are OK, since only
2819 // the commit may fail
2820 if( m
& StreamMode::SHARE_DENYALL
)
2826 bool UCBStorage::Equals( const BaseStorage
& rStorage
) const
2829 return static_cast<BaseStorage
const *>(this) == &rStorage
;
2832 bool UCBStorage::IsStorageFile( SvStream
* pFile
)
2837 sal_uInt64 nPos
= pFile
->Tell();
2838 if ( pFile
->TellEnd() < 4 )
2842 sal_uInt32
nBytes(0);
2843 pFile
->ReadUInt32( nBytes
);
2845 // search for the magic bytes
2846 bool bRet
= ( nBytes
== 0x04034b50 );
2849 // disk spanned file have an additional header in front of the usual one
2850 bRet
= ( nBytes
== 0x08074b50 );
2854 pFile
->ReadUInt32( nBytes
);
2855 bRet
= ( nBytes
== 0x04034b50 );
2859 pFile
->Seek( nPos
);
2863 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */