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 <com/sun/star/lang/IllegalArgumentException.hpp>
24 #include <ucbhelper/content.hxx>
25 #include <com/sun/star/uno/Reference.h>
26 #include <com/sun/star/ucb/NameClash.hpp>
27 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
28 #include <unotools/tempfile.hxx>
29 #include <unotools/ucbstreamhelper.hxx>
30 #include <com/sun/star/io/XInputStream.hpp>
31 #include <com/sun/star/ucb/InsertCommandArgument.hpp>
32 #include <com/sun/star/ucb/ResultSetException.hpp>
33 #include <com/sun/star/uno/Sequence.h>
34 #include <com/sun/star/sdbc/XResultSet.hpp>
35 #include <com/sun/star/ucb/XContentAccess.hpp>
36 #include <com/sun/star/sdbc/XRow.hpp>
37 #include <com/sun/star/ucb/CommandAbortedException.hpp>
38 #include <com/sun/star/datatransfer/DataFlavor.hpp>
39 #include <com/sun/star/ucb/ContentInfo.hpp>
40 #include <com/sun/star/ucb/ContentInfoAttribute.hpp>
41 #include <com/sun/star/beans/Property.hpp>
42 #include <com/sun/star/packages/manifest/ManifestWriter.hpp>
43 #include <com/sun/star/packages/manifest/ManifestReader.hpp>
44 #include <com/sun/star/ucb/InteractiveIOException.hpp>
45 #include <com/sun/star/ucb/ContentCreationException.hpp>
48 #include <rtl/digest.h>
49 #include <osl/diagnose.h>
50 #include <osl/file.hxx>
51 #include <sal/log.hxx>
52 #include <tools/ref.hxx>
53 #include <tools/debug.hxx>
54 #include <unotools/streamhelper.hxx>
55 #include <unotools/streamwrap.hxx>
56 #include <unotools/ucbhelper.hxx>
57 #include <unotools/localfilehelper.hxx>
58 #include <tools/urlobj.hxx>
59 #include <comphelper/processfactory.hxx>
60 #include <cppuhelper/implbase.hxx>
61 #include <ucbhelper/commandenvironment.hxx>
63 #include <sot/stg.hxx>
64 #include <sot/storinfo.hxx>
65 #include <sot/storage.hxx>
66 #include <sot/exchange.hxx>
67 #include <sot/formats.hxx>
68 #include <comphelper/classids.hxx>
72 using namespace ::com::sun::star::lang
;
73 using namespace ::com::sun::star::beans
;
74 using namespace ::com::sun::star::uno
;
75 using namespace ::com::sun::star::ucb
;
76 using namespace ::com::sun::star::io
;
77 using namespace ::com::sun::star::sdbc
;
78 using namespace ::ucbhelper
;
80 #if OSL_DEBUG_LEVEL > 0
81 static int nOpenFiles
=0;
82 static int nOpenStreams
=0;
85 typedef ::cppu::WeakImplHelper
< XInputStream
, XSeekable
> FileInputStreamWrapper_Base
;
86 class FileStreamWrapper_Impl
: public FileInputStreamWrapper_Base
89 ::osl::Mutex m_aMutex
;
91 std::unique_ptr
<SvStream
> m_pSvStream
;
94 explicit FileStreamWrapper_Impl(const OUString
& rName
);
95 virtual ~FileStreamWrapper_Impl() override
;
97 virtual void SAL_CALL
seek( sal_Int64 _nLocation
) override
;
98 virtual sal_Int64 SAL_CALL
getPosition( ) override
;
99 virtual sal_Int64 SAL_CALL
getLength( ) override
;
100 virtual sal_Int32 SAL_CALL
readBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
) override
;
101 virtual sal_Int32 SAL_CALL
readSomeBytes( Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
) override
;
102 virtual void SAL_CALL
skipBytes(sal_Int32 nBytesToSkip
) override
;
103 virtual sal_Int32 SAL_CALL
available() override
;
104 virtual void SAL_CALL
closeInput() override
;
107 void checkConnected();
112 FileStreamWrapper_Impl::FileStreamWrapper_Impl( const OUString
& rName
)
115 // if no URL is provided the stream is empty
119 FileStreamWrapper_Impl::~FileStreamWrapper_Impl()
124 #if OSL_DEBUG_LEVEL > 0
129 if (!m_aURL
.isEmpty())
130 osl::File::remove(m_aURL
);
134 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nBytesToRead
)
136 if ( m_aURL
.isEmpty() )
144 if (nBytesToRead
< 0)
145 throw BufferSizeExceededException(OUString(),static_cast<XWeak
*>(this));
147 ::osl::MutexGuard
aGuard( m_aMutex
);
149 if (aData
.getLength() < nBytesToRead
)
150 aData
.realloc(nBytesToRead
);
152 sal_uInt32 nRead
= m_pSvStream
->ReadBytes(static_cast<void*>(aData
.getArray()), nBytesToRead
);
155 // if read characters < MaxLength, adjust sequence
156 if (static_cast<sal_Int32
>(nRead
) < aData
.getLength())
157 aData
.realloc( nRead
);
163 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::readSomeBytes(Sequence
< sal_Int8
>& aData
, sal_Int32 nMaxBytesToRead
)
165 if ( m_aURL
.isEmpty() )
173 if (nMaxBytesToRead
< 0)
174 throw BufferSizeExceededException(OUString(),static_cast<XWeak
*>(this));
176 if (m_pSvStream
->eof())
182 return readBytes(aData
, nMaxBytesToRead
);
186 void SAL_CALL
FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip
)
188 if ( m_aURL
.isEmpty() )
191 ::osl::MutexGuard
aGuard( m_aMutex
);
194 m_pSvStream
->SeekRel(nBytesToSkip
);
199 sal_Int32 SAL_CALL
FileStreamWrapper_Impl::available()
201 if ( m_aURL
.isEmpty() )
204 ::osl::MutexGuard
aGuard( m_aMutex
);
207 sal_Int64 nAvailable
= m_pSvStream
->remainingSize();
210 return std::min
<sal_Int64
>(SAL_MAX_INT32
, nAvailable
);
214 void SAL_CALL
FileStreamWrapper_Impl::closeInput()
216 if ( m_aURL
.isEmpty() )
219 ::osl::MutexGuard
aGuard( m_aMutex
);
222 #if OSL_DEBUG_LEVEL > 0
225 osl::File::remove(m_aURL
);
230 void SAL_CALL
FileStreamWrapper_Impl::seek( sal_Int64 _nLocation
)
232 if ( m_aURL
.isEmpty() )
235 ::osl::MutexGuard
aGuard( m_aMutex
);
238 m_pSvStream
->Seek(static_cast<sal_uInt32
>(_nLocation
));
243 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getPosition( )
245 if ( m_aURL
.isEmpty() )
248 ::osl::MutexGuard
aGuard( m_aMutex
);
251 sal_uInt32 nPos
= m_pSvStream
->Tell();
253 return static_cast<sal_Int64
>(nPos
);
257 sal_Int64 SAL_CALL
FileStreamWrapper_Impl::getLength( )
259 if ( m_aURL
.isEmpty() )
262 ::osl::MutexGuard
aGuard( m_aMutex
);
267 sal_Int64 nEndPos
= m_pSvStream
->TellEnd();
273 void FileStreamWrapper_Impl::checkConnected()
275 if ( m_aURL
.isEmpty() )
276 throw NotConnectedException(OUString(), static_cast<XWeak
*>(this));
279 m_pSvStream
= ::utl::UcbStreamHelper::CreateStream( m_aURL
, StreamMode::STD_READ
);
280 #if OSL_DEBUG_LEVEL > 0
287 void FileStreamWrapper_Impl::checkError()
291 if (m_pSvStream
->SvStream::GetError() != ERRCODE_NONE
)
292 // TODO: really evaluate the error
293 throw NotConnectedException(OUString(), static_cast<XWeak
*>(this));
297 #define COMMIT_RESULT_FAILURE 0
298 #define COMMIT_RESULT_NOTHING_TO_DO 1
299 #define COMMIT_RESULT_SUCCESS 2
301 static SotClipboardFormatId
GetFormatId_Impl( const SvGlobalName
& aName
)
303 if ( aName
== SvGlobalName( SO3_SW_CLASSID_60
) )
304 return SotClipboardFormatId::STARWRITER_60
;
305 if ( aName
== SvGlobalName( SO3_SWWEB_CLASSID_60
) )
306 return SotClipboardFormatId::STARWRITERWEB_60
;
307 if ( aName
== SvGlobalName( SO3_SWGLOB_CLASSID_60
) )
308 return SotClipboardFormatId::STARWRITERGLOB_60
;
309 if ( aName
== SvGlobalName( SO3_SDRAW_CLASSID_60
) )
310 return SotClipboardFormatId::STARDRAW_60
;
311 if ( aName
== SvGlobalName( SO3_SIMPRESS_CLASSID_60
) )
312 return SotClipboardFormatId::STARIMPRESS_60
;
313 if ( aName
== SvGlobalName( SO3_SC_CLASSID_60
) )
314 return SotClipboardFormatId::STARCALC_60
;
315 if ( aName
== SvGlobalName( SO3_SCH_CLASSID_60
) )
316 return SotClipboardFormatId::STARCHART_60
;
317 if ( aName
== SvGlobalName( SO3_SM_CLASSID_60
) )
318 return SotClipboardFormatId::STARMATH_60
;
319 if ( aName
== SvGlobalName( SO3_OUT_CLASSID
) ||
320 aName
== SvGlobalName( SO3_APPLET_CLASSID
) ||
321 aName
== SvGlobalName( SO3_PLUGIN_CLASSID
) ||
322 aName
== SvGlobalName( SO3_IFRAME_CLASSID
) )
323 // allowed, but not supported
324 return SotClipboardFormatId::NONE
;
327 OSL_FAIL( "Unknown UCB storage format!" );
328 return SotClipboardFormatId::NONE
;
333 static SvGlobalName
GetClassId_Impl( SotClipboardFormatId nFormat
)
337 case SotClipboardFormatId::STARWRITER_8
:
338 case SotClipboardFormatId::STARWRITER_8_TEMPLATE
:
339 return SvGlobalName( SO3_SW_CLASSID_60
);
340 case SotClipboardFormatId::STARWRITERWEB_8
:
341 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
342 case SotClipboardFormatId::STARWRITERGLOB_8
:
343 case SotClipboardFormatId::STARWRITERGLOB_8_TEMPLATE
:
344 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
345 case SotClipboardFormatId::STARDRAW_8
:
346 case SotClipboardFormatId::STARDRAW_8_TEMPLATE
:
347 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
348 case SotClipboardFormatId::STARIMPRESS_8
:
349 case SotClipboardFormatId::STARIMPRESS_8_TEMPLATE
:
350 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
351 case SotClipboardFormatId::STARCALC_8
:
352 case SotClipboardFormatId::STARCALC_8_TEMPLATE
:
353 return SvGlobalName( SO3_SC_CLASSID_60
);
354 case SotClipboardFormatId::STARCHART_8
:
355 case SotClipboardFormatId::STARCHART_8_TEMPLATE
:
356 return SvGlobalName( SO3_SCH_CLASSID_60
);
357 case SotClipboardFormatId::STARMATH_8
:
358 case SotClipboardFormatId::STARMATH_8_TEMPLATE
:
359 return SvGlobalName( SO3_SM_CLASSID_60
);
360 case SotClipboardFormatId::STARWRITER_60
:
361 return SvGlobalName( SO3_SW_CLASSID_60
);
362 case SotClipboardFormatId::STARWRITERWEB_60
:
363 return SvGlobalName( SO3_SWWEB_CLASSID_60
);
364 case SotClipboardFormatId::STARWRITERGLOB_60
:
365 return SvGlobalName( SO3_SWGLOB_CLASSID_60
);
366 case SotClipboardFormatId::STARDRAW_60
:
367 return SvGlobalName( SO3_SDRAW_CLASSID_60
);
368 case SotClipboardFormatId::STARIMPRESS_60
:
369 return SvGlobalName( SO3_SIMPRESS_CLASSID_60
);
370 case SotClipboardFormatId::STARCALC_60
:
371 return SvGlobalName( SO3_SC_CLASSID_60
);
372 case SotClipboardFormatId::STARCHART_60
:
373 return SvGlobalName( SO3_SCH_CLASSID_60
);
374 case SotClipboardFormatId::STARMATH_60
:
375 return SvGlobalName( SO3_SM_CLASSID_60
);
377 return SvGlobalName();
381 // All storage and streams are refcounted internally; outside of this classes they are only accessible through a handle
382 // class, that uses the refcounted object as impl-class.
384 class UCBStorageStream_Impl
: public SvRefBase
, public SvStream
386 virtual ~UCBStorageStream_Impl() override
;
389 virtual std::size_t GetData(void* pData
, std::size_t nSize
) override
;
390 virtual std::size_t PutData(const void* pData
, std::size_t nSize
) override
;
391 virtual sal_uInt64
SeekPos( sal_uInt64 nPos
) override
;
392 virtual void SetSize( sal_uInt64 nSize
) override
;
393 virtual void FlushData() override
;
394 virtual void ResetError() override
;
396 UCBStorageStream
* m_pAntiImpl
; // only valid if an external reference exists
398 OUString m_aOriginalName
;// the original name before accessing the stream
399 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
400 OUString m_aURL
; // the full path name to create the content
401 OUString m_aContentType
;
402 OUString m_aOriginalContentType
;
403 OString
const m_aKey
;
404 ::ucbhelper::Content
* m_pContent
; // the content that provides the data
405 Reference
<XInputStream
> m_rSource
; // the stream covering the original data of the content
406 std::unique_ptr
<SvStream
> m_pStream
; // the stream worked on; for readonly streams it is the original stream of the content
407 // for read/write streams it's a copy into a temporary file
408 OUString m_aTempURL
; // URL of this temporary stream
410 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
411 bool m_bSourceRead
; // Source still contains useful information
412 bool m_bModified
; // only modified streams will be sent to the original content
413 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
414 bool const m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
415 // this means that the root storage does an autocommit when its external
416 // reference is destroyed
417 bool m_bIsOLEStorage
;// an OLEStorage on a UCBStorageStream makes this an Autocommit-stream
419 UCBStorageStream_Impl( const OUString
&, StreamMode
, UCBStorageStream
*, bool,
420 bool bRepair
, Reference
< XProgressHandler
> const & xProgress
);
425 sal_Int16
Commit(); // if modified and committed: transfer an XInputStream to the content
426 void Revert(); // discard all changes
427 BaseStorage
* CreateStorage();// create an OLE Storage on the UCBStorageStream
430 sal_uInt64
ReadSourceWriteTemporary( sal_uInt64 aLength
); // read aLength from source and copy to temporary,
431 // no seeking is produced
432 void ReadSourceWriteTemporary(); // read source till the end and copy to temporary,
434 void CopySourceToTemporary(); // same as ReadSourceWriteToTemporary()
435 // but the writing is done at the end of temporary
436 // pointer position is not changed
437 using SvStream::SetError
;
438 void SetError( ErrCode nError
);
439 void PrepareCachedForReopen( StreamMode nMode
);
442 typedef tools::SvRef
<UCBStorageStream_Impl
> UCBStorageStream_ImplRef
;
444 struct UCBStorageElement_Impl
;
445 typedef std::vector
<std::unique_ptr
<UCBStorageElement_Impl
>> UCBStorageElementList_Impl
;
447 class UCBStorage_Impl
: public SvRefBase
449 virtual ~UCBStorage_Impl() override
;
451 UCBStorage
* m_pAntiImpl
; // only valid if external references exists
453 OUString m_aOriginalName
;// the original name before accessing the storage
454 OUString m_aName
; // the actual name ( changed with a Rename command at the parent )
455 OUString m_aURL
; // the full path name to create the content
456 OUString m_aContentType
;
457 OUString m_aOriginalContentType
;
458 ::ucbhelper::Content
* m_pContent
; // the content that provides the storage elements
459 ::utl::TempFile
* m_pTempFile
; // temporary file, only for storages on stream
460 SvStream
* m_pSource
; // original stream, only for storages on a stream
462 StreamMode m_nMode
; // open mode ( read/write/trunc/nocreate/sharing )
463 bool m_bCommited
; // sending the streams is coordinated by the root storage of the package
464 bool const m_bDirect
; // the storage and its streams are opened in direct mode; for UCBStorages
465 // this means that the root storage does an autocommit when its external
466 // reference is destroyed
467 bool m_bIsRoot
; // marks this storage as root storages that manages all commits and reverts
470 SotClipboardFormatId m_nFormat
;
471 OUString m_aUserTypeName
;
472 SvGlobalName m_aClassId
;
474 UCBStorageElementList_Impl m_aChildrenList
;
476 bool const m_bRepairPackage
;
477 Reference
< XProgressHandler
> m_xProgressHandler
;
479 UCBStorage_Impl( const ::ucbhelper::Content
&, const OUString
&, StreamMode
, UCBStorage
*, bool,
480 bool, bool = false, Reference
< XProgressHandler
> const & = Reference
< XProgressHandler
>() );
481 UCBStorage_Impl( const OUString
&, StreamMode
, UCBStorage
*, bool, bool,
482 bool, Reference
< XProgressHandler
> const & );
483 UCBStorage_Impl( SvStream
&, UCBStorage
*, bool );
487 bool Insert( ::ucbhelper::Content
*pContent
);
488 UCBStorage_Impl
* OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
);
489 void OpenStream( UCBStorageElement_Impl
*, StreamMode
, bool );
490 void SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
491 void GetProps( sal_Int32
&, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& );
492 sal_Int32
GetObjectCount();
494 void CreateContent();
495 ::ucbhelper::Content
* GetContent()
501 UCBStorageElementList_Impl
& GetChildrenList()
503 const ErrCode nError
= m_nError
;
505 if ( m_nMode
& StreamMode::WRITE
)
510 m_pAntiImpl
->ResetError();
511 m_pAntiImpl
->SetError( nError
);
514 return m_aChildrenList
;
517 void SetError( ErrCode nError
);
520 typedef tools::SvRef
<UCBStorage_Impl
> UCBStorage_ImplRef
;
522 // this struct contains all necessary information on an element inside a UCBStorage
523 struct UCBStorageElement_Impl
525 OUString m_aName
; // the actual URL relative to the root "folder"
526 OUString m_aOriginalName
;// the original name in the content
527 sal_uLong
const m_nSize
;
528 bool m_bIsFolder
; // Only true when it is a UCBStorage !
529 bool m_bIsStorage
; // Also true when it is an OLEStorage !
530 bool m_bIsRemoved
; // element will be removed on commit
531 bool m_bIsInserted
; // element will be removed on revert
532 UCBStorage_ImplRef m_xStorage
; // reference to the "real" storage
533 UCBStorageStream_ImplRef m_xStream
; // reference to the "real" stream
535 UCBStorageElement_Impl( const OUString
& rName
,
536 bool bIsFolder
= false, sal_uLong nSize
= 0 )
538 , m_aOriginalName( rName
)
540 , m_bIsFolder( bIsFolder
)
541 , m_bIsStorage( bIsFolder
)
542 , m_bIsRemoved( false )
543 , m_bIsInserted( false )
547 ::ucbhelper::Content
* GetContent();
548 bool IsModified() const;
549 OUString
GetContentType() const;
550 void SetContentType( const OUString
& );
551 OUString
GetOriginalContentType() const;
552 bool IsLoaded() const
553 { return m_xStream
.is() || m_xStorage
.is(); }
556 ::ucbhelper::Content
* UCBStorageElement_Impl::GetContent()
558 if ( m_xStream
.is() )
559 return m_xStream
->m_pContent
;
560 else if ( m_xStorage
.is() )
561 return m_xStorage
->GetContent();
566 OUString
UCBStorageElement_Impl::GetContentType() const
568 if ( m_xStream
.is() )
569 return m_xStream
->m_aContentType
;
570 else if ( m_xStorage
.is() )
571 return m_xStorage
->m_aContentType
;
574 OSL_FAIL("Element not loaded!");
579 void UCBStorageElement_Impl::SetContentType( const OUString
& rType
)
581 if ( m_xStream
.is() ) {
582 m_xStream
->m_aContentType
= m_xStream
->m_aOriginalContentType
= rType
;
584 else if ( m_xStorage
.is() ) {
585 m_xStorage
->m_aContentType
= m_xStorage
->m_aOriginalContentType
= rType
;
588 OSL_FAIL("Element not loaded!");
592 OUString
UCBStorageElement_Impl::GetOriginalContentType() const
594 if ( m_xStream
.is() )
595 return m_xStream
->m_aOriginalContentType
;
596 else if ( m_xStorage
.is() )
597 return m_xStorage
->m_aOriginalContentType
;
602 bool UCBStorageElement_Impl::IsModified() const
604 bool bModified
= m_bIsRemoved
|| m_bIsInserted
|| m_aName
!= m_aOriginalName
;
607 if ( m_xStream
.is() )
608 bModified
= m_xStream
->m_aContentType
!= m_xStream
->m_aOriginalContentType
;
609 else if ( m_xStorage
.is() )
610 bModified
= m_xStorage
->m_aContentType
!= m_xStorage
->m_aOriginalContentType
;
616 UCBStorageStream_Impl::UCBStorageStream_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorageStream
* pStream
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
617 : m_pAntiImpl( pStream
)
619 , m_pContent( nullptr )
620 , m_nError( ERRCODE_NONE
)
622 , m_bSourceRead( !( nMode
& StreamMode::TRUNC
) )
623 , m_bModified( false )
624 , m_bCommited( false )
625 , m_bDirect( bDirect
)
626 , m_bIsOLEStorage( false )
628 // name is last segment in URL
629 INetURLObject
aObj( rName
);
630 m_aName
= m_aOriginalName
= aObj
.GetLastName();
633 // create the content
634 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
636 OUString
aTemp( rName
);
640 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(), xProgress
);
641 aTemp
+= "?repairpackage";
644 m_pContent
= new ::ucbhelper::Content( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
646 catch (const ContentCreationException
&)
648 // content could not be created
649 SetError( SVSTREAM_CANNOT_MAKE
);
651 catch (const RuntimeException
&)
653 // any other error - not specified
654 SetError( ERRCODE_IO_GENERAL
);
658 UCBStorageStream_Impl::~UCBStorageStream_Impl()
665 if (!m_aTempURL
.isEmpty())
666 osl::File::remove(m_aTempURL
);
672 bool UCBStorageStream_Impl::Init()
676 // no temporary stream was created
679 if ( m_aTempURL
.isEmpty() )
680 m_aTempURL
= ::utl::TempFile().GetURL();
682 m_pStream
= ::utl::UcbStreamHelper::CreateStream( m_aTempURL
, StreamMode::STD_READWRITE
, true /* bFileExists */ );
683 #if OSL_DEBUG_LEVEL > 0
689 OSL_FAIL( "Suspicious temporary stream creation!" );
690 SetError( SVSTREAM_CANNOT_MAKE
);
694 SetError( m_pStream
->GetError() );
697 if( m_bSourceRead
&& !m_rSource
.is() )
699 // source file contain useful information and is not opened
700 // open it from the point of noncopied data
704 m_rSource
= m_pContent
->openStream();
706 catch (const Exception
&)
708 // usually means that stream could not be opened
713 m_pStream
->Seek( STREAM_SEEK_TO_END
);
717 m_rSource
->skipBytes( m_pStream
->Tell() );
719 catch (const BufferSizeExceededException
&)
721 // the temporary stream already contain all the data
722 m_bSourceRead
= false;
724 catch (const Exception
&)
726 // something is really wrong
727 m_bSourceRead
= false;
728 OSL_FAIL( "Can not operate original stream!" );
729 SetError( SVSTREAM_CANNOT_MAKE
);
732 m_pStream
->Seek( 0 );
736 // if the new file is edited then no source exist
737 m_bSourceRead
= false;
738 //SetError( SVSTREAM_CANNOT_MAKE );
742 DBG_ASSERT( m_rSource
.is() || !m_bSourceRead
, "Unreadable source stream!" );
747 void UCBStorageStream_Impl::ReadSourceWriteTemporary()
749 // read source stream till the end and copy all the data to
750 // the current position of the temporary stream
754 Sequence
<sal_Int8
> aData(32000);
761 aReaded
= m_rSource
->readBytes( aData
, 32000 );
762 m_pStream
->WriteBytes(aData
.getArray(), aReaded
);
763 } while( aReaded
== 32000 );
765 catch (const Exception
&e
)
767 SAL_WARN( "sot", e
);
771 m_bSourceRead
= false;
774 sal_uInt64
UCBStorageStream_Impl::ReadSourceWriteTemporary(sal_uInt64 aLength
)
776 // read aLength bite from the source stream and copy them to the current
777 // position of the temporary stream
779 sal_uInt64 aResult
= 0;
783 Sequence
<sal_Int8
> aData(32000);
788 sal_uLong aReaded
= 32000;
790 for (sal_uInt64 nInd
= 0; nInd
< aLength
&& aReaded
== 32000 ; nInd
+= 32000)
792 sal_uLong aToCopy
= std::min
<sal_uInt64
>( aLength
- nInd
, 32000 );
793 aReaded
= m_rSource
->readBytes( aData
, aToCopy
);
794 aResult
+= m_pStream
->WriteBytes(aData
.getArray(), aReaded
);
797 if( aResult
< aLength
)
798 m_bSourceRead
= false;
800 catch( const Exception
& e
)
802 SAL_WARN( "sot", e
);
809 void UCBStorageStream_Impl::CopySourceToTemporary()
811 // current position of the temporary stream is not changed
814 sal_uInt64 aPos
= m_pStream
->Tell();
815 m_pStream
->Seek( STREAM_SEEK_TO_END
);
816 ReadSourceWriteTemporary();
817 m_pStream
->Seek( aPos
);
821 // UCBStorageStream_Impl must have a SvStream interface, because it then can be used as underlying stream
822 // of an OLEStorage; so every write access caused by storage operations marks the UCBStorageStream as modified
823 std::size_t UCBStorageStream_Impl::GetData(void* pData
, std::size_t const nSize
)
825 std::size_t aResult
= 0;
831 // read data that is in temporary stream
832 aResult
= m_pStream
->ReadBytes( pData
, nSize
);
833 if( m_bSourceRead
&& aResult
< nSize
)
835 // read the tail of the data from original stream
836 // copy this tail to the temporary stream
838 std::size_t aToRead
= nSize
- aResult
;
839 pData
= static_cast<void*>( static_cast<char*>(pData
) + aResult
);
843 Sequence
<sal_Int8
> aData( aToRead
);
844 std::size_t aReaded
= m_rSource
->readBytes( aData
, aToRead
);
845 aResult
+= m_pStream
->WriteBytes(static_cast<void*>(aData
.getArray()), aReaded
);
846 memcpy( pData
, aData
.getArray(), aReaded
);
848 catch (const Exception
&e
)
850 SAL_WARN( "sot", e
);
853 if( aResult
< nSize
)
854 m_bSourceRead
= false;
860 std::size_t UCBStorageStream_Impl::PutData(const void* pData
, std::size_t const nSize
)
862 if ( !(m_nMode
& StreamMode::WRITE
) )
864 SetError( ERRCODE_IO_ACCESSDENIED
);
868 if( !nSize
|| !Init() )
871 std::size_t aResult
= m_pStream
->WriteBytes( pData
, nSize
);
873 m_bModified
= aResult
> 0;
879 sal_uInt64
UCBStorageStream_Impl::SeekPos(sal_uInt64
const nPos
)
881 // check if a truncated STREAM_SEEK_TO_END was passed
882 assert(nPos
!= SAL_MAX_UINT32
);
889 if( nPos
== STREAM_SEEK_TO_END
)
891 m_pStream
->Seek( STREAM_SEEK_TO_END
);
892 ReadSourceWriteTemporary();
893 aResult
= m_pStream
->Tell();
897 // the problem is that even if nPos is larger the length
898 // of the stream, the stream pointer will be moved to this position
899 // so we have to check if temporary stream does not contain required position
901 if( m_pStream
->Tell() > nPos
902 || m_pStream
->Seek( STREAM_SEEK_TO_END
) > nPos
)
904 // no copiing is required
905 aResult
= m_pStream
->Seek( nPos
);
909 // the temp stream pointer points to the end now
910 aResult
= m_pStream
->Tell();
916 aResult
+= ReadSourceWriteTemporary( nPos
- aResult
);
918 m_bSourceRead
= false;
920 DBG_ASSERT( aResult
== m_pStream
->Tell(), "Error in stream arithmetic!\n" );
923 if( (m_nMode
& StreamMode::WRITE
) && !m_bSourceRead
&& aResult
< nPos
)
925 // it means that all the Source stream was copied already
926 // but the required position still was not reached
927 // for writable streams it should be done
928 m_pStream
->SetStreamSize( nPos
);
929 aResult
= m_pStream
->Seek( STREAM_SEEK_TO_END
);
930 DBG_ASSERT( aResult
== nPos
, "Error in stream arithmetic!\n" );
939 void UCBStorageStream_Impl::SetSize(sal_uInt64
const nSize
)
941 if ( !(m_nMode
& StreamMode::WRITE
) )
943 SetError( ERRCODE_IO_ACCESSDENIED
);
954 sal_uInt64
const aPos
= m_pStream
->Tell();
955 m_pStream
->Seek( STREAM_SEEK_TO_END
);
956 if( m_pStream
->Tell() < nSize
)
957 ReadSourceWriteTemporary( nSize
- m_pStream
->Tell() );
958 m_pStream
->Seek( aPos
);
961 m_pStream
->SetStreamSize( nSize
);
962 m_bSourceRead
= false;
965 void UCBStorageStream_Impl::FlushData()
969 CopySourceToTemporary();
976 void UCBStorageStream_Impl::SetError( ErrCode nErr
)
981 SvStream::SetError( nErr
);
982 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nErr
);
986 void UCBStorageStream_Impl::ResetError()
988 m_nError
= ERRCODE_NONE
;
989 SvStream::ResetError();
991 m_pAntiImpl
->ResetError();
994 sal_uLong
UCBStorageStream_Impl::GetSize()
999 sal_uInt64 nPos
= m_pStream
->Tell();
1000 m_pStream
->Seek( STREAM_SEEK_TO_END
);
1001 ReadSourceWriteTemporary();
1002 sal_uInt64 nRet
= m_pStream
->Tell();
1003 m_pStream
->Seek( nPos
);
1008 BaseStorage
* UCBStorageStream_Impl::CreateStorage()
1010 // create an OLEStorage on a SvStream ( = this )
1011 // it gets the root attribute because otherwise it would probably not write before my root is committed
1012 UCBStorageStream
* pNewStorageStream
= new UCBStorageStream( this );
1013 Storage
*pStorage
= new Storage( *pNewStorageStream
, m_bDirect
);
1015 // GetError() call cleares error code for OLE storages, must be changed in future
1016 const ErrCode nTmpErr
= pStorage
->GetError();
1017 pStorage
->SetError( nTmpErr
);
1019 m_bIsOLEStorage
= !nTmpErr
;
1020 return static_cast< BaseStorage
* > ( pStorage
);
1023 sal_Int16
UCBStorageStream_Impl::Commit()
1025 // send stream to the original content
1026 // the parent storage is responsible for the correct handling of deleted contents
1027 if ( m_bCommited
|| m_bIsOLEStorage
|| m_bDirect
)
1029 // modified streams with OLEStorages on it have autocommit; it is assumed that the OLEStorage
1030 // was committed as well ( if not opened in direct mode )
1036 CopySourceToTemporary();
1038 // release all stream handles
1041 // the temporary file does not exist only for truncated streams
1042 DBG_ASSERT( !m_aTempURL
.isEmpty() || ( m_nMode
& StreamMode::TRUNC
), "No temporary file to read from!");
1043 if ( m_aTempURL
.isEmpty() && !( m_nMode
& StreamMode::TRUNC
) )
1044 throw RuntimeException();
1046 // create wrapper to stream that is only used while reading inside package component
1047 Reference
< XInputStream
> xStream
= new FileStreamWrapper_Impl( m_aTempURL
);
1049 InsertCommandArgument aArg
;
1050 aArg
.Data
= xStream
;
1051 aArg
.ReplaceExisting
= true;
1052 m_pContent
->executeCommand( "insert", Any(aArg
) );
1054 // wrapper now controls lifetime of temporary file
1057 INetURLObject
aObj( m_aURL
);
1058 aObj
.SetName( m_aName
);
1059 m_aURL
= aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
);
1060 m_bModified
= false;
1061 m_bSourceRead
= true;
1063 catch (const CommandAbortedException
&)
1065 // any command wasn't executed successfully - not specified
1066 SetError( ERRCODE_IO_GENERAL
);
1067 return COMMIT_RESULT_FAILURE
;
1069 catch (const RuntimeException
&)
1071 // any other error - not specified
1072 SetError( ERRCODE_IO_GENERAL
);
1073 return COMMIT_RESULT_FAILURE
;
1075 catch (const Exception
&)
1077 // any other error - not specified
1078 SetError( ERRCODE_IO_GENERAL
);
1079 return COMMIT_RESULT_FAILURE
;
1082 m_bCommited
= false;
1083 return COMMIT_RESULT_SUCCESS
;
1087 return COMMIT_RESULT_NOTHING_TO_DO
;
1090 void UCBStorageStream_Impl::Revert()
1092 // if an OLEStorage is created on this stream, no "revert" is necessary because OLEStorages do nothing on "Revert" !
1095 OSL_FAIL("Revert while commit is in progress!" );
1100 if ( !m_aTempURL
.isEmpty() )
1102 osl::File::remove(m_aTempURL
);
1106 m_bSourceRead
= false;
1109 m_rSource
= m_pContent
->openStream();
1110 if( m_rSource
.is() )
1112 if ( m_pAntiImpl
&& ( m_nMode
& StreamMode::TRUNC
) )
1113 // stream is in use and should be truncated
1114 m_bSourceRead
= false;
1117 m_nMode
&= ~StreamMode::TRUNC
;
1118 m_bSourceRead
= true;
1122 SetError( SVSTREAM_CANNOT_MAKE
);
1124 catch (const ContentCreationException
&)
1126 SetError( ERRCODE_IO_GENERAL
);
1128 catch (const RuntimeException
&)
1130 SetError( ERRCODE_IO_GENERAL
);
1132 catch (const Exception
&)
1136 m_bModified
= false;
1137 m_aName
= m_aOriginalName
;
1138 m_aContentType
= m_aOriginalContentType
;
1141 bool UCBStorageStream_Impl::Clear()
1143 bool bRet
= ( m_pAntiImpl
== nullptr );
1144 DBG_ASSERT( bRet
, "Removing used stream!" );
1153 void UCBStorageStream_Impl::Free()
1155 #if OSL_DEBUG_LEVEL > 0
1158 if ( !m_aTempURL
.isEmpty() )
1169 void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode
)
1171 bool isWritable
= bool( m_nMode
& StreamMode::WRITE
);
1174 // once stream was writable, never reset to readonly
1175 nMode
|= StreamMode::WRITE
;
1181 if ( nMode
& StreamMode::TRUNC
)
1183 m_bSourceRead
= false; // usually it should be 0 already but just in case...
1185 if ( !m_aTempURL
.isEmpty() )
1187 osl::File::remove(m_aTempURL
);
1193 UCBStorageStream::UCBStorageStream( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bRepair
, Reference
< XProgressHandler
> const & xProgress
)
1195 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1196 // to class UCBStorageStream !
1197 pImp
= new UCBStorageStream_Impl( rName
, nMode
, this, bDirect
, bRepair
, xProgress
);
1198 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1199 StorageBase::m_nMode
= pImp
->m_nMode
;
1202 UCBStorageStream::UCBStorageStream( UCBStorageStream_Impl
*pImpl
)
1205 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1206 pImp
->m_pAntiImpl
= this;
1207 SetError( pImp
->m_nError
);
1208 StorageBase::m_nMode
= pImp
->m_nMode
;
1211 UCBStorageStream::~UCBStorageStream()
1213 if ( pImp
->m_nMode
& StreamMode::WRITE
)
1215 pImp
->m_pAntiImpl
= nullptr;
1220 sal_uLong
UCBStorageStream::Read( void * pData
, sal_uLong nSize
)
1222 //return pImp->m_pStream->Read( pData, nSize );
1223 return pImp
->GetData( pData
, nSize
);
1226 sal_uLong
UCBStorageStream::Write( const void* pData
, sal_uLong nSize
)
1228 return pImp
->PutData( pData
, nSize
);
1231 sal_uInt64
UCBStorageStream::Seek( sal_uInt64 nPos
)
1233 //return pImp->m_pStream->Seek( nPos );
1234 return pImp
->Seek( nPos
);
1237 sal_uLong
UCBStorageStream::Tell()
1241 return pImp
->m_pStream
->Tell();
1244 void UCBStorageStream::Flush()
1246 // streams are never really transacted, so flush also means commit !
1250 bool UCBStorageStream::SetSize( sal_uLong nNewSize
)
1252 pImp
->SetSize( nNewSize
);
1253 return !pImp
->GetError();
1256 bool UCBStorageStream::Validate( bool bWrite
) const
1258 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
1261 bool UCBStorageStream::ValidateMode( StreamMode m
) const
1264 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
1266 if( ( m
& StreamMode::READWRITE
) == StreamMode::READ
)
1268 // only SHARE_DENYWRITE or SHARE_DENYALL allowed
1269 if( ( m
& StreamMode::SHARE_DENYWRITE
)
1270 || ( m
& StreamMode::SHARE_DENYALL
) )
1275 // only SHARE_DENYALL allowed
1276 // storages open in r/o mode are OK, since only
1277 // the commit may fail
1278 if( m
& StreamMode::SHARE_DENYALL
)
1285 SvStream
* UCBStorageStream::GetModifySvStream()
1287 return static_cast<SvStream
*>(pImp
);
1290 bool UCBStorageStream::Equals( const BaseStorageStream
& rStream
) const
1293 return static_cast<BaseStorageStream
const *>(this) == &rStream
;
1296 bool UCBStorageStream::Commit()
1298 // mark this stream for sending it on root commit
1303 void UCBStorageStream::CopyTo( BaseStorageStream
* pDestStm
)
1308 UCBStorageStream
* pStg
= dynamic_cast<UCBStorageStream
*>( pDestStm
);
1310 pStg
->pImp
->m_aContentType
= pImp
->m_aContentType
;
1312 pDestStm
->SetSize( 0 );
1313 Seek( STREAM_SEEK_TO_END
);
1314 sal_Int32 n
= Tell();
1318 if( pDestStm
->SetSize( n
) && n
)
1320 std::unique_ptr
<sal_uInt8
[]> p(new sal_uInt8
[ 4096 ]);
1322 pDestStm
->Seek( 0 );
1328 if( Read( p
.get(), nn
) != nn
)
1330 if( pDestStm
->Write( p
.get(), nn
) != nn
)
1337 bool UCBStorageStream::SetProperty( const OUString
& rName
, const css::uno::Any
& rValue
)
1339 if ( rName
== "Title")
1342 if ( rName
== "MediaType")
1346 pImp
->m_aContentType
= aTmp
;
1351 if ( pImp
->m_pContent
)
1353 pImp
->m_pContent
->setPropertyValue( rName
, rValue
);
1357 catch (const Exception
&)
1364 sal_uLong
UCBStorageStream::GetSize() const
1366 return pImp
->GetSize();
1369 UCBStorage::UCBStorage( SvStream
& rStrm
, bool bDirect
)
1371 OUString aURL
= GetLinkedFile( rStrm
);
1372 if ( !aURL
.isEmpty() )
1374 StreamMode nMode
= StreamMode::READ
;
1375 if( rStrm
.IsWritable() )
1376 nMode
= StreamMode::READ
| StreamMode::WRITE
;
1378 ::ucbhelper::Content
aContent( aURL
, Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
1379 pImp
= new UCBStorage_Impl( aContent
, aURL
, nMode
, this, bDirect
, true );
1383 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1384 // to class UCBStorage !
1385 pImp
= new UCBStorage_Impl( rStrm
, this, bDirect
);
1388 pImp
->AddFirstRef();
1390 StorageBase::m_nMode
= pImp
->m_nMode
;
1393 UCBStorage::UCBStorage( const ::ucbhelper::Content
& rContent
, const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1395 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1396 // to class UCBStorage !
1397 pImp
= new UCBStorage_Impl( rContent
, rName
, nMode
, this, bDirect
, bIsRoot
);
1398 pImp
->AddFirstRef();
1400 StorageBase::m_nMode
= pImp
->m_nMode
;
1403 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1405 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1406 // to class UCBStorage !
1407 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, bIsRepair
, xProgressHandler
);
1408 pImp
->AddFirstRef();
1410 StorageBase::m_nMode
= pImp
->m_nMode
;
1413 UCBStorage::UCBStorage( const OUString
& rName
, StreamMode nMode
, bool bDirect
, bool bIsRoot
)
1415 // pImp must be initialized in the body, because otherwise the vtable of the stream is not initialized
1416 // to class UCBStorage !
1417 pImp
= new UCBStorage_Impl( rName
, nMode
, this, bDirect
, bIsRoot
, false, Reference
< XProgressHandler
>() );
1418 pImp
->AddFirstRef();
1420 StorageBase::m_nMode
= pImp
->m_nMode
;
1423 UCBStorage::UCBStorage( UCBStorage_Impl
*pImpl
)
1426 pImp
->m_pAntiImpl
= this;
1427 SetError( pImp
->m_nError
);
1428 pImp
->AddFirstRef(); // use direct refcounting because in header file only a pointer should be used
1429 StorageBase::m_nMode
= pImp
->m_nMode
;
1432 UCBStorage::~UCBStorage()
1434 if ( pImp
->m_bIsRoot
&& pImp
->m_bDirect
&& ( !pImp
->m_pTempFile
|| pImp
->m_pSource
) )
1435 // DirectMode is simulated with an AutoCommit
1438 pImp
->m_pAntiImpl
= nullptr;
1442 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
)
1443 : m_pAntiImpl( pStorage
)
1444 , m_pContent( new ::ucbhelper::Content( rContent
) )
1445 , m_pTempFile( nullptr )
1446 , m_pSource( nullptr )
1447 //, m_pStream( NULL )
1448 , m_nError( ERRCODE_NONE
)
1450 , m_bCommited( false )
1451 , m_bDirect( bDirect
)
1452 , m_bIsRoot( bIsRoot
)
1453 , m_bIsLinked( true )
1454 , m_bListCreated( false )
1455 , m_nFormat( SotClipboardFormatId::NONE
)
1456 , m_aClassId( SvGlobalName() )
1457 , m_bRepairPackage( bIsRepair
)
1458 , m_xProgressHandler( xProgressHandler
)
1460 OUString
aName( rName
);
1461 if( aName
.isEmpty() )
1463 // no name given = use temporary name!
1464 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1465 m_pTempFile
= new ::utl::TempFile
;
1466 m_pTempFile
->EnableKillingFile();
1467 m_aName
= m_aOriginalName
= aName
= m_pTempFile
->GetURL();
1473 UCBStorage_Impl::UCBStorage_Impl( const OUString
& rName
, StreamMode nMode
, UCBStorage
* pStorage
, bool bDirect
, bool bIsRoot
, bool bIsRepair
, Reference
< XProgressHandler
> const & xProgressHandler
)
1474 : m_pAntiImpl( pStorage
)
1475 , m_pContent( nullptr )
1476 , m_pTempFile( nullptr )
1477 , m_pSource( nullptr )
1478 //, m_pStream( NULL )
1479 , m_nError( ERRCODE_NONE
)
1481 , m_bCommited( false )
1482 , m_bDirect( bDirect
)
1483 , m_bIsRoot( bIsRoot
)
1484 , m_bIsLinked( false )
1485 , m_bListCreated( false )
1486 , m_nFormat( SotClipboardFormatId::NONE
)
1487 , m_aClassId( SvGlobalName() )
1488 , m_bRepairPackage( bIsRepair
)
1489 , m_xProgressHandler( xProgressHandler
)
1491 OUString
aName( rName
);
1492 if( aName
.isEmpty() )
1494 // no name given = use temporary name!
1495 DBG_ASSERT( m_bIsRoot
, "SubStorage must have a name!" );
1496 m_pTempFile
= new ::utl::TempFile
;
1497 m_pTempFile
->EnableKillingFile();
1498 m_aName
= m_aOriginalName
= aName
= m_pTempFile
->GetURL();
1503 // create the special package URL for the package content
1504 OUString aTemp
= "vnd.sun.star.pkg://";
1505 aTemp
+= INetURLObject::encode( aName
, INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1508 if ( m_nMode
& StreamMode::WRITE
)
1510 // the root storage opens the package, so make sure that there is any
1511 ::utl::UcbStreamHelper::CreateStream( aName
, StreamMode::STD_READWRITE
, m_pTempFile
!= nullptr /* bFileExists */ );
1516 // substorages are opened like streams: the URL is a "child URL" of the root package URL
1518 if ( !m_aURL
.startsWith( "vnd.sun.star.pkg://") )
1523 UCBStorage_Impl::UCBStorage_Impl( SvStream
& rStream
, UCBStorage
* pStorage
, bool bDirect
)
1524 : m_pAntiImpl( pStorage
)
1525 , m_pContent( nullptr )
1526 , m_pTempFile( new ::utl::TempFile
)
1527 , m_pSource( &rStream
)
1528 , m_nError( ERRCODE_NONE
)
1529 , m_bCommited( false )
1530 , m_bDirect( bDirect
)
1532 , m_bIsLinked( false )
1533 , m_bListCreated( false )
1534 , m_nFormat( SotClipboardFormatId::NONE
)
1535 , m_aClassId( SvGlobalName() )
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 OUString aTemp
= "vnd.sun.star.pkg://";
1547 aTemp
+= INetURLObject::encode( m_pTempFile
->GetURL(), INetURLObject::PART_AUTHORITY
, INetURLObject::EncodeMechanism::All
);
1550 // copy data into the temporary file
1551 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READWRITE
, true /* bFileExists */ ));
1555 rStream
.ReadStream( *pStream
);
1560 // close stream and let content access the file
1563 // check opening mode
1564 m_nMode
= StreamMode::READ
;
1565 if( rStream
.IsWritable() )
1566 m_nMode
= StreamMode::READ
| StreamMode::WRITE
;
1569 void UCBStorage_Impl::Init()
1571 // name is last segment in URL
1572 INetURLObject
aObj( m_aURL
);
1573 if ( m_aName
.isEmpty() )
1574 // if the name was not already set to a temp name
1575 m_aName
= m_aOriginalName
= aObj
.GetLastName();
1587 if ( m_nError
== ERRCODE_NONE
)
1589 // read the manifest.xml file
1590 aObj
.Append( "META-INF" );
1591 aObj
.Append( "manifest.xml" );
1593 // create input stream
1594 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( aObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), StreamMode::STD_READ
));
1595 // no stream means no manifest.xml
1598 if ( !pStream
->GetError() )
1600 ::utl::OInputStreamWrapper
* pHelper
= new ::utl::OInputStreamWrapper( *pStream
);
1601 css::uno::Reference
< css::io::XInputStream
> xInputStream( pHelper
);
1603 // create a manifest reader object that will read in the manifest from the stream
1604 Reference
< css::packages::manifest::XManifestReader
> xReader
=
1605 css::packages::manifest::ManifestReader::create(
1606 ::comphelper::getProcessComponentContext() ) ;
1607 Sequence
< Sequence
< PropertyValue
> > aProps
= xReader
->readManifestSequence( xInputStream
);
1611 xInputStream
= nullptr;
1612 SetProps( aProps
, OUString() );
1622 // get the manifest information from the package
1624 Any aAny
= m_pContent
->getPropertyValue("MediaType");
1626 if ( ( aAny
>>= aTmp
) && !aTmp
.isEmpty() )
1627 m_aContentType
= m_aOriginalContentType
= aTmp
;
1629 catch (const Exception
&)
1632 "getPropertyValue has thrown an exception! Please let developers know the scenario!" );
1637 if ( !m_aContentType
.isEmpty() )
1639 // get the clipboard format using the content type
1640 css::datatransfer::DataFlavor aDataFlavor
;
1641 aDataFlavor
.MimeType
= m_aContentType
;
1642 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1644 // get the ClassId using the clipboard format ( internal table )
1645 m_aClassId
= GetClassId_Impl( m_nFormat
);
1647 // get human presentable name using the clipboard format
1648 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1649 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1651 if( m_pContent
&& !m_bIsLinked
&& m_aClassId
!= SvGlobalName() )
1656 void UCBStorage_Impl::CreateContent()
1660 // create content; where to put StreamMode ?! ( already done when opening the file of the package ? )
1661 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1663 OUString
aTemp( m_aURL
);
1665 if ( m_bRepairPackage
)
1667 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1668 m_xProgressHandler
);
1669 aTemp
+= "?repairpackage";
1672 m_pContent
= new ::ucbhelper::Content( aTemp
, xComEnv
, comphelper::getProcessComponentContext() );
1674 catch (const ContentCreationException
&)
1676 // content could not be created
1677 SetError( SVSTREAM_CANNOT_MAKE
);
1679 catch (const RuntimeException
&)
1681 // any other error - not specified
1682 SetError( SVSTREAM_CANNOT_MAKE
);
1686 void UCBStorage_Impl::ReadContent()
1688 if ( m_bListCreated
)
1691 m_bListCreated
= true;
1693 // create cursor for access to children
1694 Sequence
< OUString
> aProps(4);
1695 aProps
[0] = "Title";
1696 aProps
[1] = "IsFolder";
1697 aProps
[2] = "MediaType";
1706 Reference
< XResultSet
> xResultSet
= m_pContent
->createCursor( aProps
, ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS
);
1707 Reference
< XRow
> xRow( xResultSet
, UNO_QUERY
);
1708 if ( xResultSet
.is() )
1710 while ( xResultSet
->next() )
1712 // insert all into the children list
1713 OUString
aTitle( xRow
->getString(1) );
1716 // unpacked storages have to deal with the meta-inf folder by themselves
1717 if ( aTitle
== "META-INF" )
1721 bool bIsFolder( xRow
->getBoolean(2) );
1722 sal_Int64 nSize
= xRow
->getLong(4);
1723 UCBStorageElement_Impl
* pElement
= new UCBStorageElement_Impl( aTitle
, bIsFolder
, static_cast<sal_uLong
>(nSize
) );
1724 m_aChildrenList
.emplace_back( pElement
);
1726 bool bIsOfficeDocument
= m_bIsLinked
|| ( m_aClassId
!= SvGlobalName() );
1730 OpenStorage( pElement
, m_nMode
, m_bDirect
);
1731 if ( pElement
->m_xStorage
.is() )
1732 pElement
->m_xStorage
->Init();
1734 else if ( bIsOfficeDocument
)
1736 // streams can be external OLE objects, so they are now folders, but storages!
1737 OUString
aName( m_aURL
+ "/" + xRow
->getString(1));
1739 Reference
< css::ucb::XCommandEnvironment
> xComEnv
;
1740 if ( m_bRepairPackage
)
1742 xComEnv
= new ::ucbhelper::CommandEnvironment( Reference
< css::task::XInteractionHandler
>(),
1743 m_xProgressHandler
);
1744 aName
+= "?repairpackage";
1747 ::ucbhelper::Content
aContent( aName
, xComEnv
, comphelper::getProcessComponentContext() );
1749 OUString aMediaType
;
1750 Any aAny
= aContent
.getPropertyValue("MediaType");
1751 if ( ( aAny
>>= aMediaType
) && ( aMediaType
== "application/vnd.sun.star.oleobject" ) )
1752 pElement
->m_bIsStorage
= true;
1753 else if ( aMediaType
.isEmpty() )
1755 // older files didn't have that special content type, so they must be detected
1756 OpenStream( pElement
, StreamMode::STD_READ
, m_bDirect
);
1757 if ( Storage::IsStorageFile( pElement
->m_xStream
.get() ) )
1758 pElement
->m_bIsStorage
= true;
1760 pElement
->m_xStream
->Free();
1766 catch (const InteractiveIOException
& r
)
1768 if ( r
.Code
!= IOErrorCode_NOT_EXISTING
)
1769 SetError( ERRCODE_IO_GENERAL
);
1771 catch (const CommandAbortedException
&)
1773 // any command wasn't executed successfully - not specified
1774 if ( !( m_nMode
& StreamMode::WRITE
) )
1775 // if the folder was just inserted and not already committed, this is not an error!
1776 SetError( ERRCODE_IO_GENERAL
);
1778 catch (const RuntimeException
&)
1780 // any other error - not specified
1781 SetError( ERRCODE_IO_GENERAL
);
1783 catch (const ResultSetException
&)
1785 // means that the package file is broken
1786 SetError( ERRCODE_IO_BROKENPACKAGE
);
1788 catch (const SQLException
&)
1790 // means that the file can be broken
1791 SetError( ERRCODE_IO_WRONGFORMAT
);
1793 catch (const Exception
&)
1795 // any other error - not specified
1796 SetError( ERRCODE_IO_GENERAL
);
1800 void UCBStorage_Impl::SetError( ErrCode nError
)
1805 if ( m_pAntiImpl
) m_pAntiImpl
->SetError( nError
);
1809 sal_Int32
UCBStorage_Impl::GetObjectCount()
1811 sal_Int32 nCount
= m_aChildrenList
.size();
1812 for (auto& pElement
: m_aChildrenList
)
1814 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1815 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1816 nCount
+= pElement
->m_xStorage
->GetObjectCount();
1822 static OUString
Find_Impl( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1824 bool bFound
= false;
1825 for ( sal_Int32 nSeqs
=0; nSeqs
<rSequence
.getLength(); nSeqs
++ )
1827 const Sequence
< PropertyValue
>& rMyProps
= rSequence
[nSeqs
];
1830 for ( sal_Int32 nProps
=0; nProps
<rMyProps
.getLength(); nProps
++ )
1832 const PropertyValue
& rAny
= rMyProps
[nProps
];
1833 if ( rAny
.Name
== "FullPath" )
1836 if ( ( rAny
.Value
>>= aTmp
) && aTmp
== rPath
)
1838 if ( !aType
.isEmpty() )
1841 else if ( rAny
.Name
== "MediaType" )
1843 if ( ( rAny
.Value
>>= aType
) && !aType
.isEmpty() && bFound
)
1855 void UCBStorage_Impl::SetProps( const Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1857 OUString
aPath( rPath
);
1862 m_aContentType
= m_aOriginalContentType
= Find_Impl( rSequence
, aPath
);
1865 // the "FullPath" of a child always starts without '/'
1868 for (auto& pElement
: m_aChildrenList
)
1870 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1871 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1872 pElement
->m_xStorage
->SetProps( rSequence
, aPath
);
1875 OUString
aElementPath( aPath
);
1876 aElementPath
+= pElement
->m_aName
;
1877 pElement
->SetContentType( Find_Impl( rSequence
, aElementPath
) );
1881 if ( !m_aContentType
.isEmpty() )
1883 // get the clipboard format using the content type
1884 css::datatransfer::DataFlavor aDataFlavor
;
1885 aDataFlavor
.MimeType
= m_aContentType
;
1886 m_nFormat
= SotExchange::GetFormat( aDataFlavor
);
1888 // get the ClassId using the clipboard format ( internal table )
1889 m_aClassId
= GetClassId_Impl( m_nFormat
);
1891 // get human presentable name using the clipboard format
1892 SotExchange::GetFormatDataFlavor( m_nFormat
, aDataFlavor
);
1893 m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
1897 void UCBStorage_Impl::GetProps( sal_Int32
& nProps
, Sequence
< Sequence
< PropertyValue
> >& rSequence
, const OUString
& rPath
)
1899 // first my own properties
1900 Sequence
< PropertyValue
> aProps(2);
1902 // first property is the "FullPath" name
1903 // it's '/' for the root storage and m_aName for each element, followed by a '/' if it's a folder
1904 OUString
aPath( rPath
);
1908 aProps
[0].Name
= "MediaType";
1909 aProps
[0].Value
<<= m_aContentType
;
1910 aProps
[1].Name
= "FullPath";
1911 aProps
[1].Value
<<= aPath
;
1912 rSequence
[ nProps
++ ] = aProps
;
1915 // the "FullPath" of a child always starts without '/'
1918 // now the properties of my elements
1919 for (auto& pElement
: m_aChildrenList
)
1921 DBG_ASSERT( !pElement
->m_bIsFolder
|| pElement
->m_xStorage
.is(), "Storage should be open!" );
1922 if ( pElement
->m_bIsFolder
&& pElement
->m_xStorage
.is() )
1923 // storages add there properties by themselves ( see above )
1924 pElement
->m_xStorage
->GetProps( nProps
, rSequence
, aPath
);
1927 // properties of streams
1928 OUString
aElementPath( aPath
);
1929 aElementPath
+= pElement
->m_aName
;
1930 aProps
[0].Name
= "MediaType";
1931 aProps
[0].Value
<<= pElement
->GetContentType();
1932 aProps
[1].Name
= "FullPath";
1933 aProps
[1].Value
<<= aElementPath
;
1934 rSequence
[ nProps
++ ] = aProps
;
1939 UCBStorage_Impl::~UCBStorage_Impl()
1941 m_aChildrenList
.clear();
1947 bool UCBStorage_Impl::Insert( ::ucbhelper::Content
*pContent
)
1949 // a new substorage is inserted into a UCBStorage ( given by the parameter pContent )
1950 // it must be inserted with a title and a type
1955 Sequence
< ContentInfo
> aInfo
= pContent
->queryCreatableContentsInfo();
1956 sal_Int32 nCount
= aInfo
.getLength();
1960 for ( sal_Int32 i
= 0; i
< nCount
; ++i
)
1962 // Simply look for the first KIND_FOLDER...
1963 const ContentInfo
& rCurr
= aInfo
[i
];
1964 if ( rCurr
.Attributes
& ContentInfoAttribute::KIND_FOLDER
)
1966 // Make sure the only required bootstrap property is "Title",
1967 const Sequence
< Property
> & rProps
= rCurr
.Properties
;
1968 if ( rProps
.getLength() != 1 )
1971 if ( rProps
[ 0 ].Name
!= "Title" )
1974 Sequence
< OUString
> aNames
{ "Title" };
1975 Sequence
< Any
> aValues(1);
1976 aValues
[0] <<= m_aName
;
1979 if ( !pContent
->insertNewContent( rCurr
.Type
, aNames
, aValues
, aNewFolder
) )
1982 // remove old content, create an "empty" new one and initialize it with the new inserted
1983 DELETEZ( m_pContent
);
1984 m_pContent
= new ::ucbhelper::Content( aNewFolder
);
1989 catch (const CommandAbortedException
&)
1991 // any command wasn't executed successfully - not specified
1992 SetError( ERRCODE_IO_GENERAL
);
1994 catch (const RuntimeException
&)
1996 // any other error - not specified
1997 SetError( ERRCODE_IO_GENERAL
);
1999 catch (const Exception
&)
2001 // any other error - not specified
2002 SetError( ERRCODE_IO_GENERAL
);
2008 sal_Int16
UCBStorage_Impl::Commit()
2010 // send all changes to the package
2011 sal_Int16 nRet
= COMMIT_RESULT_NOTHING_TO_DO
;
2013 // there is nothing to do if the storage has been opened readonly or if it was opened in transacted mode and no
2014 // commit command has been sent
2015 if ( ( m_nMode
& StreamMode::WRITE
) && ( m_bCommited
|| m_bDirect
) )
2019 // all errors will be caught in the "catch" statement outside the loop
2020 for ( size_t i
= 0; i
< m_aChildrenList
.size() && nRet
; ++i
)
2022 auto& pElement
= m_aChildrenList
[ i
];
2023 ::ucbhelper::Content
* pContent
= pElement
->GetContent();
2024 std::unique_ptr
< ::ucbhelper::Content
> xDeleteContent
;
2025 if ( !pContent
&& pElement
->IsModified() )
2027 // if the element has never been opened, no content has been created until now
2028 OUString
aName( m_aURL
);
2030 aName
+= pElement
->m_aOriginalName
;
2031 pContent
= new ::ucbhelper::Content( aName
, Reference
< css::ucb::XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2032 xDeleteContent
.reset(pContent
); // delete it later on exit scope
2035 if ( pElement
->m_bIsRemoved
)
2037 // was it inserted, then removed (so there would be nothing to do!)
2038 if ( !pElement
->m_bIsInserted
)
2040 // first remove all open stream handles
2041 if (pContent
&& (!pElement
->m_xStream
.is() || pElement
->m_xStream
->Clear()))
2043 pContent
->executeCommand( "delete", makeAny( true ) );
2044 nRet
= COMMIT_RESULT_SUCCESS
;
2047 // couldn't release stream because there are external references to it
2048 nRet
= COMMIT_RESULT_FAILURE
;
2053 sal_Int16 nLocalRet
= COMMIT_RESULT_NOTHING_TO_DO
;
2054 if ( pElement
->m_xStorage
.is() )
2056 // element is a storage
2057 // do a commit in the following cases:
2058 // - if storage is already inserted, and changed
2059 // - storage is not in a package
2060 // - it's a new storage, try to insert and commit if successful inserted
2061 if ( !pElement
->m_bIsInserted
|| m_bIsLinked
|| pElement
->m_xStorage
->Insert( m_pContent
) )
2063 nLocalRet
= pElement
->m_xStorage
->Commit();
2064 pContent
= pElement
->GetContent();
2067 else if ( pElement
->m_xStream
.is() )
2069 // element is a stream
2070 nLocalRet
= pElement
->m_xStream
->Commit();
2071 if ( pElement
->m_xStream
->m_bIsOLEStorage
)
2073 // OLE storage should be stored encrypted, if the storage uses encryption
2074 pElement
->m_xStream
->m_aContentType
= "application/vnd.sun.star.oleobject";
2077 pElement
->m_xStream
->m_pContent
->setPropertyValue("Encrypted", aValue
);
2080 pContent
= pElement
->GetContent();
2083 if (pContent
&& pElement
->m_aName
!= pElement
->m_aOriginalName
)
2085 // name ( title ) of the element was changed
2086 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2087 pContent
->setPropertyValue("Title", Any(pElement
->m_aName
) );
2090 if (pContent
&& pElement
->IsLoaded() && pElement
->GetContentType() != pElement
->GetOriginalContentType())
2092 // mediatype of the element was changed
2093 nLocalRet
= COMMIT_RESULT_SUCCESS
;
2094 pContent
->setPropertyValue("MediaType", Any(pElement
->GetContentType()) );
2097 if ( nLocalRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2101 if ( nRet
== COMMIT_RESULT_FAILURE
)
2105 catch (const ContentCreationException
&)
2107 // content could not be created
2108 SetError( ERRCODE_IO_NOTEXISTS
);
2109 return COMMIT_RESULT_FAILURE
;
2111 catch (const CommandAbortedException
&)
2113 // any command wasn't executed successfully - not specified
2114 SetError( ERRCODE_IO_GENERAL
);
2115 return COMMIT_RESULT_FAILURE
;
2117 catch (const RuntimeException
&)
2119 // any other error - not specified
2120 SetError( ERRCODE_IO_GENERAL
);
2121 return COMMIT_RESULT_FAILURE
;
2123 catch (const Exception
&)
2125 // any other error - not specified
2126 SetError( ERRCODE_IO_GENERAL
);
2127 return COMMIT_RESULT_FAILURE
;
2130 if ( m_bIsRoot
&& m_pContent
)
2132 // the root storage must flush the root package content
2133 if ( nRet
== COMMIT_RESULT_SUCCESS
)
2137 // commit the media type to the JAR file
2138 // clipboard format and ClassId will be retrieved from the media type when the file is loaded again
2140 aType
<<= m_aContentType
;
2141 m_pContent
->setPropertyValue("MediaType", aType
);
2145 // write a manifest file
2146 // first create a subfolder "META-inf"
2147 Content aNewSubFolder
;
2148 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_pContent
, "META-INF", aNewSubFolder
);
2151 // create a stream to write the manifest file - use a temp file
2152 OUString
aURL( aNewSubFolder
.getURL() );
2153 std::unique_ptr
< ::utl::TempFile
> pTempFile(new ::utl::TempFile( &aURL
));
2155 // get the stream from the temp file and create an output stream wrapper
2156 SvStream
* pStream
= pTempFile
->GetStream( StreamMode::STD_READWRITE
);
2157 ::utl::OOutputStreamWrapper
* pHelper
= new ::utl::OOutputStreamWrapper( *pStream
);
2158 css::uno::Reference
< css::io::XOutputStream
> xOutputStream( pHelper
);
2160 // create a manifest writer object that will fill the stream
2161 Reference
< css::packages::manifest::XManifestWriter
> xWriter
=
2162 css::packages::manifest::ManifestWriter::create(
2163 ::comphelper::getProcessComponentContext() );
2164 sal_Int32 nCount
= GetObjectCount() + 1;
2165 Sequence
< Sequence
< PropertyValue
> > aProps( nCount
);
2166 sal_Int32 nProps
= 0;
2167 GetProps( nProps
, aProps
, OUString() );
2168 xWriter
->writeManifestSequence( xOutputStream
, aProps
);
2170 // move the stream to its desired location
2171 Content
aSource( pTempFile
->GetURL(), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2173 xOutputStream
= nullptr;
2175 aNewSubFolder
.transferContent( aSource
, InsertOperation::Move
, "manifest.xml", NameClash::OVERWRITE
);
2180 #if OSL_DEBUG_LEVEL > 0
2181 SAL_INFO("sot", "Files: " << nOpenFiles
);
2182 SAL_INFO("sot", "Streams: " << nOpenStreams
);
2186 m_pContent
->executeCommand( "flush", aAny
);
2187 if ( m_pSource
!= nullptr )
2189 std::unique_ptr
<SvStream
> pStream(::utl::UcbStreamHelper::CreateStream( m_pTempFile
->GetURL(), StreamMode::STD_READ
));
2190 m_pSource
->SetStreamSize(0);
2191 // m_pSource->Seek(0);
2192 pStream
->ReadStream( *m_pSource
);
2198 catch (const CommandAbortedException
&)
2200 // how to tell the content : forget all changes ?!
2201 // or should we assume that the content does it by itself because he threw an exception ?!
2202 // any command wasn't executed successfully - not specified
2203 SetError( ERRCODE_IO_GENERAL
);
2204 return COMMIT_RESULT_FAILURE
;
2206 catch (const RuntimeException
&)
2208 // how to tell the content : forget all changes ?!
2209 // or should we assume that the content does it by itself because he threw an exception ?!
2210 // any other error - not specified
2211 SetError( ERRCODE_IO_GENERAL
);
2212 return COMMIT_RESULT_FAILURE
;
2214 catch (const InteractiveIOException
& r
)
2216 if ( r
.Code
== IOErrorCode_ACCESS_DENIED
|| r
.Code
== IOErrorCode_LOCKING_VIOLATION
)
2217 SetError( ERRCODE_IO_ACCESSDENIED
);
2218 else if ( r
.Code
== IOErrorCode_NOT_EXISTING
)
2219 SetError( ERRCODE_IO_NOTEXISTS
);
2220 else if ( r
.Code
== IOErrorCode_CANT_READ
)
2221 SetError( ERRCODE_IO_CANTREAD
);
2222 else if ( r
.Code
== IOErrorCode_CANT_WRITE
)
2223 SetError( ERRCODE_IO_CANTWRITE
);
2225 SetError( ERRCODE_IO_GENERAL
);
2227 return COMMIT_RESULT_FAILURE
;
2229 catch (const Exception
&)
2231 // how to tell the content : forget all changes ?!
2232 // or should we assume that the content does it by itself because he threw an exception ?!
2233 // any other error - not specified
2234 SetError( ERRCODE_IO_GENERAL
);
2235 return COMMIT_RESULT_FAILURE
;
2238 else if ( nRet
!= COMMIT_RESULT_NOTHING_TO_DO
)
2240 // how to tell the content : forget all changes ?! Should we ?!
2241 SetError( ERRCODE_IO_GENERAL
);
2245 // after successful root commit all elements names and types are adjusted and all removed elements
2246 // are also removed from the lists
2247 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2249 auto& pInnerElement
= m_aChildrenList
[ i
];
2250 if ( pInnerElement
->m_bIsRemoved
)
2251 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2254 pInnerElement
->m_aOriginalName
= pInnerElement
->m_aName
;
2255 pInnerElement
->m_bIsInserted
= false;
2261 m_bCommited
= false;
2267 void UCBStorage_Impl::Revert()
2269 for ( size_t i
= 0; i
< m_aChildrenList
.size(); )
2271 auto& pElement
= m_aChildrenList
[ i
];
2272 pElement
->m_bIsRemoved
= false;
2273 if ( pElement
->m_bIsInserted
)
2274 m_aChildrenList
.erase( m_aChildrenList
.begin() + i
);
2277 if ( pElement
->m_xStream
.is() )
2279 pElement
->m_xStream
->m_bCommited
= false;
2280 pElement
->m_xStream
->Revert();
2282 else if ( pElement
->m_xStorage
.is() )
2284 pElement
->m_xStorage
->m_bCommited
= false;
2285 pElement
->m_xStorage
->Revert();
2288 pElement
->m_aName
= pElement
->m_aOriginalName
;
2289 pElement
->m_bIsRemoved
= false;
2295 const OUString
& UCBStorage::GetName() const
2297 return pImp
->m_aName
; // pImp->m_aURL ?!
2300 bool UCBStorage::IsRoot() const
2302 return pImp
->m_bIsRoot
;
2305 void UCBStorage::SetDirty()
2309 void UCBStorage::SetClass( const SvGlobalName
& rClass
, SotClipboardFormatId nOriginalClipFormat
, const OUString
& rUserTypeName
)
2311 pImp
->m_aClassId
= rClass
;
2312 pImp
->m_nFormat
= nOriginalClipFormat
;
2313 pImp
->m_aUserTypeName
= rUserTypeName
;
2315 // in UCB storages only the content type will be stored, all other information can be reconstructed
2316 // ( see the UCBStorage_Impl::Init() method )
2317 css::datatransfer::DataFlavor aDataFlavor
;
2318 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2319 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2322 void UCBStorage::SetClassId( const ClsId
& rClsId
)
2324 pImp
->m_aClassId
= SvGlobalName( rClsId
);
2325 if ( pImp
->m_aClassId
== SvGlobalName() )
2328 // in OLE storages the clipboard format an the user name will be transferred when a storage is copied because both are
2329 // stored in one the substreams
2330 // UCB storages store the content type information as content type in the manifest file and so this information must be
2331 // kept up to date, and also the other type information that is hold only at runtime because it can be reconstructed from
2333 pImp
->m_nFormat
= GetFormatId_Impl( pImp
->m_aClassId
);
2334 if ( pImp
->m_nFormat
!= SotClipboardFormatId::NONE
)
2336 css::datatransfer::DataFlavor aDataFlavor
;
2337 SotExchange::GetFormatDataFlavor( pImp
->m_nFormat
, aDataFlavor
);
2338 pImp
->m_aUserTypeName
= aDataFlavor
.HumanPresentableName
;
2339 pImp
->m_aContentType
= aDataFlavor
.MimeType
;
2343 const ClsId
& UCBStorage::GetClassId() const
2345 return pImp
->m_aClassId
.GetCLSID();
2348 SvGlobalName
UCBStorage::GetClassName()
2350 return pImp
->m_aClassId
;
2353 SotClipboardFormatId
UCBStorage::GetFormat()
2355 return pImp
->m_nFormat
;
2358 OUString
UCBStorage::GetUserName()
2360 OSL_FAIL("UserName is not implemented in UCB storages!" );
2361 return pImp
->m_aUserTypeName
;
2364 void UCBStorage::FillInfoList( SvStorageInfoList
* pList
) const
2366 // put information in childrenlist into StorageInfoList
2367 for (auto& pElement
: pImp
->GetChildrenList())
2369 if ( !pElement
->m_bIsRemoved
)
2371 // problem: what about the size of a substorage ?!
2372 sal_uLong nSize
= pElement
->m_nSize
;
2373 if ( pElement
->m_xStream
.is() )
2374 nSize
= pElement
->m_xStream
->GetSize();
2375 SvStorageInfo
aInfo( pElement
->m_aName
, nSize
, pElement
->m_bIsStorage
);
2376 pList
->push_back( aInfo
);
2381 bool UCBStorage::CopyStorageElement_Impl( UCBStorageElement_Impl
const & rElement
, BaseStorage
* pDest
, const OUString
& rNew
) const
2383 // insert stream or storage into the list or stream of the destination storage
2384 // not into the content, this will be done on commit !
2385 // be aware of name changes !
2386 if ( !rElement
.m_bIsStorage
)
2388 // copy the streams data
2389 // the destination stream must not be open
2390 tools::SvRef
<BaseStorageStream
> pOtherStream(pDest
->OpenStream( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2391 BaseStorageStream
* pStream
= nullptr;
2392 bool bDeleteStream
= false;
2394 // if stream is already open, it is allowed to copy it, so be aware of this
2395 if ( rElement
.m_xStream
.is() )
2396 pStream
= rElement
.m_xStream
->m_pAntiImpl
;
2399 pStream
= const_cast< UCBStorage
* >(this)->OpenStream( rElement
.m_aName
, StreamMode::STD_READ
, pImp
->m_bDirect
);
2400 bDeleteStream
= true;
2403 pStream
->CopyTo( pOtherStream
.get() );
2404 SetError( pStream
->GetError() );
2405 if( pOtherStream
->GetError() )
2406 pDest
->SetError( pOtherStream
->GetError() );
2408 pOtherStream
->Commit();
2410 if ( bDeleteStream
)
2415 // copy the storage content
2416 // the destination storage must not be open
2417 BaseStorage
* pStorage
= nullptr;
2419 // if stream is already open, it is allowed to copy it, so be aware of this
2420 bool bDeleteStorage
= false;
2421 if ( rElement
.m_xStorage
.is() )
2422 pStorage
= rElement
.m_xStorage
->m_pAntiImpl
;
2425 pStorage
= const_cast<UCBStorage
*>(this)->OpenStorage( rElement
.m_aName
, pImp
->m_nMode
, pImp
->m_bDirect
);
2426 bDeleteStorage
= true;
2429 UCBStorage
* pUCBDest
= dynamic_cast<UCBStorage
*>( pDest
);
2430 UCBStorage
* pUCBCopy
= dynamic_cast<UCBStorage
*>( pStorage
);
2432 bool bOpenUCBStorage
= pUCBDest
&& pUCBCopy
;
2433 tools::SvRef
<BaseStorage
> pOtherStorage(bOpenUCBStorage
?
2434 pDest
->OpenUCBStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
) :
2435 pDest
->OpenOLEStorage( rNew
, StreamMode::WRITE
| StreamMode::SHARE_DENYALL
, pImp
->m_bDirect
));
2437 // For UCB storages, the class id and the format id may differ,
2438 // do passing the class id is not sufficient.
2439 if( bOpenUCBStorage
)
2440 pOtherStorage
->SetClass( pStorage
->GetClassName(),
2441 pStorage
->GetFormat(),
2442 pUCBCopy
->pImp
->m_aUserTypeName
);
2444 pOtherStorage
->SetClassId( pStorage
->GetClassId() );
2445 pStorage
->CopyTo( pOtherStorage
.get() );
2446 SetError( pStorage
->GetError() );
2447 if( pOtherStorage
->GetError() )
2448 pDest
->SetError( pOtherStorage
->GetError() );
2450 pOtherStorage
->Commit();
2452 if ( bDeleteStorage
)
2456 return Good() && pDest
->Good();
2459 UCBStorageElement_Impl
* UCBStorage::FindElement_Impl( const OUString
& rName
) const
2461 DBG_ASSERT( !rName
.isEmpty(), "Name is empty!" );
2462 for (auto& pElement
: pImp
->GetChildrenList())
2464 if ( pElement
->m_aName
== rName
&& !pElement
->m_bIsRemoved
)
2465 return pElement
.get();
2470 bool UCBStorage::CopyTo( BaseStorage
* pDestStg
) const
2472 DBG_ASSERT( pDestStg
!= static_cast<BaseStorage
const *>(this), "Self-Copying is not possible!" );
2473 if ( pDestStg
== static_cast<BaseStorage
const *>(this) )
2476 // perhaps it's also a problem if one storage is a parent of the other ?!
2477 // or if not: could be optimized ?!
2479 // For UCB storages, the class id and the format id may differ,
2480 // do passing the class id is not sufficient.
2481 if( dynamic_cast<const UCBStorage
*>(pDestStg
) != nullptr )
2482 pDestStg
->SetClass( pImp
->m_aClassId
, pImp
->m_nFormat
,
2483 pImp
->m_aUserTypeName
);
2485 pDestStg
->SetClassId( GetClassId() );
2486 pDestStg
->SetDirty();
2489 for ( size_t i
= 0; i
< pImp
->GetChildrenList().size() && bRet
; ++i
)
2491 auto& pElement
= pImp
->GetChildrenList()[ i
];
2492 if ( !pElement
->m_bIsRemoved
)
2493 bRet
= CopyStorageElement_Impl( *pElement
, pDestStg
, pElement
->m_aName
);
2497 SetError( pDestStg
->GetError() );
2498 return Good() && pDestStg
->Good();
2501 bool UCBStorage::CopyTo( const OUString
& rElemName
, BaseStorage
* pDest
, const OUString
& rNew
)
2503 if( rElemName
.isEmpty() )
2506 if ( pDest
== static_cast<BaseStorage
*>(this) )
2508 // can't double an element
2513 // for copying no optimization is useful, because in every case the stream data must be copied
2514 UCBStorageElement_Impl
* pElement
= FindElement_Impl( rElemName
);
2516 return CopyStorageElement_Impl( *pElement
, pDest
, rNew
);
2519 SetError( SVSTREAM_FILE_NOT_FOUND
);
2525 bool UCBStorage::Commit()
2527 // mark this storage for sending it on root commit
2528 pImp
->m_bCommited
= true;
2529 if ( pImp
->m_bIsRoot
)
2530 // the root storage coordinates committing by sending a Commit command to its content
2531 return ( pImp
->Commit() != COMMIT_RESULT_FAILURE
);
2536 bool UCBStorage::Revert()
2542 BaseStorageStream
* UCBStorage::OpenStream( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2544 if( rEleName
.isEmpty() )
2547 // try to find the storage element
2548 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2551 // element does not exist, check if creation is allowed
2552 if( nMode
& StreamMode::NOCREATE
)
2554 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2555 OUString
aName( pImp
->m_aURL
);
2558 UCBStorageStream
* pStream
= new UCBStorageStream( aName
, nMode
, bDirect
, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2559 pStream
->SetError( GetError() );
2560 pStream
->pImp
->m_aName
= rEleName
;
2565 // create a new UCBStorageElement and insert it into the list
2566 pElement
= new UCBStorageElement_Impl( rEleName
);
2567 pElement
->m_bIsInserted
= true;
2568 pImp
->m_aChildrenList
.emplace_back( pElement
);
2572 if ( !pElement
->m_bIsFolder
)
2574 // check if stream is already created
2575 if ( pElement
->m_xStream
.is() )
2577 // stream has already been created; if it has no external reference, it may be opened another time
2578 if ( pElement
->m_xStream
->m_pAntiImpl
)
2580 OSL_FAIL("Stream is already open!" );
2581 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2586 // check if stream is opened with the same keyword as before
2587 // if not, generate a new stream because it could be encrypted vs. decrypted!
2588 if ( pElement
->m_xStream
->m_aKey
.isEmpty() )
2590 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2592 return new UCBStorageStream( pElement
->m_xStream
.get() );
2597 // stream is opened the first time
2598 pImp
->OpenStream( pElement
, nMode
, bDirect
);
2600 // if name has been changed before creating the stream: set name!
2601 pElement
->m_xStream
->m_aName
= rEleName
;
2602 return new UCBStorageStream( pElement
->m_xStream
.get() );
2608 void UCBStorage_Impl::OpenStream( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2610 OUString
aName( m_aURL
);
2612 aName
+= pElement
->m_aOriginalName
;
2613 pElement
->m_xStream
= new UCBStorageStream_Impl( aName
, nMode
, nullptr, bDirect
, m_bRepairPackage
, m_xProgressHandler
);
2616 BaseStorage
* UCBStorage::OpenUCBStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2618 if( rEleName
.isEmpty() )
2621 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2624 BaseStorage
* UCBStorage::OpenOLEStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2626 if( rEleName
.isEmpty() )
2629 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, false );
2632 BaseStorage
* UCBStorage::OpenStorage( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
)
2634 if( rEleName
.isEmpty() )
2637 return OpenStorage_Impl( rEleName
, nMode
, bDirect
, true );
2640 BaseStorage
* UCBStorage::OpenStorage_Impl( const OUString
& rEleName
, StreamMode nMode
, bool bDirect
, bool bForceUCBStorage
)
2642 // try to find the storage element
2643 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2646 // element does not exist, check if creation is allowed
2647 if( nMode
& StreamMode::NOCREATE
)
2649 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2650 OUString
aName( pImp
->m_aURL
);
2652 aName
+= rEleName
; // ???
2653 UCBStorage
*pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2654 pStorage
->pImp
->m_bIsRoot
= false;
2655 pStorage
->pImp
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2656 pStorage
->SetError( GetError() );
2660 // create a new UCBStorageElement and insert it into the list
2661 // problem: perhaps an OLEStorage should be created ?!
2662 // Because nothing is known about the element that should be created, an external parameter is needed !
2663 pElement
= new UCBStorageElement_Impl( rEleName
);
2664 pElement
->m_bIsInserted
= true;
2665 pImp
->m_aChildrenList
.emplace_back( pElement
);
2668 if ( !pElement
->m_bIsFolder
&& ( pElement
->m_bIsStorage
|| !bForceUCBStorage
) )
2670 // create OLE storages on a stream ( see ctor of SotStorage )
2671 // Such a storage will be created on a UCBStorageStream; it will write into the stream
2672 // if it is opened in direct mode or when it is committed. In this case the stream will be
2673 // modified and then it MUST be treated as committed.
2674 if ( !pElement
->m_xStream
.is() )
2676 BaseStorageStream
* pStr
= OpenStream( rEleName
, nMode
, bDirect
);
2677 UCBStorageStream
* pStream
= dynamic_cast<UCBStorageStream
*>( pStr
);
2680 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2684 pElement
->m_xStream
= pStream
->pImp
;
2688 pElement
->m_xStream
->PrepareCachedForReopen( nMode
);
2689 bool bInited
= pElement
->m_xStream
->Init();
2692 SetError( ( nMode
& StreamMode::WRITE
) ? SVSTREAM_CANNOT_MAKE
: SVSTREAM_FILE_NOT_FOUND
);
2696 pElement
->m_bIsStorage
= true;
2697 return pElement
->m_xStream
->CreateStorage(); // can only be created in transacted mode
2699 else if ( pElement
->m_xStorage
.is() )
2701 // storage has already been opened; if it has no external reference, it may be opened another time
2702 if ( pElement
->m_xStorage
->m_pAntiImpl
)
2704 OSL_FAIL("Storage is already open!" );
2705 SetError( SVSTREAM_ACCESS_DENIED
); // ???
2709 bool bIsWritable
= bool( pElement
->m_xStorage
->m_nMode
& StreamMode::WRITE
);
2710 if ( !bIsWritable
&& ( nMode
& StreamMode::WRITE
) )
2712 OUString
aName( pImp
->m_aURL
);
2714 aName
+= pElement
->m_aOriginalName
;
2715 UCBStorage
* pStorage
= new UCBStorage( aName
, nMode
, bDirect
, false, pImp
->m_bRepairPackage
, pImp
->m_xProgressHandler
);
2716 pElement
->m_xStorage
= pStorage
->pImp
;
2721 return new UCBStorage( pElement
->m_xStorage
.get() );
2725 else if ( !pElement
->m_xStream
.is() )
2727 // storage is opened the first time
2728 bool bIsWritable
= bool(pImp
->m_nMode
& StreamMode::WRITE
);
2729 if ( pImp
->m_bIsLinked
&& pImp
->m_bIsRoot
&& bIsWritable
)
2731 // make sure that the root storage object has been created before substorages will be created
2732 INetURLObject
aFolderObj( pImp
->m_aURL
);
2733 aFolderObj
.removeSegment();
2735 Content
aFolder( aFolderObj
.GetMainURL( INetURLObject::DecodeMechanism::NONE
), Reference
< XCommandEnvironment
>(), comphelper::getProcessComponentContext() );
2736 pImp
->m_pContent
= new Content
;
2737 bool bRet
= ::utl::UCBContentHelper::MakeFolder( aFolder
, pImp
->m_aName
, *pImp
->m_pContent
);
2740 SetError( SVSTREAM_CANNOT_MAKE
);
2745 UCBStorage_Impl
* pStor
= pImp
->OpenStorage( pElement
, nMode
, bDirect
);
2748 if ( pElement
->m_bIsInserted
)
2749 pStor
->m_bListCreated
= true; // the storage is pretty new, nothing to read
2751 return new UCBStorage( pStor
);
2758 UCBStorage_Impl
* UCBStorage_Impl::OpenStorage( UCBStorageElement_Impl
* pElement
, StreamMode nMode
, bool bDirect
)
2760 UCBStorage_Impl
* pRet
= nullptr;
2761 OUString
aName( m_aURL
);
2763 aName
+= pElement
->m_aOriginalName
; // ???
2765 pElement
->m_bIsStorage
= pElement
->m_bIsFolder
= true;
2767 if ( m_bIsLinked
&& !::utl::UCBContentHelper::Exists( aName
) )
2770 bool bRet
= ::utl::UCBContentHelper::MakeFolder( *m_pContent
, pElement
->m_aOriginalName
, aNewFolder
);
2772 pRet
= new UCBStorage_Impl( aNewFolder
, aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2776 pRet
= new UCBStorage_Impl( aName
, nMode
, nullptr, bDirect
, false, m_bRepairPackage
, m_xProgressHandler
);
2781 pRet
->m_bIsLinked
= m_bIsLinked
;
2782 pRet
->m_bIsRoot
= false;
2784 // if name has been changed before creating the stream: set name!
2785 pRet
->m_aName
= pElement
->m_aOriginalName
;
2786 pElement
->m_xStorage
= pRet
;
2795 bool UCBStorage::IsStorage( const OUString
& rEleName
) const
2797 if( rEleName
.isEmpty() )
2800 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2801 return ( pElement
&& pElement
->m_bIsStorage
);
2804 bool UCBStorage::IsStream( const OUString
& rEleName
) const
2806 if( rEleName
.isEmpty() )
2809 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2810 return ( pElement
&& !pElement
->m_bIsStorage
);
2813 bool UCBStorage::IsContained( const OUString
& rEleName
) const
2815 if( rEleName
.isEmpty() )
2817 const UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2818 return ( pElement
!= nullptr );
2821 void UCBStorage::Remove( const OUString
& rEleName
)
2823 if( rEleName
.isEmpty() )
2826 UCBStorageElement_Impl
*pElement
= FindElement_Impl( rEleName
);
2829 pElement
->m_bIsRemoved
= true;
2832 SetError( SVSTREAM_FILE_NOT_FOUND
);
2835 bool UCBStorage::ValidateFAT()
2841 bool UCBStorage::Validate( bool bWrite
) const
2844 return ( !bWrite
|| ( pImp
->m_nMode
& StreamMode::WRITE
) );
2847 bool UCBStorage::ValidateMode( StreamMode m
) const
2850 if( m
== ( StreamMode::READ
| StreamMode::TRUNC
) ) // from stg.cxx
2852 // only SHARE_DENYALL allowed
2853 // storages open in r/o mode are OK, since only
2854 // the commit may fail
2855 if( m
& StreamMode::SHARE_DENYALL
)
2861 bool UCBStorage::Equals( const BaseStorage
& rStorage
) const
2864 return static_cast<BaseStorage
const *>(this) == &rStorage
;
2867 bool UCBStorage::IsStorageFile( SvStream
* pFile
)
2872 sal_uInt64 nPos
= pFile
->Tell();
2873 if ( pFile
->TellEnd() < 4 )
2877 sal_uInt32
nBytes(0);
2878 pFile
->ReadUInt32( nBytes
);
2880 // search for the magic bytes
2881 bool bRet
= ( nBytes
== 0x04034b50 );
2884 // disk spanned file have an additional header in front of the usual one
2885 bRet
= ( nBytes
== 0x08074b50 );
2889 pFile
->ReadUInt32( nBytes
);
2890 bRet
= ( nBytes
== 0x04034b50 );
2894 pFile
->Seek( nPos
);
2898 OUString
UCBStorage::GetLinkedFile( SvStream
&rStream
)
2901 sal_uInt64 nPos
= rStream
.Tell();
2902 if ( !rStream
.TellEnd() )
2907 rStream
.ReadUInt32( nBytes
);
2908 if( nBytes
== 0x04034b50 )
2910 OString aTmp
= read_uInt16_lenPrefixed_uInt8s_ToOString(rStream
);
2911 if (aTmp
.match("ContentURL="))
2913 aString
= OStringToOUString(aTmp
.copy(11), RTL_TEXTENCODING_UTF8
);
2917 rStream
.Seek( nPos
);
2921 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */