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