simplify writeBitmapObject
[LibreOffice.git] / sot / source / sdstor / ucbstorage.cxx
blob178194bba87d87effef8c456db3628e4015628c7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
44 #include <memory>
45 #include <optional>
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>
67 #include <mutex>
68 #include <utility>
69 #include <vector>
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;
84 #endif
86 typedef ::cppu::WeakImplHelper < XInputStream, XSeekable > FileInputStreamWrapper_Base;
88 namespace {
90 class FileStreamWrapper_Impl : public FileInputStreamWrapper_Base, public comphelper::ByteReader
92 protected:
93 std::mutex m_aMutex;
94 OUString m_aURL;
95 std::unique_ptr<SvStream> m_pSvStream;
97 public:
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;
112 protected:
113 void checkConnected();
114 void checkError();
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()
128 if ( m_pSvStream )
130 m_pSvStream.reset();
131 #if OSL_DEBUG_LEVEL > 0
132 --nOpenFiles;
133 #endif
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() )
145 aData.realloc( 0 );
146 return 0;
149 checkConnected();
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);
160 checkError();
162 // if read characters < MaxLength, adjust sequence
163 if (nRead < o3tl::make_unsigned(aData.getLength()))
164 aData.realloc( nRead );
166 return nRead;
169 sal_Int32 FileStreamWrapper_Impl::readSomeBytes(sal_Int8* aData, sal_Int32 nBytesToRead)
171 if ( m_aURL.isEmpty() )
172 return 0;
174 checkConnected();
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);
182 checkError();
184 return nRead;
187 sal_Int32 SAL_CALL FileStreamWrapper_Impl::readSomeBytes(Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
189 if ( m_aURL.isEmpty() )
191 aData.realloc( 0 );
192 return 0;
195 checkError();
197 if (nMaxBytesToRead < 0)
198 throw BufferSizeExceededException(OUString(), getXWeak());
200 if (m_pSvStream->eof())
202 aData.realloc(0);
203 return 0;
205 else
206 return readBytes(aData, nMaxBytesToRead);
210 void SAL_CALL FileStreamWrapper_Impl::skipBytes(sal_Int32 nBytesToSkip)
212 if ( m_aURL.isEmpty() )
213 return;
215 std::scoped_lock aGuard( m_aMutex );
216 checkError();
218 m_pSvStream->SeekRel(nBytesToSkip);
219 checkError();
223 sal_Int32 SAL_CALL FileStreamWrapper_Impl::available()
225 if ( m_aURL.isEmpty() )
226 return 0;
228 std::scoped_lock aGuard( m_aMutex );
229 checkConnected();
231 sal_Int64 nAvailable = m_pSvStream->remainingSize();
232 checkError();
234 return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
238 void SAL_CALL FileStreamWrapper_Impl::closeInput()
240 if ( m_aURL.isEmpty() )
241 return;
243 std::scoped_lock aGuard( m_aMutex );
244 checkConnected();
245 m_pSvStream.reset();
246 #if OSL_DEBUG_LEVEL > 0
247 --nOpenFiles;
248 #endif
249 osl::File::remove(m_aURL);
250 m_aURL.clear();
254 void SAL_CALL FileStreamWrapper_Impl::seek( sal_Int64 _nLocation )
256 if ( m_aURL.isEmpty() )
257 return;
259 std::scoped_lock aGuard( m_aMutex );
260 checkConnected();
262 m_pSvStream->Seek(static_cast<sal_uInt32>(_nLocation));
263 checkError();
267 sal_Int64 SAL_CALL FileStreamWrapper_Impl::getPosition( )
269 if ( m_aURL.isEmpty() )
270 return 0;
272 std::scoped_lock aGuard( m_aMutex );
273 checkConnected();
275 sal_uInt64 nPos = m_pSvStream->Tell();
276 checkError();
277 return nPos;
281 sal_Int64 SAL_CALL FileStreamWrapper_Impl::getLength( )
283 if ( m_aURL.isEmpty() )
284 return 0;
286 std::scoped_lock aGuard( m_aMutex );
287 checkConnected();
289 checkError();
291 sal_Int64 nEndPos = m_pSvStream->TellEnd();
293 return nEndPos;
297 void FileStreamWrapper_Impl::checkConnected()
299 if ( m_aURL.isEmpty() )
300 throw NotConnectedException(OUString(), getXWeak());
301 if ( !m_pSvStream )
303 m_pSvStream = ::utl::UcbStreamHelper::CreateStream( m_aURL, StreamMode::STD_READ );
304 #if OSL_DEBUG_LEVEL > 0
305 ++nOpenFiles;
306 #endif
311 void FileStreamWrapper_Impl::checkError()
313 checkConnected();
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;
349 else
351 OSL_FAIL( "Unknown UCB storage format!" );
352 return SotClipboardFormatId::NONE;
357 static SvGlobalName GetClassId_Impl( SotClipboardFormatId nFormat )
359 switch ( 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 );
400 default :
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;
411 public:
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;
427 OString m_aKey;
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
433 ErrCode m_nError;
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 );
446 void Free();
447 bool Init();
448 bool Clear();
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;
474 public:
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
484 ErrCode m_nError;
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
491 bool m_bIsLinked;
492 bool m_bListCreated;
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 );
507 void Init();
508 sal_Int16 Commit();
509 void Revert();
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();
516 void ReadContent();
517 void CreateContent();
518 ::ucbhelper::Content* GetContent()
520 if ( !m_oContent )
521 CreateContent();
522 return m_oContent ? &*m_oContent : nullptr;
524 UCBStorageElementList_Impl& GetChildrenList()
526 const ErrCode nError = m_nError;
527 ReadContent();
528 if ( m_nMode & StreamMode::WRITE )
530 m_nError = nError;
531 if ( m_pAntiImpl )
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
550 sal_uInt64 m_nSize;
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 )
560 : m_aName( rName )
561 , m_aOriginalName( rName )
562 , m_nSize( nSize )
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();
585 else
586 return nullptr;
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;
595 else
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;
610 else {
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;
621 else
622 return EMPTY_OUSTRING;
625 bool UCBStorageElement_Impl::IsModified() const
627 bool bModified = m_bIsRemoved || m_bIsInserted || m_aName != m_aOriginalName;
628 if ( bModified )
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;
636 return bModified;
639 UCBStorageStream_Impl::UCBStorageStream_Impl( const OUString& rName, StreamMode nMode, UCBStorageStream* pStream, bool bDirect, bool bRepair, Reference< XProgressHandler > const & xProgress )
640 : m_pAntiImpl( pStream )
641 , m_aURL( rName )
642 , m_pContent( nullptr )
643 , m_nError( ERRCODE_NONE )
644 , m_nMode( nMode )
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 );
661 if ( bRepair )
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()
683 if( m_rSource.is() )
684 m_rSource.clear();
686 m_pStream.reset();
688 if (!m_aTempURL.isEmpty())
689 osl::File::remove(m_aTempURL);
691 delete m_pContent;
695 bool UCBStorageStream_Impl::Init()
697 if( !m_pStream )
699 // no temporary stream was created
700 // create one
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
707 ++nOpenFiles;
708 #endif
710 if( !m_pStream )
712 OSL_FAIL( "Suspicious temporary stream creation!" );
713 SetError( SVSTREAM_CANNOT_MAKE );
714 return false;
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
734 if( m_rSource.is() )
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 );
757 else
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!" );
767 return true;
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
775 if( m_bSourceRead )
777 Sequence<sal_Int8> aData(32000);
781 sal_Int32 aReaded;
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;
804 if( m_bSourceRead )
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", "");
829 return aResult;
832 void UCBStorageStream_Impl::CopySourceToTemporary()
834 // current position of the temporary stream is not changed
835 if( m_bSourceRead )
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;
850 if( !Init() )
851 return 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;
880 return aResult;
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 );
888 return 0; // ?mav?
891 if( !nSize || !Init() )
892 return 0;
894 std::size_t aResult = m_pStream->WriteBytes( pData, nSize );
896 m_bModified = aResult > 0;
898 return aResult;
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);
907 if( !Init() )
908 return 0;
910 sal_uInt64 aResult;
912 if( nPos == STREAM_SEEK_TO_END )
914 m_pStream->Seek( STREAM_SEEK_TO_END );
915 ReadSourceWriteTemporary();
916 aResult = m_pStream->Tell();
918 else
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 );
930 else
932 // the temp stream pointer points to the end now
933 aResult = m_pStream->Tell();
935 if( aResult < nPos )
937 if( m_bSourceRead )
939 aResult += ReadSourceWriteTemporary( nPos - aResult );
940 if( aResult < nPos )
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" );
959 return aResult;
962 void UCBStorageStream_Impl::SetSize(sal_uInt64 const nSize)
964 if ( !(m_nMode & StreamMode::WRITE) )
966 SetError( ERRCODE_IO_ACCESSDENIED );
967 return;
970 if( !Init() )
971 return;
973 m_bModified = true;
975 if( m_bSourceRead )
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()
990 if( m_pStream )
992 CopySourceToTemporary();
993 m_pStream->Flush();
996 m_bCommited = true;
999 void UCBStorageStream_Impl::SetError( ErrCode nErr )
1001 if ( !m_nError )
1003 m_nError = 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();
1013 if ( m_pAntiImpl )
1014 m_pAntiImpl->ResetError();
1017 sal_uInt64 UCBStorageStream_Impl::GetSize()
1019 if( !Init() )
1020 return 0;
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 );
1028 return nRet;
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 )
1055 if ( m_bModified )
1059 CopySourceToTemporary();
1061 // release all stream handles
1062 Free();
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
1076 m_aTempURL.clear();
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" !
1114 if ( m_bCommited )
1116 OSL_FAIL("Revert while commit is in progress!" );
1117 return; // ???
1120 Free();
1121 if ( !m_aTempURL.isEmpty() )
1123 osl::File::remove(m_aTempURL);
1124 m_aTempURL.clear();
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;
1136 else
1138 m_nMode &= ~StreamMode::TRUNC;
1139 m_bSourceRead = true;
1142 else
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!" );
1166 if( bRet )
1168 Free();
1171 return bRet;
1174 void UCBStorageStream_Impl::Free()
1176 #if OSL_DEBUG_LEVEL > 0
1177 if ( m_pStream )
1179 if ( !m_aTempURL.isEmpty() )
1180 --nOpenFiles;
1181 else
1182 --nOpenStreams;
1184 #endif
1186 m_rSource.clear();
1187 m_pStream.reset();
1190 void UCBStorageStream_Impl::PrepareCachedForReopen( StreamMode nMode )
1192 bool isWritable = bool( m_nMode & StreamMode::WRITE );
1193 if ( isWritable )
1195 // once stream was writable, never reset to readonly
1196 nMode |= StreamMode::WRITE;
1199 m_nMode = nMode;
1200 Free();
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);
1209 m_aTempURL.clear();
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 )
1224 : pImp( 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 )
1235 pImp->Flush();
1236 pImp->m_pAntiImpl = nullptr;
1237 pImp->Free();
1238 pImp->ReleaseRef();
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()
1260 if( !pImp->Init() )
1261 return 0;
1262 return pImp->m_pStream->Tell();
1265 void UCBStorageStream::Flush()
1267 // streams are never really transacted, so flush also means commit !
1268 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
1284 // ???
1285 if( m == ( StreamMode::READ | StreamMode::TRUNC ) ) // from stg.cxx
1286 return true;
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 ) )
1292 return true;
1294 else
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 )
1300 return true;
1303 return true;
1306 SvStream* UCBStorageStream::GetModifySvStream()
1308 return static_cast<SvStream*>(pImp);
1311 bool UCBStorageStream::Equals( const BaseStorageStream& rStream ) const
1313 // ???
1314 return static_cast<BaseStorageStream const *>(this) == &rStream;
1317 bool UCBStorageStream::Commit()
1319 // mark this stream for sending it on root commit
1320 pImp->FlushData();
1321 return true;
1324 void UCBStorageStream::CopyTo( BaseStorageStream* pDestStm )
1326 if( !pImp->Init() )
1327 return;
1329 UCBStorageStream* pStg = dynamic_cast<UCBStorageStream*>( pDestStm );
1330 if ( pStg )
1331 pStg->pImp->m_aContentType = pImp->m_aContentType;
1333 pDestStm->SetSize( 0 );
1334 Seek( STREAM_SEEK_TO_END );
1335 sal_Int32 n = Tell();
1336 if( n < 0 )
1337 return;
1339 if( !pDestStm->SetSize( n ) || !n )
1340 return;
1342 std::unique_ptr<sal_uInt8[]> p(new sal_uInt8[ 4096 ]);
1343 Seek( 0 );
1344 pDestStm->Seek( 0 );
1345 while( n )
1347 sal_Int32 nn = n;
1348 if( nn > 4096 )
1349 nn = 4096;
1350 if( Read( p.get(), nn ) != nn )
1351 break;
1352 if( pDestStm->Write( p.get(), nn ) != nn )
1353 break;
1354 n -= nn;
1358 bool UCBStorageStream::SetProperty( const OUString& rName, const css::uno::Any& rValue )
1360 if ( rName == "Title")
1361 return false;
1363 if ( rName == "MediaType")
1365 OUString aTmp;
1366 rValue >>= aTmp;
1367 pImp->m_aContentType = aTmp;
1372 if ( pImp->m_pContent )
1374 pImp->m_pContent->setPropertyValue( rName, rValue );
1375 return true;
1378 catch (const Exception&)
1382 return false;
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();
1397 pImp->Init();
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();
1407 pImp->Init();
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();
1417 pImp->Init();
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();
1427 pImp->Init();
1428 StorageBase::m_nMode = pImp->m_nMode;
1431 UCBStorage::UCBStorage( UCBStorage_Impl *pImpl )
1432 : pImp( 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
1444 Commit();
1446 pImp->m_pAntiImpl = nullptr;
1447 pImp->ReleaseRef();
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 )
1456 , m_nMode( nMode )
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();
1476 m_aURL = rName;
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 )
1484 , m_nMode( nMode )
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();
1504 if ( m_bIsRoot )
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 */ );
1516 else
1518 // substorages are opened like streams: the URL is a "child URL" of the root package URL
1519 m_aURL = rName;
1520 if ( !m_aURL.startsWith( "vnd.sun.star.pkg://") )
1521 m_bIsLinked = true;
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 )
1532 , m_bIsRoot( true )
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 */ ));
1551 if ( pStream )
1553 rStream.Seek(0);
1554 rStream.ReadStream( *pStream );
1555 pStream->Flush();
1556 pStream.reset();
1559 // close stream and let content access the file
1560 m_pSource->Seek(0);
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();
1576 if ( !m_oContent )
1577 CreateContent();
1579 if ( m_oContent )
1581 if ( m_bIsLinked )
1583 if( m_bIsRoot )
1585 ReadContent();
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
1595 if ( pStream )
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 );
1607 // cleanup
1608 xReader = nullptr;
1609 pHelper = nullptr;
1610 SetProps( aProps, OUString() );
1615 else
1616 ReadContent();
1618 else
1620 // get the manifest information from the package
1621 try {
1622 Any aAny = m_oContent->getPropertyValue(u"MediaType"_ustr);
1623 OUString aTmp;
1624 if ( ( aAny >>= aTmp ) && !aTmp.isEmpty() )
1625 m_aContentType = m_aOriginalContentType = aTmp;
1627 catch (const Exception&)
1629 SAL_WARN( "sot",
1630 "getPropertyValue has thrown an exception! Please let developers know the scenario!" );
1635 if ( m_aContentType.isEmpty() )
1636 return;
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() )
1651 ReadContent();
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 )
1687 return;
1689 m_bListCreated = true;
1693 GetContent();
1694 if ( !m_oContent )
1695 return;
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) );
1706 if ( m_bIsLinked )
1708 // unpacked storages have to deal with the meta-inf folder by themselves
1709 if ( aTitle == "META-INF" )
1710 continue;
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() );
1719 if ( bIsFolder )
1721 if ( m_bIsLinked )
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;
1751 else
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 )
1794 if ( !m_nError )
1796 m_nError = 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();
1811 return nCount;
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 )
1819 OUString aType;
1821 for ( const PropertyValue& rAny : rMyProps )
1823 if ( rAny.Name == "FullPath" )
1825 OUString aTmp;
1826 if ( ( rAny.Value >>= aTmp ) && aTmp == rPath )
1827 bFound = true;
1828 if ( !aType.isEmpty() )
1829 break;
1831 else if ( rAny.Name == "MediaType" )
1833 if ( ( rAny.Value >>= aType ) && !aType.isEmpty() && bFound )
1834 break;
1838 if ( bFound )
1839 return aType;
1842 return OUString();
1845 void UCBStorage_Impl::SetProps( const Sequence < Sequence < PropertyValue > >& rSequence, const OUString& rPath )
1847 OUString aPath( rPath );
1848 if ( !m_bIsRoot )
1849 aPath += m_aName;
1850 aPath += "/";
1852 m_aContentType = m_aOriginalContentType = Find_Impl( rSequence, aPath );
1854 if ( m_bIsRoot )
1855 // the "FullPath" of a child always starts without '/'
1856 aPath.clear();
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 );
1863 else
1865 OUString aElementPath = aPath + pElement->m_aName;
1866 pElement->SetContentType( Find_Impl( rSequence, aElementPath ) );
1870 if ( m_aContentType.isEmpty() )
1871 return;
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 );
1894 if ( !m_bIsRoot )
1895 aPath += m_aName;
1896 aPath += "/";
1897 Sequence < PropertyValue > aProps{ comphelper::makePropertyValue(u"MediaType"_ustr, m_aContentType),
1898 comphelper::makePropertyValue(u"FullPath"_ustr, aPath) };
1899 pSequence[nProps++] = aProps;
1901 if ( m_bIsRoot )
1902 // the "FullPath" of a child always starts without '/'
1903 aPath.clear();
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 );
1912 else
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();
1927 m_oContent.reset();
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
1935 bool bRet = false;
1939 const Sequence< ContentInfo > aInfo = pContent->queryCreatableContentsInfo();
1940 if ( !aInfo.hasElements() )
1941 return false;
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 )
1951 continue;
1953 if ( rProps[ 0 ].Name != "Title" )
1954 continue;
1956 Content aNewFolder;
1957 if ( !pContent->insertNewContent( rCurr.Type, { u"Title"_ustr }, { Any(m_aName) }, aNewFolder ) )
1958 continue;
1960 // remove old content, create an "empty" new one and initialize it with the new inserted
1961 m_oContent.emplace( aNewFolder );
1962 bRet = true;
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 );
1982 return bRet;
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;
2021 else
2022 // couldn't release stream because there are external references to it
2023 nRet = COMMIT_RESULT_FAILURE;
2026 else
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";
2051 Any aValue;
2052 aValue <<= true;
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 )
2074 nRet = nLocalRet;
2077 if ( nRet == COMMIT_RESULT_FAILURE )
2078 break;
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
2115 Any aType;
2116 aType <<= m_aContentType;
2117 m_oContent->setPropertyValue(u"MediaType"_ustr, aType );
2119 if ( m_bIsLinked )
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 );
2125 if ( bRet )
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() );
2147 xWriter = nullptr;
2148 xOutputStream = nullptr;
2149 pTempFile.reset();
2150 aNewSubFolder.transferContent( aSource, InsertOperation::Move, u"manifest.xml"_ustr, NameClash::OVERWRITE );
2153 else
2155 #if OSL_DEBUG_LEVEL > 0
2156 SAL_INFO("sot", "Files: " << nOpenFiles);
2157 SAL_INFO("sot", "Streams: " << nOpenStreams);
2158 #endif
2159 // force writing
2160 Any aAny;
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 );
2168 pStream.reset();
2169 m_pSource->Seek(0);
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 );
2199 else
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 );
2217 return nRet;
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 );
2227 else
2229 pInnerElement->m_aOriginalName = pInnerElement->m_aName;
2230 pInnerElement->m_bIsInserted = false;
2231 ++i;
2236 m_bCommited = false;
2239 return nRet;
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 );
2250 else
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;
2265 ++i;
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() )
2301 return;
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
2307 // the content type
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;
2372 if ( !pStream )
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() );
2382 else
2383 pOtherStream->Commit();
2385 if ( bDeleteStream )
2386 delete pStream;
2388 else
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;
2398 if ( !pStorage )
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 );
2418 else
2419 pOtherStorage->SetClassId( pStorage->GetClassId() );
2420 pStorage->CopyTo( *pOtherStorage );
2421 SetError( pStorage->GetError() );
2422 if( pOtherStorage->GetError() )
2423 pDest->SetError( pOtherStorage->GetError() );
2424 else
2425 pOtherStorage->Commit();
2427 if ( bDeleteStorage )
2428 delete pStorage;
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();
2442 return nullptr;
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) )
2449 return false;
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 );
2459 else
2460 rDestStg.SetClassId( GetClassId() );
2461 rDestStg.SetDirty();
2463 bool bRet = true;
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 );
2471 if( !bRet )
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() )
2479 return false;
2481 if ( pDest == static_cast<BaseStorage*>(this) )
2483 // can't double an element
2484 return false;
2486 else
2488 // for copying no optimization is useful, because in every case the stream data must be copied
2489 UCBStorageElement_Impl* pElement = FindElement_Impl( rElemName );
2490 if ( pElement )
2491 return CopyStorageElement_Impl( *pElement, pDest, rNew );
2492 else
2494 SetError( SVSTREAM_FILE_NOT_FOUND );
2495 return false;
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 );
2507 else
2508 return true;
2511 bool UCBStorage::Revert()
2513 pImp->Revert();
2514 return true;
2517 BaseStorageStream* UCBStorage::OpenStream( const OUString& rEleName, StreamMode nMode, bool bDirect )
2519 if( rEleName.isEmpty() )
2520 return nullptr;
2522 // try to find the storage element
2523 UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
2524 if ( !pElement )
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;
2534 return pStream;
2536 else
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 ); // ???
2555 return nullptr;
2557 else
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() );
2578 return nullptr;
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() )
2590 return nullptr;
2592 return OpenStorage_Impl( rEleName, nMode, bDirect, true );
2595 BaseStorage* UCBStorage::OpenOLEStorage( const OUString& rEleName, StreamMode nMode, bool bDirect )
2597 if( rEleName.isEmpty() )
2598 return nullptr;
2600 return OpenStorage_Impl( rEleName, nMode, bDirect, false );
2603 BaseStorage* UCBStorage::OpenStorage( const OUString& rEleName, StreamMode nMode, bool bDirect )
2605 if( rEleName.isEmpty() )
2606 return nullptr;
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 );
2615 if ( !pElement )
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() );
2626 return pStorage;
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 );
2647 if ( !pStream )
2649 SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
2650 return nullptr;
2653 pElement->m_xStream = pStream->pImp;
2654 delete pStream;
2657 pElement->m_xStream->PrepareCachedForReopen( nMode );
2658 bool bInited = pElement->m_xStream->Init();
2659 if (!bInited)
2661 SetError( ( nMode & StreamMode::WRITE ) ? SVSTREAM_CANNOT_MAKE : SVSTREAM_FILE_NOT_FOUND );
2662 return nullptr;
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 ); // ???
2676 else
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;
2684 return pStorage;
2686 else
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 );
2705 if ( !bRet )
2707 SetError( SVSTREAM_CANNOT_MAKE );
2708 return nullptr;
2712 UCBStorage_Impl* pStor = pImp->OpenStorage( pElement, nMode, bDirect );
2713 if ( pStor )
2715 if ( pElement->m_bIsInserted )
2716 pStor->m_bListCreated = true; // the storage is pretty new, nothing to read
2718 return new UCBStorage( pStor );
2722 return nullptr;
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 ) )
2734 Content aNewFolder;
2735 bool bRet = ::utl::UCBContentHelper::MakeFolder( *m_oContent, pElement->m_aOriginalName, aNewFolder );
2736 if ( bRet )
2737 pRet = new UCBStorage_Impl( aNewFolder, aName, nMode, nullptr, bDirect, false, m_bRepairPackage, m_xProgressHandler );
2739 else
2741 pRet = new UCBStorage_Impl( aName, nMode, nullptr, bDirect, false, m_bRepairPackage, m_xProgressHandler );
2744 if ( pRet )
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;
2754 if ( pRet )
2755 pRet->Init();
2757 return pRet;
2760 bool UCBStorage::IsStorage( const OUString& rEleName ) const
2762 if( rEleName.isEmpty() )
2763 return false;
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() )
2772 return false;
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() )
2781 return false;
2782 const UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
2783 return ( pElement != nullptr );
2786 void UCBStorage::Remove( const OUString& rEleName )
2788 if( rEleName.isEmpty() )
2789 return;
2791 UCBStorageElement_Impl *pElement = FindElement_Impl( rEleName );
2792 if ( pElement )
2794 pElement->m_bIsRemoved = true;
2796 else
2797 SetError( SVSTREAM_FILE_NOT_FOUND );
2800 bool UCBStorage::ValidateFAT()
2802 // ???
2803 return true;
2806 bool UCBStorage::Validate( bool bWrite ) const
2808 // ???
2809 return ( !bWrite || ( pImp->m_nMode & StreamMode::WRITE ) );
2812 bool UCBStorage::ValidateMode( StreamMode m ) const
2814 // ???
2815 if( m == ( StreamMode::READ | StreamMode::TRUNC ) ) // from stg.cxx
2816 return true;
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 )
2821 return true;
2823 return true;
2826 bool UCBStorage::Equals( const BaseStorage& rStorage ) const
2828 // ???
2829 return static_cast<BaseStorage const *>(this) == &rStorage;
2832 bool UCBStorage::IsStorageFile( SvStream* pFile )
2834 if ( !pFile )
2835 return false;
2837 sal_uInt64 nPos = pFile->Tell();
2838 if ( pFile->TellEnd() < 4 )
2839 return false;
2841 pFile->Seek(0);
2842 sal_uInt32 nBytes(0);
2843 pFile->ReadUInt32( nBytes );
2845 // search for the magic bytes
2846 bool bRet = ( nBytes == 0x04034b50 );
2847 if ( !bRet )
2849 // disk spanned file have an additional header in front of the usual one
2850 bRet = ( nBytes == 0x08074b50 );
2851 if ( bRet )
2853 nBytes = 0;
2854 pFile->ReadUInt32( nBytes );
2855 bRet = ( nBytes == 0x04034b50 );
2859 pFile->Seek( nPos );
2860 return bRet;
2863 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */